From 85e74691f272941ebee480696a74b77ffe97eed2 Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 13:27:40 +0000 Subject: [PATCH 01/17] fix(deps): update dependency postcss to ^8.5.15 (#5940) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- apps/docs/package.json | 2 +- pnpm-lock.yaml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/docs/package.json b/apps/docs/package.json index 8e5c6048e..8273d14ff 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -37,7 +37,7 @@ "dayjs": "catalog:", "docusaurus-plugin-image-zoom": "^3.0.1", "leader-line-new": "^1.1.9", - "postcss": "^8.5.10", + "postcss": "^8.5.15", "posthog-docusaurus": "^2.0.5", "prism-react-renderer": "^2.4.1", "random-word-slugs": "^0.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7797baf4d..72d87bea5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -725,7 +725,7 @@ importers: specifier: ^1.1.9 version: 1.1.9 postcss: - specifier: ^8.5.10 + specifier: ^8.5.15 version: 8.5.15 posthog-docusaurus: specifier: ^2.0.5 @@ -14447,6 +14447,7 @@ packages: tsconfck@3.1.3: resolution: {integrity: sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==} engines: {node: ^18 || >=20} + deprecated: unmaintained hasBin: true peerDependencies: typescript: ^5.0.0 From f263388f714a5df2fd266bafd758b861b0b406fc Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:40:14 +0000 Subject: [PATCH 02/17] chore(deps): update turbo monorepo to ^2.9.17 (#5959) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 72 ++++++++++++++++++++++----------------------- pnpm-workspace.yaml | 4 +-- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72d87bea5..b2057de41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,8 +217,8 @@ catalogs: specifier: ^11.17.0 version: 11.17.0 '@turbo/gen': - specifier: ^2.9.16 - version: 2.9.16 + specifier: ^2.9.17 + version: 2.9.18 '@types/adm-zip': specifier: 0.5.8 version: 0.5.8 @@ -514,8 +514,8 @@ catalogs: specifier: 4.20.4 version: 4.20.4 turbo: - specifier: ^2.9.16 - version: 2.9.16 + specifier: ^2.9.17 + version: 2.9.18 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -618,7 +618,7 @@ importers: version: 12.0.1 '@turbo/gen': specifier: 'catalog:' - version: 2.9.16(@types/node@24.13.1) + version: 2.9.18(@types/node@24.13.1) '@vitejs/plugin-react': specifier: 'catalog:' version: 6.0.2(babel-plugin-react-compiler@1.0.0)(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) @@ -654,7 +654,7 @@ importers: version: 12.0.1 turbo: specifier: 'catalog:' - version: 2.9.16 + version: 2.9.18 typescript: specifier: 'catalog:' version: 6.0.3 @@ -7468,37 +7468,37 @@ packages: '@tsconfig/docusaurus@2.0.9': resolution: {integrity: sha512-0jVxZCgy2v7TnJrW9kVNikNwJHeiWb68Zhiiw2vHw4tzBFSP5vS2zn3O5EY2oPEVz5dXZRkK7MnWG3Ay5q0mIg==} - '@turbo/darwin-64@2.9.16': - resolution: {integrity: sha512-jLjApWTSNd7JZ5JaLYfelW1ytnGQOvB7ivl+2RD1xQvJTbi8I9gBjzcga7tDZVPyaxpl10YTfJt3BrYXR18KDw==} + '@turbo/darwin-64@2.9.18': + resolution: {integrity: sha512-9f27peFu16ur8c0v9nUFUEyBnbKuuFsUTjHFWfmwGfzySBXbHwzU44QhZon6Mznz0cHsIr3984NQj/bVrnGSRw==} cpu: [x64] os: [darwin] - '@turbo/darwin-arm64@2.9.16': - resolution: {integrity: sha512-YPgrn+5HIGzrx0O2a631SV4MBQUe4W/DafMFUuBVgaU32PW9/OTT0ehviF0QSxTXuRJlHvW2eUTemddF5/spmw==} + '@turbo/darwin-arm64@2.9.18': + resolution: {integrity: sha512-9A6TMRq/Ib+QnbhLlgkhOm+624wO4pzSQ/yQviQfWHOlFvaYxdnIAYmu2H6TS6y7kSVL0DvzNe04NbESTOzFVQ==} cpu: [arm64] os: [darwin] - '@turbo/gen@2.9.16': - resolution: {integrity: sha512-nvEBt1FUqA7zO821Yw/GuggAG8JeT0J2en+vwvuSNqHa0df+a2zhzHF2vx/oguWQm1HCjgx87VIJWzMOr2grYA==} + '@turbo/gen@2.9.18': + resolution: {integrity: sha512-9Ry3V2eqFANYI7A5dyjehq2EOuLTY30XQSg4aDR7F3cJOuiP/Ad2KXwrxD3AnwNDkuSDVbJjlbES7yfJ0y7dhw==} hasBin: true - '@turbo/linux-64@2.9.16': - resolution: {integrity: sha512-vAEf1H6l26lTpl9FJ/peQo1NUB8RC0sbEJJz5mPcUhHA2bPDup2x3CZPgo/bH8S4cUcBLm4FN3UHd5iUO2RAew==} + '@turbo/linux-64@2.9.18': + resolution: {integrity: sha512-zCdIDtz69AnbYh913elJRRoF3QY5aa2HNnf+4rAkc7bQ+tWujiDkCNV7stazOUPggaDvhKIf2Z87qHftTeXSkw==} cpu: [x64] os: [linux] - '@turbo/linux-arm64@2.9.16': - resolution: {integrity: sha512-xDBLR2PZg4BrQOchfG6svgpv5FCNJ2TOtT2psLdEJcdKo1BH+pnPs9Xj6pvUjgfkHbuvBOfeE4R6tvxMoQKDHQ==} + '@turbo/linux-arm64@2.9.18': + resolution: {integrity: sha512-Va1kXI04naMgYwqv/5Dfa36dTDx8015U7oaQAjrXa45ua9OoFjSV4OmvkML4EmXvUclQHCiBRbY8bvd0jV7eAg==} cpu: [arm64] os: [linux] - '@turbo/windows-64@2.9.16': - resolution: {integrity: sha512-NBAJnaUiGdgkSzQwUIdOvkCkcpTSu58G/sBGa0mvBtzfvFOOgrQwepKOOQ8cp6sWM6OcKDNFj2p1dsZA1OWjPg==} + '@turbo/windows-64@2.9.18': + resolution: {integrity: sha512-m0kDhZANxSNz9ck1ybogFscHabriAsp4eDFNrN/1H5WrgTF7b3VlcPZnhuO3v2+E2KnCbeAc+UUT10BZZHdDKw==} cpu: [x64] os: [win32] - '@turbo/windows-arm64@2.9.16': - resolution: {integrity: sha512-Y7SJppD0Z8wjO3Ec0ZGd9KQ4Yv0BMnA8CIowj5Vp+OEVsosXDG2weK6/t1RRLfJmc2Ozrnd6y4DOgQys+mn3WQ==} + '@turbo/windows-arm64@2.9.18': + resolution: {integrity: sha512-nUdR8WqoomUys9iIQmG45TMiizJ+5BV8egSeLLZba/AWblyp3fVBcIH1kSE58OtK4g2YzbMJEth6Ttv9w5rqMA==} cpu: [arm64] os: [win32] @@ -14490,8 +14490,8 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - turbo@2.9.16: - resolution: {integrity: sha512-NqgRQy6j6dPYcdSdv0q1g9QsZg7SWg87RERM8otw/1AtKU2yTFVClOM7cbwKzOonZr/Ek1blTBucw64L9H0Bwg==} + turbo@2.9.18: + resolution: {integrity: sha512-bwabv6PupzeavybzEoArBAkwq5fnzwf8OFnRtpHwnviFWuwJPFxtyH+aVp36TmIqK3aYYgtTJ3J0m2ysxxSzQg==} hasBin: true tweetnacl@0.14.5: @@ -21214,29 +21214,29 @@ snapshots: '@tsconfig/docusaurus@2.0.9': {} - '@turbo/darwin-64@2.9.16': + '@turbo/darwin-64@2.9.18': optional: true - '@turbo/darwin-arm64@2.9.16': + '@turbo/darwin-arm64@2.9.18': optional: true - '@turbo/gen@2.9.16(@types/node@24.13.1)': + '@turbo/gen@2.9.18(@types/node@24.13.1)': dependencies: '@inquirer/prompts': 7.10.1(@types/node@24.13.1) esbuild: 0.25.12 transitivePeerDependencies: - '@types/node' - '@turbo/linux-64@2.9.16': + '@turbo/linux-64@2.9.18': optional: true - '@turbo/linux-arm64@2.9.16': + '@turbo/linux-arm64@2.9.18': optional: true - '@turbo/windows-64@2.9.16': + '@turbo/windows-64@2.9.18': optional: true - '@turbo/windows-arm64@2.9.16': + '@turbo/windows-arm64@2.9.18': optional: true '@tybys/wasm-util@0.10.2': @@ -29188,14 +29188,14 @@ snapshots: tunnel@0.0.6: {} - turbo@2.9.16: + turbo@2.9.18: optionalDependencies: - '@turbo/darwin-64': 2.9.16 - '@turbo/darwin-arm64': 2.9.16 - '@turbo/linux-64': 2.9.16 - '@turbo/linux-arm64': 2.9.16 - '@turbo/windows-64': 2.9.16 - '@turbo/windows-arm64': 2.9.16 + '@turbo/darwin-64': 2.9.18 + '@turbo/darwin-arm64': 2.9.18 + '@turbo/linux-64': 2.9.18 + '@turbo/linux-arm64': 2.9.18 + '@turbo/windows-64': 2.9.18 + '@turbo/windows-arm64': 2.9.18 tweetnacl@0.14.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a11bec68a..c88c12eaf 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -73,7 +73,7 @@ catalog: "@trpc/react-query": ^11.17.0 "@trpc/server": ^11.17.0 "@trpc/tanstack-react-query": ^11.17.0 - "@turbo/gen": ^2.9.16 + "@turbo/gen": ^2.9.17 "@types/adm-zip": 0.5.8 "@types/bcrypt": 6.0.0 "@types/better-sqlite3": 7.6.13 @@ -174,7 +174,7 @@ catalog: trpc-to-openapi: ^3.3.0 tsdav: ^2.2.2 tsx: 4.20.4 - turbo: ^2.9.16 + turbo: ^2.9.17 typescript: ^6.0.3 vite: ^8.0.16 undici: 7.27.2 From 7837b6fa6be6e363aad6b565135958e8734be09b Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 21:55:49 +0000 Subject: [PATCH 03/17] chore(deps): update pnpm to >=11.5.2 (#5941) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- deployments/prebuilt-debian/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployments/prebuilt-debian/package.json b/deployments/prebuilt-debian/package.json index 61c06690d..99977658b 100644 --- a/deployments/prebuilt-debian/package.json +++ b/deployments/prebuilt-debian/package.json @@ -6,7 +6,7 @@ }, "engines": { "node": ">=24.16.0", - "pnpm": ">=11.0.0" + "pnpm": ">=11.5.3" }, "packageManager": "pnpm@11.5.3" } diff --git a/package.json b/package.json index 98676bf67..0742d3e98 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ }, "engines": { "node": ">=24.16.0", - "pnpm": ">=11.0.0" + "pnpm": ">=11.5.3" }, "packageManager": "pnpm@11.5.3", "dependencies": { From 6881b8355c7e940360a0fb582787f0a770430c5b Mon Sep 17 00:00:00 2001 From: Daniel Lopes Date: Tue, 16 Jun 2026 10:15:56 +0200 Subject: [PATCH 04/17] fix: fixes and unifies descriptions in dns-hole-controls and dns-hole-summary docs (#5938) --- apps/docs/docs/widgets/dns-hole-controls/index.ts | 5 +++-- apps/docs/docs/widgets/dns-hole-summary/index.ts | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/docs/docs/widgets/dns-hole-controls/index.ts b/apps/docs/docs/widgets/dns-hole-controls/index.ts index 5125c0ce3..31d2b45c9 100644 --- a/apps/docs/docs/widgets/dns-hole-controls/index.ts +++ b/apps/docs/docs/widgets/dns-hole-controls/index.ts @@ -1,16 +1,17 @@ import { WidgetDefinition } from "@site/src/types"; import { IconDeviceGamepad } from "@tabler/icons-react"; +import { integrations } from "../dns-hole-summary"; export const dnsHoleControlsWidget: WidgetDefinition = { icon: IconDeviceGamepad, name: "DNS Hole Controls", - description: "Control PiHole or AdGuard from your dashboard", + description: `Control the blocking feature of your ${integrations} from your dashboard`, path: "../../widgets/dns-hole-controls", configuration: { items: [ { name: "Show Toggle All Buttons", - description: "Show or not the 2 buttons that may be redundant when having only one DNS-hole.", + description: "Show or not the 2 buttons that may be redundant when having only one DNS Hole.", values: { type: "boolean" }, defaultValue: "yes", }, diff --git a/apps/docs/docs/widgets/dns-hole-summary/index.ts b/apps/docs/docs/widgets/dns-hole-summary/index.ts index 3f49f9fbd..76de8b588 100644 --- a/apps/docs/docs/widgets/dns-hole-summary/index.ts +++ b/apps/docs/docs/widgets/dns-hole-summary/index.ts @@ -1,10 +1,12 @@ import { WidgetDefinition } from "@site/src/types"; import { IconAd } from "@tabler/icons-react"; +export const integrations = "Pi-hole, AdGuard Home or Technitium DNS"; + export const dnsHoleSummaryWidget: WidgetDefinition = { icon: IconAd, name: "DNS Hole Summary", - description: "Displays the summary of your DNS Hole", + description: `Displays the summary of your ${integrations} blocks and queries statistics`, path: "../../widgets/dns-hole-summary", configuration: { items: [ @@ -19,7 +21,7 @@ export const dnsHoleSummaryWidget: WidgetDefinition = { }, { name: "Use Pi-Hole colors", - description: "Color the tiles using the background color, that is used by default in PiHole.", + description: `Color the tiles using the background color, that is used by default in ${integrations}.`, values: { type: "boolean" }, defaultValue: "yes", }, From 58058f2ea6a67259676228f7f791230b0e33f57a Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Tue, 16 Jun 2026 01:17:56 -0700 Subject: [PATCH 05/17] fix: make quicklook optional in Glances integration schema (#5949) On macOS (and some Glances configurations) the /api/4/all endpoint omits the quicklook key, causing a Zod invalid_type error and crashing the system health widget. Mark quicklook as .optional() in allSchema and add a null-safe fallback (`?? "Unknown"`) for cpuModelName. Fixes #5728 Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> --- .../src/glances/glances-integration.ts | 10 +- .../integrations/test/glances-unit.spec.ts | 103 ++++++++++++++++++ 2 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 packages/integrations/test/glances-unit.spec.ts diff --git a/packages/integrations/src/glances/glances-integration.ts b/packages/integrations/src/glances/glances-integration.ts index 7b6ae3b79..6364ebcb7 100644 --- a/packages/integrations/src/glances/glances-integration.ts +++ b/packages/integrations/src/glances/glances-integration.ts @@ -55,7 +55,7 @@ export class GlancesIntegration extends Integration implements ISystemHealthMoni })), uptime: stats.uptime.as("seconds"), rebootRequired: false, - cpuModelName: stats.quicklook.cpu_name, + cpuModelName: stats.quicklook?.cpu_name ?? "Unknown", loadAverage: null, smart: [], cpuTemp: undefined, @@ -155,9 +155,11 @@ const allSchema = z.object({ return dayjs.duration({ days, hours, minutes, seconds }); }), - quicklook: z.object({ - cpu_name: z.string(), - }), + quicklook: z + .object({ + cpu_name: z.string(), + }) + .optional(), gpu: z .array( z.object({ diff --git a/packages/integrations/test/glances-unit.spec.ts b/packages/integrations/test/glances-unit.spec.ts new file mode 100644 index 000000000..121ab594a --- /dev/null +++ b/packages/integrations/test/glances-unit.spec.ts @@ -0,0 +1,103 @@ +// @vitest-environment node + +import { describe, expect, test, vi } from "vitest"; + +import { createDb } from "@homarr/db/test"; +import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http"; + +import { GlancesIntegration } from "../src/glances/glances-integration"; + +// ─── module mocks ──────────────────────────────────────────────────────────── + +vi.mock("@homarr/db", async (importActual) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actual = await importActual(); + return { ...actual, db: createDb() }; +}); + +vi.mock("@homarr/core/infrastructure/certificates", async (importActual) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actual = await importActual(); + return { + ...actual, + getTrustedCertificateHostnamesAsync: vi.fn().mockResolvedValue([]), + }; +}); + +vi.mock("@homarr/core/infrastructure/http", () => ({ + fetchWithTrustedCertificatesAsync: vi.fn(), +})); + +vi.mock("../src/base/session-store", () => ({ + createSessionStore: () => ({ + async getAsync() { + return { version: "4.0.0" }; + }, + async setAsync() { + return; + }, + async clearAsync() { + return; + }, + }), +})); + +// ─── helpers ───────────────────────────────────────────────────────────────── + +const baseStats = { + cpu: { total: 10 }, + mem: { total: 8_000_000_000, used: 4_000_000_000, free: 4_000_000_000 }, + network: [{ bytes_sent_rate_per_sec: 1000, bytes_recv_rate_per_sec: 2000 }], + fs: [{ device_name: "/dev/sda1", used: 10_000, free: 90_000, percent: 10 }], + uptime: "1 day, 2:03:04", + gpu: [], +}; + +const mockFetch = vi.mocked(fetchWithTrustedCertificatesAsync); + +// Cast lives here so call sites don't need `as never`. +function makeResponse(body: unknown) { + return { + ok: true, + status: 200, + json: () => Promise.resolve(body), + } as never; +} + +const integrationInput = { + id: "test-glances", + name: "Glances", + url: "http://localhost:61208", + decryptedSecrets: [], + externalUrl: null, +}; + +// ─── tests ─────────────────────────────────────────────────────────────────── + +describe("GlancesIntegration schema", () => { + test("parses stats when quicklook is present", async () => { + mockFetch.mockResolvedValueOnce(makeResponse({ ...baseStats, quicklook: { cpu_name: "Apple M1" } })); + + const integration = new GlancesIntegration(integrationInput); + const result = await integration.getSystemInfoAsync(); + + expect(result.cpuModelName).toBe("Apple M1"); + expect(result.cpuUtilization).toBe(10); + }); + + test("returns Unknown cpuModelName when quicklook is absent (macOS scenario)", async () => { + mockFetch.mockResolvedValueOnce(makeResponse(baseStats)); + + const integration = new GlancesIntegration(integrationInput); + const result = await integration.getSystemInfoAsync(); + + expect(result.cpuModelName).toBe("Unknown"); + }); + + test("does not throw a Zod parse error when quicklook is missing", async () => { + mockFetch.mockResolvedValueOnce(makeResponse(baseStats)); + + const integration = new GlancesIntegration(integrationInput); + await expect(integration.getSystemInfoAsync()).resolves.not.toThrow(); + }); +}); From 7ff115465a9f045ad5efd091ea7181841827ef02 Mon Sep 17 00:00:00 2001 From: "homarr-releases[bot]" <175486441+homarr-releases[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:21:19 +0200 Subject: [PATCH 06/17] chore: update bug report template (#5944) * chore: update bug report template * style: auto-fix lint and format --------- Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 7b8493454..d54b28a73 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -33,6 +33,7 @@ body: options: # The below comment is used to insert a new version with on-release.yml #NEXT_VERSION# + - 1.65.0 - 1.64.0 - 1.62.0 - 1.61.0 From ec9f3cc5ad3e524d16f567788bc64e1785435223 Mon Sep 17 00:00:00 2001 From: "homarr-update-contributors[bot]" <210161987+homarr-update-contributors[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:21:33 +0200 Subject: [PATCH 07/17] chore: update contributors (#5945) * chore: update contributors * style: auto-fix lint and format --------- Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com> --- static-data/contributors.json | 2 +- static-data/translators.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static-data/contributors.json b/static-data/contributors.json index 36bf46f0a..b942c6ae9 100644 --- a/static-data/contributors.json +++ b/static-data/contributors.json @@ -1 +1 @@ -[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"marektancak0106","avatar_url":"https://avatars.githubusercontent.com/u/25422667?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/u/47495914?v=4"},{"login":"bebsworthy","avatar_url":"https://avatars.githubusercontent.com/u/5266759?v=4"},{"login":"catrielmuller","avatar_url":"https://avatars.githubusercontent.com/u/2272323?v=4"},{"login":"Vysp3r","avatar_url":"https://avatars.githubusercontent.com/u/62019735?v=4"},{"login":"DR-Blogs","avatar_url":"https://avatars.githubusercontent.com/u/96393186?v=4"},{"login":"singular0","avatar_url":"https://avatars.githubusercontent.com/u/1925883?v=4"},{"login":"diogovalentte","avatar_url":"https://avatars.githubusercontent.com/u/49578155?v=4"},{"login":"codezninja","avatar_url":"https://avatars.githubusercontent.com/u/4400773?v=4"},{"login":"fabricionaweb","avatar_url":"https://avatars.githubusercontent.com/u/15933?v=4"},{"login":"nyok1912","avatar_url":"https://avatars.githubusercontent.com/u/32163949?v=4"},{"login":"bo3bdo","avatar_url":"https://avatars.githubusercontent.com/u/5271380?v=4"},{"login":"hskrtich","avatar_url":"https://avatars.githubusercontent.com/u/1214484?v=4"},{"login":"ishults","avatar_url":"https://avatars.githubusercontent.com/u/7275442?v=4"},{"login":"jelliuk","avatar_url":"https://avatars.githubusercontent.com/u/4790959?v=4"},{"login":"Moohan","avatar_url":"https://avatars.githubusercontent.com/u/5982260?v=4"},{"login":"tesar-tech","avatar_url":"https://avatars.githubusercontent.com/u/33880579?v=4"},{"login":"jbruell","avatar_url":"https://avatars.githubusercontent.com/u/31685172?v=4"},{"login":"JasonLeeB06","avatar_url":"https://avatars.githubusercontent.com/u/82266978?v=4"},{"login":"jeffersonraimon","avatar_url":"https://avatars.githubusercontent.com/u/80064475?v=4"},{"login":"jo-chemla","avatar_url":"https://avatars.githubusercontent.com/u/16822841?v=4"},{"login":"joser93","avatar_url":"https://avatars.githubusercontent.com/u/6912089?v=4"},{"login":"justijndepover","avatar_url":"https://avatars.githubusercontent.com/u/9008623?v=4"},{"login":"Aandree5","avatar_url":"https://avatars.githubusercontent.com/u/32734153?v=4"},{"login":"jackwellerreal","avatar_url":"https://avatars.githubusercontent.com/u/97435840?v=4"},{"login":"mvanhorn","avatar_url":"https://avatars.githubusercontent.com/u/455140?v=4"},{"login":"SergioChan","avatar_url":"https://avatars.githubusercontent.com/u/10103766?v=4"},{"login":"toineenzo","avatar_url":"https://avatars.githubusercontent.com/u/4921397?v=4"},{"login":"oben01","avatar_url":"https://avatars.githubusercontent.com/u/22369027?v=4"},{"login":"rezstje","avatar_url":"https://avatars.githubusercontent.com/u/45420315?v=4"},{"login":"castielwaverly","avatar_url":"https://avatars.githubusercontent.com/u/137005689?v=4"},{"login":"xoxyuxu","avatar_url":"https://avatars.githubusercontent.com/u/3543416?v=4"},{"login":"StrandedTurtle","avatar_url":"https://avatars.githubusercontent.com/u/132835255?v=4"},{"login":"MaienM","avatar_url":"https://avatars.githubusercontent.com/u/583129?v=4"},{"login":"kylegrieder","avatar_url":"https://avatars.githubusercontent.com/u/25424229?v=4"},{"login":"HeapReaper","avatar_url":"https://avatars.githubusercontent.com/u/31496522?v=4"},{"login":"Copilot","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},{"login":"Gilmoursa","avatar_url":"https://avatars.githubusercontent.com/u/10321421?v=4"},{"login":"leandroscardua","avatar_url":"https://avatars.githubusercontent.com/u/24698541?v=4"},{"login":"mairas","avatar_url":"https://avatars.githubusercontent.com/u/407010?v=4"},{"login":"itzTheMeow","avatar_url":"https://avatars.githubusercontent.com/u/50887230?v=4"},{"login":"NicolasNewman","avatar_url":"https://avatars.githubusercontent.com/u/16135852?v=4"},{"login":"NikeLaosClericus","avatar_url":"https://avatars.githubusercontent.com/u/39810627?v=4"},{"login":"noahcroghan","avatar_url":"https://avatars.githubusercontent.com/u/27422774?v=4"},{"login":"ohmzi","avatar_url":"https://avatars.githubusercontent.com/u/6551272?v=4"},{"login":"Rhiz3K","avatar_url":"https://avatars.githubusercontent.com/u/33246262?v=4"},{"login":"saschabuehrle","avatar_url":"https://avatars.githubusercontent.com/u/47737812?v=4"},{"login":"CrazyWolf13","avatar_url":"https://avatars.githubusercontent.com/u/96661824?v=4"},{"login":"VIVAAN-DHAWAN","avatar_url":"https://avatars.githubusercontent.com/u/150339722?v=4"},{"login":"generalmat82","avatar_url":"https://avatars.githubusercontent.com/u/110256073?v=4"},{"login":"lthomsen","avatar_url":"https://avatars.githubusercontent.com/u/28560325?v=4"},{"login":"pitschi","avatar_url":"https://avatars.githubusercontent.com/u/6841565?v=4"},{"login":"sharpsalat","avatar_url":"https://avatars.githubusercontent.com/u/15666932?v=4"},{"login":"tech62","avatar_url":"https://avatars.githubusercontent.com/u/16237642?v=4"},{"login":"AartSchinkel","avatar_url":"https://avatars.githubusercontent.com/u/189675191?v=4"},{"login":"afonsojramos","avatar_url":"https://avatars.githubusercontent.com/u/19473034?v=4"},{"login":"m0s4ik","avatar_url":"https://avatars.githubusercontent.com/u/26578395?v=4"},{"login":"ashtonbatty","avatar_url":"https://avatars.githubusercontent.com/u/230635?v=4"},{"login":"Oupsman","avatar_url":"https://avatars.githubusercontent.com/u/11260343?v=4"},{"login":"cethien","avatar_url":"https://avatars.githubusercontent.com/u/60463918?v=4"},{"login":"DZ945brH","avatar_url":"https://avatars.githubusercontent.com/u/42635243?v=4"},{"login":"dgshue","avatar_url":"https://avatars.githubusercontent.com/u/12952156?v=4"},{"login":"d-lindahl","avatar_url":"https://avatars.githubusercontent.com/u/690410?v=4"},{"login":"KartulUdus","avatar_url":"https://avatars.githubusercontent.com/u/23346150?v=4"},{"login":"ieugen","avatar_url":"https://avatars.githubusercontent.com/u/1050920?v=4"},{"login":"faugusto-oliveira","avatar_url":"https://avatars.githubusercontent.com/u/37384074?v=4"},{"login":"frankvHoof93","avatar_url":"https://avatars.githubusercontent.com/u/7346516?v=4"},{"login":"febrile42","avatar_url":"https://avatars.githubusercontent.com/u/14597122?v=4"},{"login":"Kaotic3","avatar_url":"https://avatars.githubusercontent.com/u/80522225?v=4"},{"login":"KelianB","avatar_url":"https://avatars.githubusercontent.com/u/11216155?v=4"},{"login":"hotrungnhan","avatar_url":"https://avatars.githubusercontent.com/u/40029631?v=4"}] \ No newline at end of file +[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"marektancak0106","avatar_url":"https://avatars.githubusercontent.com/u/25422667?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"beanbag420","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/u/47495914?v=4"},{"login":"bebsworthy","avatar_url":"https://avatars.githubusercontent.com/u/5266759?v=4"},{"login":"catrielmuller","avatar_url":"https://avatars.githubusercontent.com/u/2272323?v=4"},{"login":"Vysp3r","avatar_url":"https://avatars.githubusercontent.com/u/62019735?v=4"},{"login":"DR-Blogs","avatar_url":"https://avatars.githubusercontent.com/u/96393186?v=4"},{"login":"singular0","avatar_url":"https://avatars.githubusercontent.com/u/1925883?v=4"},{"login":"diogovalentte","avatar_url":"https://avatars.githubusercontent.com/u/49578155?v=4"},{"login":"codezninja","avatar_url":"https://avatars.githubusercontent.com/u/4400773?v=4"},{"login":"fabricionaweb","avatar_url":"https://avatars.githubusercontent.com/u/15933?v=4"},{"login":"nyok1912","avatar_url":"https://avatars.githubusercontent.com/u/32163949?v=4"},{"login":"bo3bdo","avatar_url":"https://avatars.githubusercontent.com/u/5271380?v=4"},{"login":"hskrtich","avatar_url":"https://avatars.githubusercontent.com/u/1214484?v=4"},{"login":"ishults","avatar_url":"https://avatars.githubusercontent.com/u/7275442?v=4"},{"login":"jelliuk","avatar_url":"https://avatars.githubusercontent.com/u/4790959?v=4"},{"login":"Moohan","avatar_url":"https://avatars.githubusercontent.com/u/5982260?v=4"},{"login":"tesar-tech","avatar_url":"https://avatars.githubusercontent.com/u/33880579?v=4"},{"login":"jbruell","avatar_url":"https://avatars.githubusercontent.com/u/31685172?v=4"},{"login":"JasonLeeB06","avatar_url":"https://avatars.githubusercontent.com/u/82266978?v=4"},{"login":"jeffersonraimon","avatar_url":"https://avatars.githubusercontent.com/u/80064475?v=4"},{"login":"jo-chemla","avatar_url":"https://avatars.githubusercontent.com/u/16822841?v=4"},{"login":"joser93","avatar_url":"https://avatars.githubusercontent.com/u/6912089?v=4"},{"login":"justijndepover","avatar_url":"https://avatars.githubusercontent.com/u/9008623?v=4"},{"login":"Aandree5","avatar_url":"https://avatars.githubusercontent.com/u/32734153?v=4"},{"login":"jackwellerreal","avatar_url":"https://avatars.githubusercontent.com/u/97435840?v=4"},{"login":"rezstje","avatar_url":"https://avatars.githubusercontent.com/u/45420315?v=4"},{"login":"oben01","avatar_url":"https://avatars.githubusercontent.com/u/22369027?v=4"},{"login":"toineenzo","avatar_url":"https://avatars.githubusercontent.com/u/4921397?v=4"},{"login":"SergioChan","avatar_url":"https://avatars.githubusercontent.com/u/10103766?v=4"},{"login":"mvanhorn","avatar_url":"https://avatars.githubusercontent.com/u/455140?v=4"},{"login":"Copilot","avatar_url":"https://avatars.githubusercontent.com/in/1143301?v=4"},{"login":"afonsojramos","avatar_url":"https://avatars.githubusercontent.com/u/19473034?v=4"},{"login":"castielwaverly","avatar_url":"https://avatars.githubusercontent.com/u/137005689?v=4"},{"login":"xoxyuxu","avatar_url":"https://avatars.githubusercontent.com/u/3543416?v=4"},{"login":"StrandedTurtle","avatar_url":"https://avatars.githubusercontent.com/u/132835255?v=4"},{"login":"MaienM","avatar_url":"https://avatars.githubusercontent.com/u/583129?v=4"},{"login":"kylegrieder","avatar_url":"https://avatars.githubusercontent.com/u/25424229?v=4"},{"login":"HeapReaper","avatar_url":"https://avatars.githubusercontent.com/u/31496522?v=4"},{"login":"Gilmoursa","avatar_url":"https://avatars.githubusercontent.com/u/10321421?v=4"},{"login":"leandroscardua","avatar_url":"https://avatars.githubusercontent.com/u/24698541?v=4"},{"login":"mairas","avatar_url":"https://avatars.githubusercontent.com/u/407010?v=4"},{"login":"itzTheMeow","avatar_url":"https://avatars.githubusercontent.com/u/50887230?v=4"},{"login":"NicolasNewman","avatar_url":"https://avatars.githubusercontent.com/u/16135852?v=4"},{"login":"NikeLaosClericus","avatar_url":"https://avatars.githubusercontent.com/u/39810627?v=4"},{"login":"noahcroghan","avatar_url":"https://avatars.githubusercontent.com/u/27422774?v=4"},{"login":"ohmzi","avatar_url":"https://avatars.githubusercontent.com/u/6551272?v=4"},{"login":"Rhiz3K","avatar_url":"https://avatars.githubusercontent.com/u/33246262?v=4"},{"login":"saschabuehrle","avatar_url":"https://avatars.githubusercontent.com/u/47737812?v=4"},{"login":"CrazyWolf13","avatar_url":"https://avatars.githubusercontent.com/u/96661824?v=4"},{"login":"VIVAAN-DHAWAN","avatar_url":"https://avatars.githubusercontent.com/u/150339722?v=4"},{"login":"generalmat82","avatar_url":"https://avatars.githubusercontent.com/u/110256073?v=4"},{"login":"lthomsen","avatar_url":"https://avatars.githubusercontent.com/u/28560325?v=4"},{"login":"pitschi","avatar_url":"https://avatars.githubusercontent.com/u/6841565?v=4"},{"login":"sharpsalat","avatar_url":"https://avatars.githubusercontent.com/u/15666932?v=4"},{"login":"tech62","avatar_url":"https://avatars.githubusercontent.com/u/16237642?v=4"},{"login":"AartSchinkel","avatar_url":"https://avatars.githubusercontent.com/u/189675191?v=4"},{"login":"m0s4ik","avatar_url":"https://avatars.githubusercontent.com/u/26578395?v=4"},{"login":"ashtonbatty","avatar_url":"https://avatars.githubusercontent.com/u/230635?v=4"},{"login":"Oupsman","avatar_url":"https://avatars.githubusercontent.com/u/11260343?v=4"},{"login":"cethien","avatar_url":"https://avatars.githubusercontent.com/u/60463918?v=4"},{"login":"DZ945brH","avatar_url":"https://avatars.githubusercontent.com/u/42635243?v=4"},{"login":"dgshue","avatar_url":"https://avatars.githubusercontent.com/u/12952156?v=4"},{"login":"lopsch","avatar_url":"https://avatars.githubusercontent.com/u/4073114?v=4"},{"login":"d-lindahl","avatar_url":"https://avatars.githubusercontent.com/u/690410?v=4"},{"login":"KartulUdus","avatar_url":"https://avatars.githubusercontent.com/u/23346150?v=4"},{"login":"ieugen","avatar_url":"https://avatars.githubusercontent.com/u/1050920?v=4"},{"login":"faugusto-oliveira","avatar_url":"https://avatars.githubusercontent.com/u/37384074?v=4"},{"login":"frankvHoof93","avatar_url":"https://avatars.githubusercontent.com/u/7346516?v=4"},{"login":"febrile42","avatar_url":"https://avatars.githubusercontent.com/u/14597122?v=4"},{"login":"Kaotic3","avatar_url":"https://avatars.githubusercontent.com/u/80522225?v=4"},{"login":"KelianB","avatar_url":"https://avatars.githubusercontent.com/u/11216155?v=4"},{"login":"hotrungnhan","avatar_url":"https://avatars.githubusercontent.com/u/40029631?v=4"}] \ No newline at end of file diff --git a/static-data/translators.json b/static-data/translators.json index 5818e6339..fb4b7b273 100644 --- a/static-data/translators.json +++ b/static-data/translators.json @@ -1 +1 @@ -[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14934947/medium/f5a8570713c34ab0f7d5405d105e2a9a.jpeg"},{"username":"f1refa11","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14949159/medium/fd2ae63b8eb4462200ba96abf943c1b9.png"},{"username":"jeffersonraimon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057621/medium/6dff469563860e2618bc9d45646d0ab4.png"},{"username":"giop98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057987/medium/b8a4825d3fc39fc662f35ee258db4b2d.jpeg"},{"username":"tomislav.kraljevic","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15118857/medium/e133f1061cc92850b854d05d8faaeafd.png"},{"username":"Walkx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15202182/medium/5c37361ae45aeed487b34582c1f7ca37.png"},{"username":"andrea.rosso","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15246318/medium/11f3f5ef44ec7f55b6f143090e208704_default.png"},{"username":"ajnart","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15270132/medium/a0f107a463c8910ee96bc2fa843a17e3.jpeg"},{"username":"Witchling","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15282238/medium/9e196b6783415b10ca571b7673c2ef33.jpg"},{"username":"guineuu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15292058/medium/22fbb758bda3b7805d50bf21d38f2c20.jpeg"},{"username":"Bulgus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15304568/medium/0e8787e5ceb02ed5c96a514d0068ae87.jpg"},{"username":"Narno","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15314996/medium/a6ba093f24884250a1bf6c6f2ca50f1b_default.png"},{"username":"BySempron","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15315986/medium/e3d22d7b1423c6823a9f36d595ed4bdb.png"},{"username":"jonathan.berglin.work","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15359236/medium/95930b2093db13b76179782f7322c5d5.png"},{"username":"icdmkg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405504/medium/f334d16cde1ac4ffb935b440d03465af.gif"},{"username":"irithys","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405614/medium/3086461c47cce0a0c031925e5f943412.png"},{"username":"JannesV","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419912/medium/a7809eb4b817d7c49b62cf10ae86b950.png"},{"username":"benniblot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419914/medium/8086b1573d31f152ba41949b3a172445.jpeg"},{"username":"pacjo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419916/medium/3cbeddbd7bc01faafb5a3bf47bba915b_default.png"},{"username":"Payou6994","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419934/medium/37c9b9b17dfb578404c1c1ddb73ba7a8.png"},{"username":"fzibi21","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419976/medium/0da688450358e0290a7b7359cc1f7328.png"},{"username":"WowMurdock","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420118/medium/de19576fccb495b6dfe4c4c04a56b834.png"},{"username":"hbooo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420120/medium/6c7c7f8db785061356ebb03d044d3329.jpeg"},{"username":"Manicraft1001","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420178/medium/50ec94563a06a9f74f33bd09f01eed4d.jpg"},{"username":"Void123","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420354/medium/86929d44df92a00f9fe900a985c196df_default.png"},{"username":"R4cc","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15422606/medium/a390979662b84694f59de30bdb732141.jpeg"},{"username":"fabricionaweb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15425808/medium/a9354142e7ae5152c144409d55fda551_default.png"},{"username":"JokeOfDead","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15426890/medium/a383eac9365e9de64fd4ab1d6fd0cb95.jpeg"},{"username":"hkz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15427174/medium/c88acefb0d7306e1f7470e872029fb39_default.png"},{"username":"Steken","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428516/medium/d5aea5653c769c3a523182bdb60d1664.png"},{"username":"flar.anton","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428592/medium/b01fdb365d892e9f811f77fcb50a80a2.jpeg"},{"username":"Bon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15433542/medium/5397da4dfc821f20b6ac14fe0c514e9a.jpeg"},{"username":"bfkadan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434162/medium/4f9a0b43cfe3acaea60124c14ba7f44a.png"},{"username":"BunnySweety","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434662/medium/f0ef200a6a0dcf0e1d0e9ecd4148f560_default.png"},{"username":"HRKings","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15436168/medium/69a31e461d38549f01864e7ef10d642e.png"},{"username":"wiston81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15439078/medium/10f292c8d12a7c21a44b54495fa4a3d8.jpeg"},{"username":"qaz0911","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15440860/medium/3d3e50ee388c72dc4bf7a771761f2d89_default.png"},{"username":"qqyule","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15441462/medium/7a3cdf82710ffb5d8f388bc0bd010665.png"},{"username":"Bims0n","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15445560/medium/603220b603eeb3367e0f0d3fa675247c.jpg"},{"username":"TariqDaCoder","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15446228/medium/50b0f4040112bbd67690b769477398e5_default.png"},{"username":"Anarchon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15449644/medium/e925e1f3e3ffbf0f982391ce263a1a28.jpeg"},{"username":"Ashun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15453020/medium/ccdcf51c73d6aae40751bb30beee1915_default.png"},{"username":"sebekmartin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15454038/medium/bcfb44598cdfd1d7cd4eb35812538962.jpeg"},{"username":"RagnarGraves","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15459882/medium/143d5af850c1154070a218bea124e9cb_default.png"},{"username":"PrtmPhlp","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15462414/medium/b80db55e9de301432dcd1f8c8b24fd49_default.png"},{"username":"DooYoo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15470768/medium/2a18cf4be67094724b508c9e1e698a21_default.png"},{"username":"espentruls","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486092/medium/8e38afc3a4ff669226a0cfd3e420ff3a.jpeg"},{"username":"frisco82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486922/medium/4e1c5d4189b42508e660daa3c1c25b2a.jpeg"},{"username":"lupineDK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15491798/medium/461bd501e8371c062bf29ea171aedd36_default.png"},{"username":"hillaliy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15492732/medium/0bae17b421604892d888e3fc70cf0587.jpeg"},{"username":"MarcOrfilaCarreras","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15501072/medium/ea52b26c3c6f21e4931e38e3ce3f3d6e.png"},{"username":"robertbridda","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15507822/medium/a368c2e30411bb2da9b49290084191f3.png"},{"username":"MKoniuszko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518090/medium/87605434fcc839f6763ab07c50f6d232.jpeg"},{"username":"HeroSizy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518710/medium/4e79c0e98cbeb536dd961e656331b509.png"},{"username":"dwt136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15520022/medium/554422503f2baea43ace85facb4546fb_default.png"},{"username":"asifthewebguy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15526719/medium/b18931dd0c800d725048bd440646198b_default.png"},{"username":"eiloogs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15545537/medium/f290a2f1190983530a9b76b2e858a609.gif"},{"username":"_vytdv","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15547289/medium/60d8644cc8ad6f11a92ccea4a14cd098_default.png"},{"username":"crendasien","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15554645/medium/598ab1d4aaf6b8dccd5ba16be92da7b9.jpeg"},{"username":"edxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15573823/medium/8a565b732a75a77f840dd123cdb30bf4.png"},{"username":"somerlev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15617065/medium/f4b13513e311ec902d90b2f718412c55.jpg"},{"username":"kid1412621","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15643771/medium/dd455e32de652fa88e6fd97598bdffa7.png"},{"username":"suming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15644717/medium/244159dfe10fa03436205506f80c9e25.png"},{"username":"nick.gher","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15647517/medium/5374a2c6fef60a3fbf0edf86b997c351_default.png"},{"username":"DataCat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15650315/medium/ce7c1365adf35c5d490d77500a4607fb_default.png"},{"username":"dizo89","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15658375/medium/0ca745e5017d491fe1b22b0239904de8.jpeg"},{"username":"tee_noodle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674577/medium/804d9cf06f5196026acb6436b809d0da_default.png"},{"username":"Marty88","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674593/medium/492b1509d52bd2809dea768121217125.jpeg"},{"username":"Spillebulle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15677023/medium/20b98bb85062e07afe0e63618f8a5feb.PNG"},{"username":"petitmewen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15685239/medium/15de9b62d2e0bc25013435f1784bbcc1.jpeg"},{"username":"NoProsNoNoobs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15687709/medium/ae8f65fafeb8bcf74dcb8871bbe46461.png"},{"username":"y.gybson","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15690777/medium/b5cb5d2d5768904ff6586c727e3a6c77.jpeg"},{"username":"inside90","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15704947/medium/c1355fcb30dd76f8e39d98d1d49f1c52.png"},{"username":"RJSkudra","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15709853/medium/c3abf2774913dc4e81fb261d36d7668c.png"},{"username":"binge203","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15713937/medium/77c1cfa4314673db80e6881fd9f64668.gif"},{"username":"Mailootje","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15714337/medium/743c3bc4ab1989966a375eeeec83d8b8.jpeg"},{"username":"tim-wiegers","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15719805/medium/08a96f064813350661cd6b20bf3d7d99.png"},{"username":"GkhnG","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15722911/medium/71a027caec489ef6ce82bcf1888329d0_default.png"},{"username":"Zoen-Millo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15760967/medium/9e956f11adc5b34f5636268b5c485dbf.jpg"},{"username":"HooinKyoma","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15792897/medium/9489f0a9b368e0e827ae758b740a2eed.jpeg"},{"username":"wathergeen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15794495/medium/a83b7fc80d180669ebfd6deb3544cfd0.png"},{"username":"MoeToo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15818233/medium/d35cd6953717706eaf20f6c143c62947.png"},{"username":"itodouble","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15836233/medium/e984caea18fb0673bf319fcf28cef649.png"},{"username":"loslocitos","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15847901/medium/fe30d12fd2cf38212f929e13b169f9ec.jpeg"},{"username":"Oversleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15849065/medium/06141f13a6d541d753f3c2f2947b8068_default.png"},{"username":"Beardy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15865139/medium/fca6b9d2b3f52e286d1568f52b83b6a0_default.png"},{"username":"raelyan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15875457/medium/2f4fda1d1aaa5dcc79b328baf3f03151.jpeg"},{"username":"dolphin738","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15913763/medium/ee6fede7b8528ca642329ada80d1cc18.png"},{"username":"brunotco","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15916719/medium/09db45880fc05abc18adb8d932a5ecf9_default.png"},{"username":"LukeIsHereToDevelop","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15924739/medium/3e6cd3940297bb4ba7bb039c93e6f918.png"},{"username":"kennit","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15925879/medium/6b0733ad3c5949b91c55e4d8b03db8a5_default.png"},{"username":"kuunpire","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15950309/medium/8192a4f08f07086828ac9f74ed29a169.jpeg"},{"username":"Sandor-dev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15951759/medium/0216c2da4eb028164ebbecf1c72f6271_default.png"},{"username":"Meierschlumpf","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15953187/medium/23c744faef1ab84fbdc9351a7850aab6.jpeg"},{"username":"harmlesscat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15970733/medium/dd8d0214a0250c932bb518b1b55e45a2_default.png"},{"username":"OrzWTF","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15976121/medium/4c4557cbff7ff7b0503455bc59c020e0.jpeg"},{"username":"tagaishi","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15977271/medium/eade504c83a5a1ff831c80a538fbdb44_default.png"},{"username":"azurite928","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15981895/medium/547ccc459ee123e78b5401c499f1022d.png"},{"username":"Ronner231","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16021342/medium/7734d550df2de5a2fec2ffff33e7024c.jpeg"},{"username":"ugyes","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16034148/medium/ed001e3f470a2dea9a8ce955b18e7bd5.png"},{"username":"rpieja","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16045554/medium/bd55baca2ef8b92502a760cc9ee7c505_default.png"},{"username":"flambyisyou","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16051620/medium/3a3bc0c90f6b95ab4ef74396a0a17beb.png"},{"username":"Topbcy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16077170/medium/d3aed33ea56330338756cfcd89477cfe.jpeg"},{"username":"ai5d02sb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16084674/medium/7c8119fe2a5ca71bb15f636916a42b95_default.png"},{"username":"explosiveparrot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16097722/medium/7762f80fc1da63f5b2eb87de9d640324_default.png"},{"username":"DestroyCom","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16111544/medium/83e3d3ac5c8bfed634cd1a074854db67.png"},{"username":"neotraxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16116966/medium/b30df957873329019a8a90a69f6efe3a.png"},{"username":"gzxpa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16123314/medium/487b05577310a946b0b98bc72c11213b_default.png"},{"username":"TORIK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16124732/medium/de770890e6411860ac06e8cc2dc0abd0_default.png"},{"username":"sergio.pibot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16138114/medium/80db802b2fcd6bbf19ea4c7413367dca.jpeg"},{"username":"karam-ajaj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16139838/medium/7539ab958d3c85d5f67e7849483ea8f7.jpeg"},{"username":"SimonHenz97","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16151520/medium/9b94a85b536688e2d94538c384fd0a76.png"},{"username":"olsson82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16198988/medium/eeaab4cba210c537424d4204754b604e.png"},{"username":"bo3bdo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16222806/medium/8652f3beacb6098dde5a4cd0a29fe80d.jpeg"},{"username":"AngryPlayer04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245192/medium/342715274f4dc31fd4132cdae9126188.jpeg"},{"username":"ehatamm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245884/medium/979d5e5526fe545be9bf46db481f7182.jpeg"},{"username":"Balionelis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16246058/medium/986d62ba363594c517d966fe9178dcbb.png"},{"username":"kwargs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16248816/medium/19ce11f04bf1ae2637e0483399c675f9_default.png"},{"username":"lucicu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16273892/medium/3b09e8f3bead221ef98d6a45a16851dc_default.png"},{"username":"Michael5564445","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16357688/medium/9d9026a8bf3eaccb0417da240932ebf7.png"},{"username":"Netscream","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16359198/medium/694e0c8572dc8a47e6d63da31ec5b992.png"},{"username":"belicstefan04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16368142/medium/4742dcfd7bf7ad7e33e37ac44fba7e0f.png"},{"username":"Hamad2066","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16371478/medium/f37bbc7685b6757defdbd1494236af61.jpeg"},{"username":"3ct0pl4sm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16399616/medium/ce796d1c40163975624c0a483e56374f.png"},{"username":"sanchez.marcell626","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16433865/medium/3ddde1314764cc2205ffdcd1ae719a91.png"},{"username":"Minoctis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16490079/medium/91fd0849303a4eca033d660d0a7b6854.jpeg"},{"username":"homarr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16657003/medium/3afabfd3de949a54ff5d40ad04552aba.png"},{"username":"mwgg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16658379/medium/763dd09f5c238cc5324d4d5357985292.png"},{"username":"kiwinho","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16671855/medium/bfe3a2d497dc4748a04d02e4ae2139cb.jpeg"},{"username":"tjvg4m34r13","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16679135/medium/7a483b9b0abe0d925e54808e1cb38fa0.jpeg"},{"username":"Diegoem","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16705921/medium/29ce68ebc191fc3f65499511b12de98f_default.png"},{"username":"bouks69","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16746091/medium/b58b1fc08520de416198be9df5d093eb.jpeg"},{"username":"futhgar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16748067/medium/b8c526b097f9aa9d687250cac3df16dc.jpeg"},{"username":"GrimSQL","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16764343/medium/fff729a76d1f8fbcdaee9bb0c0f5f3fd.png"},{"username":"chnikel","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767047/medium/3dda2a37e03049dafefe6697bb00f75c.png"},{"username":"yuki19870616","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767951/medium/f59c848e38382cda6c8464b0a7b9e642_default.png"},{"username":"noamdal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16782245/medium/b4a72a61f9caf138f0322c0cb987e6d0.png"},{"username":"MalithRukshan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16809057/medium/084d6a5b35fbbeacc70e7021197d222f.png"},{"username":"minirope19","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16930929/medium/e8cbbc26747e31c5a8959215e1dd067b.jpeg"},{"username":"prubin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17503492/medium/96ac87644691e49c1f983f2f094e541c_default.png"},{"username":"sam-tam-sam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17578052/medium/1628dc2954ce3ae95e0caaa2bd60ab0c.png"},{"username":"toineenzo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12371852/medium/9f1b2bedb4794c59479cd289bdba9fab.png"},{"username":"ArcaneWater","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12421105/medium/1ac82517a6c658f6b62c48aebcb17e86_default.png"},{"username":"Ebedami","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12974801/medium/b596dea5c09454c4c828eac558389755.jpg"},{"username":"perrygamerptyt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13117177/medium/1406c949507ecd438480473c8a1f6124.jpg"},{"username":"SergiSvK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13320543/medium/3df3c79cc5f0c53716c559cd3fd2fc20.png"},{"username":"jeongeon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13362856/medium/eb572f72cde5e5526c517ca3a6489850_default.png"},{"username":"MrTranCFCVN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13439475/medium/a1789d69c85d90c667194b5d6a518c08_default.png"},{"username":"Tobost06","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13467449/medium/9227dc07f6366bc060d28304f5cb8ce7.jpeg"},{"username":"Kf637","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13530622/medium/118b00bb72d01d3229c97409637e25d1_default.png"},{"username":"Cruellest","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13610615/medium/2a74faa0947ee08d18f7946fb8602c1e.jpg"},{"username":"olavinto","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14424644/medium/37c01732bd2b7a272b421807a05ccfeb.png"},{"username":"Thario","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14491372/medium/dd5e142070802a672900121aa3f332a6_default.png"},{"username":"Wjouter","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14585620/medium/b91a51513332bdc0a0a9ecbed99ecbc3.png"},{"username":"albanobattistella","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14686194/medium/564afcfd76f5cd24751bd669cf4943aa.jpg"},{"username":"Kachelkaiser","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14763406/medium/203a781797da4a18fe0f4e7db8fd547c.png"},{"username":"regi4","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14842936/medium/f7e1d672f562a7db61ba6b4b8f4de41d.png"},{"username":"baton5129","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14891064/medium/8d438f557b82977dd02f0ead3187265f.PNG"},{"username":"meviper","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15958903/medium/209f521ce33a05831fa8d7d1b7babc20_default.png"},{"username":"GerOriki","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16268376/medium/da00e54b08cd40348231d92873a0cd0a_default.png"},{"username":"TheRedCyclops","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16486399/medium/7f39226180120d5a9efcb8e13d8aec8e_default.png"},{"username":"dymek37","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16659221/medium/d1dc6d11d7e7fc370efc5070e837ae03.png"},{"username":"B3CKDOOR","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16744975/medium/e6c7b2d45e76465d54e6efb67c83da7d.png"},{"username":"Larsluph","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16779933/medium/ed7d36f8b44013bad3a16ac0491bd0f7.png"},{"username":"FOEDI","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16781001/medium/6b4552fb72e359aa016f7a054078fac8.png"},{"username":"xmarkurion","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16805443/medium/34a20428427de5aaef99c7d140646803.png"},{"username":"gregid","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16806705/medium/f3c8f4a20436e8538929130142bbc104.png"},{"username":"Flameberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16820453/medium/d47be866148474c9d05fec6fbe406d88.png"},{"username":"Playfust","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16872865/medium/5f4609cc711e5fb9fa8a0b178635769c.jpeg"},{"username":"Cotignac","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16879297/medium/81ff353edf030699a159bb79cda26e84.jpg"},{"username":"LeaHun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16916375/medium/618520f0543b1e212ebc83d66b683d4b_default.png"},{"username":"jackwellerreal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16919717/medium/37bbfdc03893f141133ccc619593266d.png"},{"username":"renan.costaalencar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16938427/medium/6d25b25320c5bde734118dec4af9cd5b.png"},{"username":"andrius.cim","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16967845/medium/46028fc1a0ddfaaeebcb37b39739625d_default.png"},{"username":"xjxexrxrxyx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17041412/medium/d34d664166db6fb25579bca9ff3b50b4.jpeg"},{"username":"bilhal.fellah","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17044572/medium/d1ec6f17e4aa013e9eededc83778d2c6.png"},{"username":"the_octo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17058060/medium/7423ab3a4cabb67f5decb2e2dfa2b675_default.png"},{"username":"BamesJond","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17075846/medium/128d7ffd40254c8e376f45678e2fc620_default.png"},{"username":"Kornikus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17134240/medium/d7a42b2c30db46ff152699ca01f9e11a_default.png"},{"username":"xoxyuxu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17176526/medium/108055aa24343043de852e412bec74be.png"},{"username":"cryptodntsleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17249408/medium/b9e15aa2b24a0dbc1e4f194aa8ed5109.png"},{"username":"yaniorg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17261842/medium/7dbfc77f21c826845961243c44540d26.jpg"},{"username":"Vidariondr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17262438/medium/45fbc71be02ef27296b6f423e97b6a03_default.png"},{"username":"Fastery","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17263066/medium/701b0edc59676174863ad651528bbb37.jpeg"},{"username":"stratself","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17290388/medium/57d9d8161b3fd0abf4dc5558b4a1fa05_default.png"},{"username":"wuzeqin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17292606/medium/b9e0308410b31337e2d7aa6ed346d718_default.png"},{"username":"ChristopherBull","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17304338/medium/21a175ce596bfae81430767467908b90.jpeg"},{"username":"darkbcnlord","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17311436/medium/b271bc0b419ec3a26ddb7b63ff8ffc4b.png"},{"username":"zhoran1997","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17344028/medium/c8f450b5a8f1e9494a45dc60af7ce63e.png"},{"username":"gilalves1703","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17447768/medium/3f1de562a5c2391c88bbf8d5cf40a266.png"},{"username":"iServeCloud","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17470960/medium/11649b99d609438d66455a4d2fdc70f7.png"},{"username":"guibolzantvbox","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17474136/medium/59a576e6da538664ceb972253916fa02.jpeg"},{"username":"ivann136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17506062/medium/67d09ada36fac5f444a1f7517e3a97bd_default.png"},{"username":"Maheshivara","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17516082/medium/576e1e6581cf969615b265de6aeff8b7.jpeg"},{"username":"brunogui81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17517542/medium/7c0d174e810435af518fe8e0c05e151f.jpeg"},{"username":"michelwilhelm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17542190/medium/4c6b33fd8993ff565fa75add2ce8355e.jpeg"},{"username":"dias-gxstavo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17547376/medium/8c6b109aae8aeb651192346a39ae9e5b.jpeg"},{"username":"ToxykAuBleu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17614030/medium/8d21e0608560172b121bd46126d827c7.jpeg"},{"username":"e.erdemir","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17617994/medium/7259a29db9941c6171a8cbbd643b92bd.png"},{"username":"sjonkreutz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17618306/medium/7601c45d015189a863656e21348d223b.png"},{"username":"volovich0377","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17620388/medium/c929a8fbd24f96d17620fa497ade7309.png"},{"username":"Hamstio1","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17624898/medium/406e0bf40094c24a01c3064f8f5b2a82.png"},{"username":"i3laa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17627462/medium/613a9d283ed8b69819ee181903851660.jpeg"},{"username":"bastiengrignon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17629884/medium/ddfc3339285bd65195fcd834e512e9b7.jpeg"},{"username":"kauhee","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17644600/medium/def1b9066cf9275b4273a5a5dca7f325.png"},{"username":"hongminzheng","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17683860/medium/a764f4963912777d27a7cd98e136a2b2.png"},{"username":"d-lindahl","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17726729/medium/cbde348582fbabcfadd89a56cd5a0b40.png"},{"username":"Enoal-Fauchille-Bolle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17734649/medium/6e6fb29198782dc7856ea21811db669c.jpeg"}] \ No newline at end of file +[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/aaa1f083e04c572f953fde4563171e4a.jpeg"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14934947/medium/f5a8570713c34ab0f7d5405d105e2a9a.jpeg"},{"username":"f1refa11","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14949159/medium/fd2ae63b8eb4462200ba96abf943c1b9.png"},{"username":"jeffersonraimon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057621/medium/6dff469563860e2618bc9d45646d0ab4.png"},{"username":"giop98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15057987/medium/b8a4825d3fc39fc662f35ee258db4b2d.jpeg"},{"username":"tomislav.kraljevic","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15118857/medium/e133f1061cc92850b854d05d8faaeafd.png"},{"username":"Walkx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15202182/medium/5c37361ae45aeed487b34582c1f7ca37.png"},{"username":"andrea.rosso","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15246318/medium/11f3f5ef44ec7f55b6f143090e208704_default.png"},{"username":"ajnart","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15270132/medium/a0f107a463c8910ee96bc2fa843a17e3.jpeg"},{"username":"Witchling","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15282238/medium/9e196b6783415b10ca571b7673c2ef33.jpg"},{"username":"guineuu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15292058/medium/22fbb758bda3b7805d50bf21d38f2c20.jpeg"},{"username":"Bulgus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15304568/medium/0e8787e5ceb02ed5c96a514d0068ae87.jpg"},{"username":"Narno","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15314996/medium/a6ba093f24884250a1bf6c6f2ca50f1b_default.png"},{"username":"BySempron","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15315986/medium/e3d22d7b1423c6823a9f36d595ed4bdb.png"},{"username":"jonathan.berglin.work","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15359236/medium/95930b2093db13b76179782f7322c5d5.png"},{"username":"icdmkg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405504/medium/f334d16cde1ac4ffb935b440d03465af.gif"},{"username":"irithys","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15405614/medium/3086461c47cce0a0c031925e5f943412.png"},{"username":"JannesV","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419912/medium/a7809eb4b817d7c49b62cf10ae86b950.png"},{"username":"benniblot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419914/medium/8086b1573d31f152ba41949b3a172445.jpeg"},{"username":"pacjo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419916/medium/3cbeddbd7bc01faafb5a3bf47bba915b_default.png"},{"username":"Payou6994","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419934/medium/37c9b9b17dfb578404c1c1ddb73ba7a8.png"},{"username":"fzibi21","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15419976/medium/0da688450358e0290a7b7359cc1f7328.png"},{"username":"WowMurdock","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420118/medium/de19576fccb495b6dfe4c4c04a56b834.png"},{"username":"hbooo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420120/medium/6c7c7f8db785061356ebb03d044d3329.jpeg"},{"username":"Manicraft1001","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420178/medium/50ec94563a06a9f74f33bd09f01eed4d.jpg"},{"username":"Void123","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15420354/medium/86929d44df92a00f9fe900a985c196df_default.png"},{"username":"R4cc","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15422606/medium/a390979662b84694f59de30bdb732141.jpeg"},{"username":"fabricionaweb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15425808/medium/a9354142e7ae5152c144409d55fda551_default.png"},{"username":"JokeOfDead","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15426890/medium/a383eac9365e9de64fd4ab1d6fd0cb95.jpeg"},{"username":"hkz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15427174/medium/c88acefb0d7306e1f7470e872029fb39_default.png"},{"username":"Steken","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428516/medium/d5aea5653c769c3a523182bdb60d1664.png"},{"username":"flar.anton","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15428592/medium/b01fdb365d892e9f811f77fcb50a80a2.jpeg"},{"username":"Bon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15433542/medium/5397da4dfc821f20b6ac14fe0c514e9a.jpeg"},{"username":"bfkadan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434162/medium/4f9a0b43cfe3acaea60124c14ba7f44a.png"},{"username":"BunnySweety","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15434662/medium/f0ef200a6a0dcf0e1d0e9ecd4148f560_default.png"},{"username":"HRKings","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15436168/medium/69a31e461d38549f01864e7ef10d642e.png"},{"username":"wiston81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15439078/medium/10f292c8d12a7c21a44b54495fa4a3d8.jpeg"},{"username":"qaz0911","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15440860/medium/3d3e50ee388c72dc4bf7a771761f2d89_default.png"},{"username":"qqyule","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15441462/medium/7a3cdf82710ffb5d8f388bc0bd010665.png"},{"username":"Bims0n","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15445560/medium/603220b603eeb3367e0f0d3fa675247c.jpg"},{"username":"TariqDaCoder","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15446228/medium/50b0f4040112bbd67690b769477398e5_default.png"},{"username":"Anarchon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15449644/medium/e925e1f3e3ffbf0f982391ce263a1a28.jpeg"},{"username":"Ashun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15453020/medium/ccdcf51c73d6aae40751bb30beee1915_default.png"},{"username":"sebekmartin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15454038/medium/bcfb44598cdfd1d7cd4eb35812538962.jpeg"},{"username":"RagnarGraves","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15459882/medium/143d5af850c1154070a218bea124e9cb_default.png"},{"username":"PrtmPhlp","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15462414/medium/b80db55e9de301432dcd1f8c8b24fd49_default.png"},{"username":"DooYoo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15470768/medium/2a18cf4be67094724b508c9e1e698a21_default.png"},{"username":"espentruls","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486092/medium/8e38afc3a4ff669226a0cfd3e420ff3a.jpeg"},{"username":"frisco82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15486922/medium/4e1c5d4189b42508e660daa3c1c25b2a.jpeg"},{"username":"lupineDK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15491798/medium/461bd501e8371c062bf29ea171aedd36_default.png"},{"username":"hillaliy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15492732/medium/0bae17b421604892d888e3fc70cf0587.jpeg"},{"username":"MarcOrfilaCarreras","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15501072/medium/ea52b26c3c6f21e4931e38e3ce3f3d6e.png"},{"username":"robertbridda","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15507822/medium/a368c2e30411bb2da9b49290084191f3.png"},{"username":"MKoniuszko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518090/medium/87605434fcc839f6763ab07c50f6d232.jpeg"},{"username":"HeroSizy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15518710/medium/4e79c0e98cbeb536dd961e656331b509.png"},{"username":"dwt136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15520022/medium/554422503f2baea43ace85facb4546fb_default.png"},{"username":"asifthewebguy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15526719/medium/b18931dd0c800d725048bd440646198b_default.png"},{"username":"eiloogs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15545537/medium/f290a2f1190983530a9b76b2e858a609.gif"},{"username":"_vytdv","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15547289/medium/60d8644cc8ad6f11a92ccea4a14cd098_default.png"},{"username":"crendasien","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15554645/medium/598ab1d4aaf6b8dccd5ba16be92da7b9.jpeg"},{"username":"edxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15573823/medium/8a565b732a75a77f840dd123cdb30bf4.png"},{"username":"somerlev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15617065/medium/f4b13513e311ec902d90b2f718412c55.jpg"},{"username":"kid1412621","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15643771/medium/dd455e32de652fa88e6fd97598bdffa7.png"},{"username":"suming","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15644717/medium/244159dfe10fa03436205506f80c9e25.png"},{"username":"nick.gher","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15647517/medium/5374a2c6fef60a3fbf0edf86b997c351_default.png"},{"username":"DataCat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15650315/medium/ce7c1365adf35c5d490d77500a4607fb_default.png"},{"username":"dizo89","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15658375/medium/0ca745e5017d491fe1b22b0239904de8.jpeg"},{"username":"tee_noodle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674577/medium/804d9cf06f5196026acb6436b809d0da_default.png"},{"username":"Marty88","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15674593/medium/492b1509d52bd2809dea768121217125.jpeg"},{"username":"Spillebulle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15677023/medium/20b98bb85062e07afe0e63618f8a5feb.PNG"},{"username":"petitmewen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15685239/medium/15de9b62d2e0bc25013435f1784bbcc1.jpeg"},{"username":"NoProsNoNoobs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15687709/medium/ae8f65fafeb8bcf74dcb8871bbe46461.png"},{"username":"y.gybson","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15690777/medium/b5cb5d2d5768904ff6586c727e3a6c77.jpeg"},{"username":"inside90","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15704947/medium/c1355fcb30dd76f8e39d98d1d49f1c52.png"},{"username":"RJSkudra","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15709853/medium/c3abf2774913dc4e81fb261d36d7668c.png"},{"username":"binge203","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15713937/medium/77c1cfa4314673db80e6881fd9f64668.gif"},{"username":"Mailootje","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15714337/medium/743c3bc4ab1989966a375eeeec83d8b8.jpeg"},{"username":"tim-wiegers","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15719805/medium/08a96f064813350661cd6b20bf3d7d99.png"},{"username":"GkhnG","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15722911/medium/71a027caec489ef6ce82bcf1888329d0_default.png"},{"username":"Zoen-Millo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15760967/medium/9e956f11adc5b34f5636268b5c485dbf.jpg"},{"username":"HooinKyoma","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15792897/medium/9489f0a9b368e0e827ae758b740a2eed.jpeg"},{"username":"wathergeen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15794495/medium/a83b7fc80d180669ebfd6deb3544cfd0.png"},{"username":"MoeToo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15818233/medium/d35cd6953717706eaf20f6c143c62947.png"},{"username":"itodouble","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15836233/medium/e984caea18fb0673bf319fcf28cef649.png"},{"username":"loslocitos","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15847901/medium/fe30d12fd2cf38212f929e13b169f9ec.jpeg"},{"username":"Oversleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15849065/medium/06141f13a6d541d753f3c2f2947b8068_default.png"},{"username":"Beardy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15865139/medium/fca6b9d2b3f52e286d1568f52b83b6a0_default.png"},{"username":"raelyan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15875457/medium/2f4fda1d1aaa5dcc79b328baf3f03151.jpeg"},{"username":"dolphin738","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15913763/medium/ee6fede7b8528ca642329ada80d1cc18.png"},{"username":"brunotco","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15916719/medium/09db45880fc05abc18adb8d932a5ecf9_default.png"},{"username":"LukeIsHereToDevelop","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15924739/medium/3e6cd3940297bb4ba7bb039c93e6f918.png"},{"username":"kennit","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15925879/medium/6b0733ad3c5949b91c55e4d8b03db8a5_default.png"},{"username":"kuunpire","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15950309/medium/8192a4f08f07086828ac9f74ed29a169.jpeg"},{"username":"Sandor-dev","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15951759/medium/0216c2da4eb028164ebbecf1c72f6271_default.png"},{"username":"Meierschlumpf","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15953187/medium/23c744faef1ab84fbdc9351a7850aab6.jpeg"},{"username":"harmlesscat","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15970733/medium/dd8d0214a0250c932bb518b1b55e45a2_default.png"},{"username":"OrzWTF","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15976121/medium/4c4557cbff7ff7b0503455bc59c020e0.jpeg"},{"username":"tagaishi","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15977271/medium/eade504c83a5a1ff831c80a538fbdb44_default.png"},{"username":"azurite928","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15981895/medium/547ccc459ee123e78b5401c499f1022d.png"},{"username":"Ronner231","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16021342/medium/7734d550df2de5a2fec2ffff33e7024c.jpeg"},{"username":"ugyes","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16034148/medium/ed001e3f470a2dea9a8ce955b18e7bd5.png"},{"username":"rpieja","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16045554/medium/bd55baca2ef8b92502a760cc9ee7c505_default.png"},{"username":"flambyisyou","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16051620/medium/3a3bc0c90f6b95ab4ef74396a0a17beb.png"},{"username":"Topbcy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16077170/medium/d3aed33ea56330338756cfcd89477cfe.jpeg"},{"username":"ai5d02sb","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16084674/medium/7c8119fe2a5ca71bb15f636916a42b95_default.png"},{"username":"explosiveparrot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16097722/medium/7762f80fc1da63f5b2eb87de9d640324_default.png"},{"username":"DestroyCom","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16111544/medium/83e3d3ac5c8bfed634cd1a074854db67.png"},{"username":"neotraxo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16116966/medium/b30df957873329019a8a90a69f6efe3a.png"},{"username":"gzxpa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16123314/medium/487b05577310a946b0b98bc72c11213b_default.png"},{"username":"TORIK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16124732/medium/de770890e6411860ac06e8cc2dc0abd0_default.png"},{"username":"sergio.pibot","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16138114/medium/80db802b2fcd6bbf19ea4c7413367dca.jpeg"},{"username":"karam-ajaj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16139838/medium/7539ab958d3c85d5f67e7849483ea8f7.jpeg"},{"username":"SimonHenz97","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16151520/medium/9b94a85b536688e2d94538c384fd0a76.png"},{"username":"olsson82","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16198988/medium/eeaab4cba210c537424d4204754b604e.png"},{"username":"bo3bdo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16222806/medium/8652f3beacb6098dde5a4cd0a29fe80d.jpeg"},{"username":"AngryPlayer04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245192/medium/342715274f4dc31fd4132cdae9126188.jpeg"},{"username":"ehatamm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16245884/medium/979d5e5526fe545be9bf46db481f7182.jpeg"},{"username":"Balionelis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16246058/medium/986d62ba363594c517d966fe9178dcbb.png"},{"username":"kwargs","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16248816/medium/19ce11f04bf1ae2637e0483399c675f9_default.png"},{"username":"lucicu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16273892/medium/3b09e8f3bead221ef98d6a45a16851dc_default.png"},{"username":"Michael5564445","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16357688/medium/9d9026a8bf3eaccb0417da240932ebf7.png"},{"username":"Netscream","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16359198/medium/694e0c8572dc8a47e6d63da31ec5b992.png"},{"username":"belicstefan04","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16368142/medium/4742dcfd7bf7ad7e33e37ac44fba7e0f.png"},{"username":"Hamad2066","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16371478/medium/f37bbc7685b6757defdbd1494236af61.jpeg"},{"username":"3ct0pl4sm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16399616/medium/ce796d1c40163975624c0a483e56374f.png"},{"username":"sanchez.marcell626","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16433865/medium/3ddde1314764cc2205ffdcd1ae719a91.png"},{"username":"Minoctis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16490079/medium/91fd0849303a4eca033d660d0a7b6854.jpeg"},{"username":"homarr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16657003/medium/3afabfd3de949a54ff5d40ad04552aba.png"},{"username":"mwgg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16658379/medium/763dd09f5c238cc5324d4d5357985292.png"},{"username":"kiwinho","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16671855/medium/bfe3a2d497dc4748a04d02e4ae2139cb.jpeg"},{"username":"tjvg4m34r13","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16679135/medium/7a483b9b0abe0d925e54808e1cb38fa0.jpeg"},{"username":"Diegoem","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16705921/medium/29ce68ebc191fc3f65499511b12de98f_default.png"},{"username":"bouks69","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16746091/medium/b58b1fc08520de416198be9df5d093eb.jpeg"},{"username":"futhgar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16748067/medium/b8c526b097f9aa9d687250cac3df16dc.jpeg"},{"username":"GrimSQL","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16764343/medium/fff729a76d1f8fbcdaee9bb0c0f5f3fd.png"},{"username":"chnikel","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767047/medium/3dda2a37e03049dafefe6697bb00f75c.png"},{"username":"yuki19870616","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16767951/medium/f59c848e38382cda6c8464b0a7b9e642_default.png"},{"username":"noamdal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16782245/medium/b4a72a61f9caf138f0322c0cb987e6d0.png"},{"username":"MalithRukshan","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16809057/medium/084d6a5b35fbbeacc70e7021197d222f.png"},{"username":"minirope19","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16930929/medium/e8cbbc26747e31c5a8959215e1dd067b.jpeg"},{"username":"prubin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17503492/medium/96ac87644691e49c1f983f2f094e541c_default.png"},{"username":"sam-tam-sam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17578052/medium/1628dc2954ce3ae95e0caaa2bd60ab0c.png"},{"username":"toineenzo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12371852/medium/9f1b2bedb4794c59479cd289bdba9fab.png"},{"username":"ArcaneWater","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12421105/medium/1ac82517a6c658f6b62c48aebcb17e86_default.png"},{"username":"Ebedami","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12974801/medium/b596dea5c09454c4c828eac558389755.jpg"},{"username":"perrygamerptyt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13117177/medium/1406c949507ecd438480473c8a1f6124.jpg"},{"username":"SergiSvK","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13320543/medium/3df3c79cc5f0c53716c559cd3fd2fc20.png"},{"username":"jeongeon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13362856/medium/eb572f72cde5e5526c517ca3a6489850_default.png"},{"username":"MrTranCFCVN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13439475/medium/a1789d69c85d90c667194b5d6a518c08_default.png"},{"username":"Tobost06","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13467449/medium/9227dc07f6366bc060d28304f5cb8ce7.jpeg"},{"username":"Kf637","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13530622/medium/118b00bb72d01d3229c97409637e25d1_default.png"},{"username":"Cruellest","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13610615/medium/2a74faa0947ee08d18f7946fb8602c1e.jpg"},{"username":"olavinto","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14424644/medium/37c01732bd2b7a272b421807a05ccfeb.png"},{"username":"Thario","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14491372/medium/dd5e142070802a672900121aa3f332a6_default.png"},{"username":"Wjouter","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14585620/medium/b91a51513332bdc0a0a9ecbed99ecbc3.png"},{"username":"albanobattistella","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14686194/medium/564afcfd76f5cd24751bd669cf4943aa.jpg"},{"username":"Kachelkaiser","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14763406/medium/203a781797da4a18fe0f4e7db8fd547c.png"},{"username":"regi4","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14842936/medium/f7e1d672f562a7db61ba6b4b8f4de41d.png"},{"username":"baton5129","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14891064/medium/8d438f557b82977dd02f0ead3187265f.PNG"},{"username":"meviper","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/15958903/medium/209f521ce33a05831fa8d7d1b7babc20_default.png"},{"username":"GerOriki","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16268376/medium/da00e54b08cd40348231d92873a0cd0a_default.png"},{"username":"TheRedCyclops","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16486399/medium/7f39226180120d5a9efcb8e13d8aec8e_default.png"},{"username":"dymek37","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16659221/medium/d1dc6d11d7e7fc370efc5070e837ae03.png"},{"username":"B3CKDOOR","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16744975/medium/e6c7b2d45e76465d54e6efb67c83da7d.png"},{"username":"on9686","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16768173/medium/6282fed9aafe0262a173a5ec0887e5d9_default.png"},{"username":"Larsluph","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16779933/medium/ed7d36f8b44013bad3a16ac0491bd0f7.png"},{"username":"FOEDI","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16781001/medium/6b4552fb72e359aa016f7a054078fac8.png"},{"username":"xmarkurion","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16805443/medium/34a20428427de5aaef99c7d140646803.png"},{"username":"gregid","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16806705/medium/f3c8f4a20436e8538929130142bbc104.png"},{"username":"Flameberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16820453/medium/d47be866148474c9d05fec6fbe406d88.png"},{"username":"Playfust","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16872865/medium/5f4609cc711e5fb9fa8a0b178635769c.jpeg"},{"username":"Cotignac","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16879297/medium/81ff353edf030699a159bb79cda26e84.jpg"},{"username":"LeaHun","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16916375/medium/618520f0543b1e212ebc83d66b683d4b_default.png"},{"username":"jackwellerreal","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16919717/medium/37bbfdc03893f141133ccc619593266d.png"},{"username":"renan.costaalencar","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16938427/medium/6d25b25320c5bde734118dec4af9cd5b.png"},{"username":"andrius.cim","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/16967845/medium/46028fc1a0ddfaaeebcb37b39739625d_default.png"},{"username":"xjxexrxrxyx","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17041412/medium/d34d664166db6fb25579bca9ff3b50b4.jpeg"},{"username":"bilhal.fellah","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17044572/medium/d1ec6f17e4aa013e9eededc83778d2c6.png"},{"username":"the_octo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17058060/medium/7423ab3a4cabb67f5decb2e2dfa2b675_default.png"},{"username":"BamesJond","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17075846/medium/128d7ffd40254c8e376f45678e2fc620_default.png"},{"username":"Kornikus","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17134240/medium/d7a42b2c30db46ff152699ca01f9e11a_default.png"},{"username":"xoxyuxu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17176526/medium/108055aa24343043de852e412bec74be.png"},{"username":"cryptodntsleep","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17249408/medium/b9e15aa2b24a0dbc1e4f194aa8ed5109.png"},{"username":"yaniorg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17261842/medium/7dbfc77f21c826845961243c44540d26.jpg"},{"username":"Vidariondr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17262438/medium/45fbc71be02ef27296b6f423e97b6a03_default.png"},{"username":"Fastery","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17263066/medium/701b0edc59676174863ad651528bbb37.jpeg"},{"username":"stratself","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17290388/medium/57d9d8161b3fd0abf4dc5558b4a1fa05_default.png"},{"username":"wuzeqin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17292606/medium/b9e0308410b31337e2d7aa6ed346d718_default.png"},{"username":"ChristopherBull","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17304338/medium/21a175ce596bfae81430767467908b90.jpeg"},{"username":"darkbcnlord","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17311436/medium/b271bc0b419ec3a26ddb7b63ff8ffc4b.png"},{"username":"zhoran1997","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17344028/medium/c8f450b5a8f1e9494a45dc60af7ce63e.png"},{"username":"gilalves1703","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17447768/medium/3f1de562a5c2391c88bbf8d5cf40a266.png"},{"username":"iServeCloud","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17470960/medium/11649b99d609438d66455a4d2fdc70f7.png"},{"username":"guibolzantvbox","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17474136/medium/59a576e6da538664ceb972253916fa02.jpeg"},{"username":"ivann136","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17506062/medium/67d09ada36fac5f444a1f7517e3a97bd_default.png"},{"username":"Maheshivara","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17516082/medium/576e1e6581cf969615b265de6aeff8b7.jpeg"},{"username":"brunogui81","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17517542/medium/7c0d174e810435af518fe8e0c05e151f.jpeg"},{"username":"michelwilhelm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17542190/medium/4c6b33fd8993ff565fa75add2ce8355e.jpeg"},{"username":"dias-gxstavo","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17547376/medium/8c6b109aae8aeb651192346a39ae9e5b.jpeg"},{"username":"ToxykAuBleu","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17614030/medium/8d21e0608560172b121bd46126d827c7.jpeg"},{"username":"e.erdemir","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17617994/medium/7259a29db9941c6171a8cbbd643b92bd.png"},{"username":"sjonkreutz","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17618306/medium/7601c45d015189a863656e21348d223b.png"},{"username":"volovich0377","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17620388/medium/c929a8fbd24f96d17620fa497ade7309.png"},{"username":"Hamstio1","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17624898/medium/406e0bf40094c24a01c3064f8f5b2a82.png"},{"username":"i3laa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17627462/medium/613a9d283ed8b69819ee181903851660.jpeg"},{"username":"bastiengrignon","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17629884/medium/ddfc3339285bd65195fcd834e512e9b7.jpeg"},{"username":"kauhee","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17644600/medium/def1b9066cf9275b4273a5a5dca7f325.png"},{"username":"hongminzheng","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17683860/medium/a764f4963912777d27a7cd98e136a2b2.png"},{"username":"d-lindahl","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17726729/medium/cbde348582fbabcfadd89a56cd5a0b40.png"},{"username":"Enoal-Fauchille-Bolle","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17734649/medium/6e6fb29198782dc7856ea21811db669c.jpeg"},{"username":"manos-grigorakis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17737973/medium/b37f685587c920c81fdaf9a357962434.jpeg"},{"username":"SertraFurr","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17738703/medium/5f1e9f270e315ee3185cf2ccb3f5a833.png"},{"username":"Kle.Ma","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17739207/medium/b767d6093e6608f251ca152f53d359b8_default.png"},{"username":"WalkerLukas","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/17744485/medium/87508ce83a9b6e5391f55883a66a054b.png"}] \ No newline at end of file From 094f23778c9271c7eaedb846134065aec4a404b1 Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:23:50 +0200 Subject: [PATCH 08/17] chore(deps): update dependency @types/node to ^24.13.2 (#5970) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 204 +++++++++++++++++++++++++++++++------------- pnpm-workspace.yaml | 2 +- 2 files changed, 144 insertions(+), 62 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2057de41..e78b09371 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -244,8 +244,8 @@ catalogs: specifier: ^4.0.1 version: 4.0.1 '@types/node': - specifier: ^24.13.1 - version: 24.13.1 + specifier: ^24.13.2 + version: 24.13.2 '@types/node-unifi': specifier: ^2.5.1 version: 2.5.1 @@ -666,7 +666,7 @@ importers: version: 6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) vitest: specifier: 'catalog:' - version: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) apps/docs: dependencies: @@ -973,7 +973,7 @@ importers: version: 17.4.2 drizzle-orm: specifier: 'catalog:' - version: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1) + version: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1) flag-icons: specifier: 'catalog:' version: 7.5.0 @@ -1033,7 +1033,7 @@ importers: version: 2.2.6 trpc-to-mcp: specifier: 'catalog:' - version: 1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3) + version: 1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3) use-deep-compare-effect: specifier: 'catalog:' version: 1.8.1(react@19.2.7) @@ -1058,7 +1058,7 @@ importers: version: 3.1.2 '@types/node': specifier: 'catalog:' - version: 24.13.1 + version: 24.13.2 '@types/prismjs': specifier: 'catalog:' version: 1.26.6 @@ -1152,7 +1152,7 @@ importers: version: link:../../tooling/typescript '@types/node': specifier: 'catalog:' - version: 24.13.1 + version: 24.13.2 dotenv-cli: specifier: 'catalog:' version: 11.0.0 @@ -1343,7 +1343,7 @@ importers: version: 2.2.6 trpc-to-mcp: specifier: 'catalog:' - version: 1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.26.0(zod@4.4.3))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3) + version: 1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.26.0(zod@4.4.3))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3) trpc-to-openapi: specifier: 'catalog:' version: 3.3.0(patch_hash=2ca3c16af0fcca0c736697ad4fe553a14f794524fa9ce0d5c3e8ee4aea76090c)(@trpc/server@11.17.0(typescript@6.0.3))(zod-openapi@5.3.0(zod@4.4.3))(zod@4.4.3) @@ -7745,12 +7745,12 @@ packages: '@types/node@18.19.50': resolution: {integrity: sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==} - '@types/node@24.12.4': - resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} - '@types/node@24.13.1': resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==} + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -14547,9 +14547,6 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} @@ -16296,12 +16293,12 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.0 - '@better-auth/drizzle-adapter@1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))': + '@better-auth/drizzle-adapter@1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))': dependencies: '@better-auth/core': 1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0) '@better-auth/utils': 0.4.1 optionalDependencies: - drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1) + drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1) '@better-auth/kysely-adapter@1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(kysely@0.29.2)': dependencies: @@ -18582,12 +18579,12 @@ snapshots: optionalDependencies: '@types/node': 24.13.1 - '@inquirer/confirm@6.0.13(@types/node@24.13.1)': + '@inquirer/confirm@6.0.13(@types/node@24.13.2)': dependencies: - '@inquirer/core': 11.1.10(@types/node@24.13.1) - '@inquirer/type': 4.0.5(@types/node@24.13.1) + '@inquirer/core': 11.1.10(@types/node@24.13.2) + '@inquirer/type': 4.0.5(@types/node@24.13.2) optionalDependencies: - '@types/node': 24.13.1 + '@types/node': 24.13.2 optional: true '@inquirer/core@10.3.2(@types/node@24.13.1)': @@ -18603,17 +18600,17 @@ snapshots: optionalDependencies: '@types/node': 24.13.1 - '@inquirer/core@11.1.10(@types/node@24.13.1)': + '@inquirer/core@11.1.10(@types/node@24.13.2)': dependencies: '@inquirer/ansi': 2.0.5 '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@24.13.1) + '@inquirer/type': 4.0.5(@types/node@24.13.2) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 24.13.1 + '@types/node': 24.13.2 optional: true '@inquirer/editor@4.2.23(@types/node@24.13.1)': @@ -18712,9 +18709,9 @@ snapshots: optionalDependencies: '@types/node': 24.13.1 - '@inquirer/type@4.0.5(@types/node@24.13.1)': + '@inquirer/type@4.0.5(@types/node@24.13.2)': optionalDependencies: - '@types/node': 24.13.1 + '@types/node': 24.13.2 optional: true '@internationalized/date@3.12.2': @@ -18919,7 +18916,7 @@ snapshots: '@kubernetes/client-node@1.4.0': dependencies: '@types/js-yaml': 4.0.9 - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/node-fetch': 2.6.13 '@types/stream-buffers': 3.0.7 form-data: 4.0.5 @@ -21252,7 +21249,7 @@ snapshots: '@types/bcrypt@6.0.0': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/better-sqlite3@7.6.13': dependencies: @@ -21280,14 +21277,14 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/cookies@0.9.2': dependencies: '@types/connect': 3.4.38 '@types/express': 4.17.21 '@types/keygrip': 1.0.6 - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/crypto-js@4.2.2': {} @@ -21426,13 +21423,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/ssh2': 1.15.1 '@types/dockerode@4.0.1': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/ssh2': 1.15.1 '@types/estree-jsx@1.0.5': @@ -21512,7 +21509,7 @@ snapshots: '@types/node-fetch@2.6.13': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 form-data: 4.0.5 '@types/node-unifi@2.5.1(patch_hash=5e6ae51e2a17a7f9729bfa30b0eb3d0842a5810ac6db47603ab4a6efa1ed84c5)': @@ -21525,19 +21522,19 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@24.12.4': - dependencies: - undici-types: 7.16.0 - '@types/node@24.13.1': dependencies: undici-types: 7.18.2 + '@types/node@24.13.2': + dependencies: + undici-types: 7.18.2 + '@types/normalize-package-data@2.4.4': {} '@types/pg@8.20.0': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -21636,7 +21633,7 @@ snapshots: '@types/stream-buffers@3.0.7': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/triple-beam@1.3.5': {} @@ -21657,11 +21654,11 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/xml2js@0.4.14': dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 '@types/yargs-parser@21.0.3': {} @@ -21733,7 +21730,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/expect@4.1.8': dependencies: @@ -21744,15 +21741,25 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.8(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.14.6(@types/node@24.13.1)(typescript@6.0.3) + msw: 2.14.6(@types/node@24.13.2)(typescript@6.0.3) vite: 8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + '@vitest/mocker@4.1.8(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.14.6(@types/node@24.13.2)(typescript@6.0.3) + vite: 8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + optional: true + '@vitest/pretty-format@4.1.8': dependencies: tinyrainbow: 3.1.0 @@ -21780,7 +21787,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/utils@4.1.8': dependencies: @@ -22330,10 +22337,10 @@ snapshots: before-after-hook@4.0.0: {} - better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)): + better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)): dependencies: '@better-auth/core': 1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0) - '@better-auth/drizzle-adapter': 1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1)) + '@better-auth/drizzle-adapter': 1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1)) '@better-auth/kysely-adapter': 1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(kysely@0.29.2) '@better-auth/memory-adapter': 1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1) '@better-auth/mongo-adapter': 1.6.15(@better-auth/core@1.6.15(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.1) @@ -22352,13 +22359,13 @@ snapshots: optionalDependencies: better-sqlite3: 12.10.0 drizzle-kit: 0.31.10 - drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1) - mysql2: 3.22.5(@types/node@24.13.1) + drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1) + mysql2: 3.22.5(@types/node@24.13.2) next: 16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0) pg: 8.21.0 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.2)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) vue: 3.5.35(typescript@6.0.3) transitivePeerDependencies: - '@cloudflare/workers-types' @@ -23568,6 +23575,19 @@ snapshots: pg: 8.21.0 sql.js: 1.14.1 + drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1): + optionalDependencies: + '@libsql/client-wasm': 0.14.0 + '@opentelemetry/api': 1.9.0 + '@types/better-sqlite3': 7.6.13 + '@types/pg': 8.20.0 + better-sqlite3: 12.10.0 + gel: 2.0.0 + kysely: 0.29.2 + mysql2: 3.22.5(@types/node@24.13.2) + pg: 8.21.0 + sql.js: 1.14.1 + drizzle-zod@0.8.3(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(zod@4.4.3): dependencies: drizzle-orm: 0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1) @@ -23843,7 +23863,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 24.12.4 + '@types/node': 24.13.1 require-like: 0.1.2 event-target-shim@5.0.1: {} @@ -26218,9 +26238,9 @@ snapshots: ms@2.1.3: {} - msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3): + msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3): dependencies: - '@inquirer/confirm': 6.0.13(@types/node@24.13.1) + '@inquirer/confirm': 6.0.13(@types/node@24.13.2) '@mswjs/interceptors': 0.41.9 '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -26271,6 +26291,19 @@ snapshots: named-placeholders: 1.1.6 sql-escaper: 1.3.3 + mysql2@3.22.5(@types/node@24.13.2): + dependencies: + '@types/node': 24.13.2 + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.2 + long: 5.3.2 + lru.min: 1.1.4 + named-placeholders: 1.1.6 + sql-escaper: 1.3.3 + optional: true + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -27532,7 +27565,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 24.12.4 + '@types/node': 24.13.1 long: 5.3.2 proxmox-api@1.1.1: @@ -29105,22 +29138,22 @@ snapshots: trough@2.2.0: {} - trpc-to-mcp@1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3): + trpc-to-mcp@1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3): dependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) '@trpc/server': 11.17.0(typescript@6.0.3) - better-auth: 1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)) + better-auth: 1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)) mcp-handler: 1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)) zod: 4.4.3 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - trpc-to-mcp@1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.26.0(zod@4.4.3))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3): + trpc-to-mcp@1.3.2(@trpc/server@11.17.0(typescript@6.0.3))(better-auth@1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)))(mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.26.0(zod@4.4.3))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)))(zod@4.4.3): dependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) '@trpc/server': 11.17.0(typescript@6.0.3) - better-auth: 1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.1))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.1))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)) + better-auth: 1.6.15(@opentelemetry/api@1.9.0)(better-sqlite3@12.10.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.20.0)(better-sqlite3@12.10.0)(gel@2.0.0)(kysely@0.29.2)(mysql2@3.22.5(@types/node@24.13.2))(pg@8.21.0)(sql.js@1.14.1))(mysql2@3.22.5(@types/node@24.13.2))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0))(pg@8.21.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vitest@4.1.8)(vue@3.5.35(typescript@6.0.3)) mcp-handler: 1.1.0(@modelcontextprotocol/sdk@1.26.0(zod@4.4.3))(next@16.2.9(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)) zod: 4.4.3 transitivePeerDependencies: @@ -29237,8 +29270,6 @@ snapshots: undici-types@5.26.5: {} - undici-types@7.16.0: {} - undici-types@7.18.2: {} undici@6.23.0: {} @@ -29539,10 +29570,29 @@ snapshots: tsx: 4.21.0 yaml: 2.9.0 - vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.2 + esbuild: 0.28.0 + fsevents: 2.3.3 + jiti: 2.7.0 + sass: 1.100.0 + sugarss: 5.0.0(postcss@8.5.15) + terser: 5.46.1 + tsx: 4.21.0 + yaml: 2.9.0 + optional: true + + vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(msw@2.14.6(@types/node@24.13.1)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.1)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -29570,6 +29620,38 @@ snapshots: transitivePeerDependencies: - msw + vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.2)(@vitest/coverage-v8@4.1.8)(@vitest/ui@4.1.8)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(msw@2.14.6(@types/node@24.13.2)(typescript@6.0.3))(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 8.0.16(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.100.0)(sugarss@5.0.0(postcss@8.5.15))(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 24.13.2 + '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) + '@vitest/ui': 4.1.8(vitest@4.1.8) + jsdom: 29.1.1(@noble/hashes@2.0.1) + transitivePeerDependencies: + - msw + optional: true + vue-component-type-helpers@3.3.4: {} vue-demi@0.14.10(vue@3.5.35(typescript@6.0.3)): diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c88c12eaf..cf5acf02f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -82,7 +82,7 @@ catalog: "@types/crypto-js": ^4.2.2 "@types/css-modules": ^1.0.5 "@types/dockerode": ^4.0.1 - "@types/node": ^24.13.1 + "@types/node": ^24.13.2 "@types/node-unifi": ^2.5.1 "@types/pg": ^8.20.0 "@types/prismjs": ^1.26.6 From d6ce320eb2813aeb842e1dcd73519643c2ff3e8b Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:24:22 +0200 Subject: [PATCH 09/17] chore(deps): update turbo monorepo to ^2.9.18 (#5971) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 4 ++-- pnpm-workspace.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e78b09371..f8ac8d863 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,7 +217,7 @@ catalogs: specifier: ^11.17.0 version: 11.17.0 '@turbo/gen': - specifier: ^2.9.17 + specifier: ^2.9.18 version: 2.9.18 '@types/adm-zip': specifier: 0.5.8 @@ -514,7 +514,7 @@ catalogs: specifier: 4.20.4 version: 4.20.4 turbo: - specifier: ^2.9.17 + specifier: ^2.9.18 version: 2.9.18 typescript: specifier: ^6.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index cf5acf02f..544da9089 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -73,7 +73,7 @@ catalog: "@trpc/react-query": ^11.17.0 "@trpc/server": ^11.17.0 "@trpc/tanstack-react-query": ^11.17.0 - "@turbo/gen": ^2.9.17 + "@turbo/gen": ^2.9.18 "@types/adm-zip": 0.5.8 "@types/bcrypt": 6.0.0 "@types/better-sqlite3": 7.6.13 @@ -174,7 +174,7 @@ catalog: trpc-to-openapi: ^3.3.0 tsdav: ^2.2.2 tsx: 4.20.4 - turbo: ^2.9.17 + turbo: ^2.9.18 typescript: ^6.0.3 vite: ^8.0.16 undici: 7.27.2 From 176222f56b01b52886c5e9ddfa2e874fb20c609d Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:25:28 +0200 Subject: [PATCH 10/17] chore(deps): update dependency form-data@>=4.0.0 <4.0.4 to >=4.0.6 [security] (#5969) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 32 ++++++++++++++++++-------------- pnpm-workspace.yaml | 4 +++- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8ac8d863..6311919a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -566,7 +566,7 @@ overrides: brace-expansion@>=2.0.0 <=2.0.1: '>=5.0.6' brace-expansion@>=1.0.0 <=1.1.11: '>=5.0.6' esbuild@<=0.24.2: '>=0.28.0' - form-data@>=4.0.0 <4.0.4: '>=4.0.5' + form-data@>=4.0.0 <4.0.4: '>=4.0.6' hono@<4.6.5: '>=4.12.25' linkifyjs@<4.3.2: '>=4.3.3' nanoid@>=4.0.0 <5.0.9: '>=5.1.11' @@ -10191,6 +10191,10 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -10423,10 +10427,6 @@ packages: resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - hasown@2.0.4: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} @@ -18919,7 +18919,7 @@ snapshots: '@types/node': 24.13.1 '@types/node-fetch': 2.6.13 '@types/stream-buffers': 3.0.7 - form-data: 4.0.5 + form-data: 4.0.6 hpagent: 1.2.0 isomorphic-ws: 5.0.0(ws@8.20.1) js-yaml: 4.1.1 @@ -22231,7 +22231,7 @@ snapshots: axios@1.9.0: dependencies: follow-redirects: 1.16.0 - form-data: 4.0.5 + form-data: 4.0.6 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -23684,7 +23684,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 es-toolkit@1.46.1: {} @@ -24212,7 +24212,15 @@ snapshots: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 + mime-types: 2.1.35 + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 mime-types: 2.1.35 format@0.2.2: {} @@ -24281,7 +24289,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-nonce@1.0.1: {} @@ -24457,10 +24465,6 @@ snapshots: has-yarn@3.0.0: {} - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - hasown@2.0.4: dependencies: function-bind: 1.1.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 544da9089..1129b10a2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -200,6 +200,8 @@ minimumReleaseAgeExclude: - "@next/*" # Renovate security update: turbo@2.9.14 - turbo@2.9.14 + # Renovate security update: form-data@4.0.6 + - form-data@4.0.6 overrides: "@babel/helpers@<7.26.10": ">=7.29.7" "@babel/runtime@<7.26.10": ">=7.29.7" @@ -207,7 +209,7 @@ overrides: "brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.6" "brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.6" "esbuild@<=0.24.2": ">=0.28.0" - "form-data@>=4.0.0 <4.0.4": ">=4.0.5" + "form-data@>=4.0.0 <4.0.4": ">=4.0.6" "hono@<4.6.5": ">=4.12.25" "linkifyjs@<4.3.2": ">=4.3.3" "nanoid@>=4.0.0 <5.0.9": ">=5.1.11" From 1e07ce5c96813024b073f08ace8711def11fa6d6 Mon Sep 17 00:00:00 2001 From: "homarr-renovate[bot]" <158783068+homarr-renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:26:10 +0200 Subject: [PATCH 11/17] chore(deps): update mantine monorepo to ^9.3.1 (#5956) Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 273 ++++++++++++++++++++++---------------------- pnpm-workspace.yaml | 22 ++-- 2 files changed, 145 insertions(+), 150 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6311919a0..7dd9be2c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,38 +61,38 @@ catalogs: specifier: ^1.4.0 version: 1.4.0 '@mantine/charts': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/colors-generator': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/core': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/dates': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/dropzone': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/form': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/hooks': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/modals': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/notifications': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/spotlight': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@mantine/tiptap': - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.3.1 + version: 9.3.1 '@modelcontextprotocol/sdk': specifier: 1.29.0 version: 1.29.0 @@ -796,7 +796,7 @@ importers: version: 3.2.2(react@19.2.7) '@gfazioli/mantine-onboarding-tour': specifier: 'catalog:' - version: 4.0.4(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 4.0.4(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@homarr/analytics': specifier: workspace:^0.1.0 version: link:../../packages/analytics @@ -892,22 +892,22 @@ importers: version: link:../../packages/widgets '@mantine/colors-generator': specifier: 'catalog:' - version: 9.3.0(chroma-js@3.2.0) + version: 9.3.1(chroma-js@3.2.0) '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/dropzone': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/hooks': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) '@mantine/modals': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/tiptap': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tiptap/extension-link@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))(@tiptap/react@3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tiptap/extension-link@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))(@tiptap/react@3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@modelcontextprotocol/sdk': specifier: 'catalog:' version: 1.29.0(zod@4.4.3) @@ -994,10 +994,10 @@ importers: version: 1.17.0(react@19.2.7) mantine-datatable: specifier: 'catalog:' - version: 9.2.2(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(clsx@2.1.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.2.2(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(clsx@2.1.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) mantine-react-table: specifier: 'catalog:' - version: mantine-react-table-open@9.0.3(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: mantine-react-table-open@9.0.3(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) mcp-handler: specifier: 'catalog:' version: 1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(next@16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0)) @@ -1679,7 +1679,7 @@ importers: version: link:../validation '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@paralleldrive/cuid2': specifier: 'catalog:' version: 3.3.0 @@ -1793,7 +1793,7 @@ importers: version: link:../validation '@mantine/form': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) zod: specifier: 'catalog:' version: 4.4.3 @@ -1833,7 +1833,7 @@ importers: version: link:../validation '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: 'catalog:' version: 19.2.7 @@ -1999,10 +1999,10 @@ importers: version: link:../ui '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/hooks': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) react: specifier: 'catalog:' version: 19.2.7 @@ -2051,7 +2051,7 @@ importers: version: link:../validation '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tabler/icons-react': specifier: 'catalog:' version: 3.44.0(react@19.2.7) @@ -2085,7 +2085,7 @@ importers: version: link:../ui '@mantine/notifications': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tabler/icons-react': specifier: 'catalog:' version: 3.44.0(react@19.2.7) @@ -2134,10 +2134,10 @@ importers: version: link:../validation '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/hooks': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) adm-zip: specifier: 'catalog:' version: 0.5.17 @@ -2305,7 +2305,7 @@ importers: version: link:../server-settings '@mantine/dates': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next: specifier: 'catalog:' version: 16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0) @@ -2360,13 +2360,13 @@ importers: version: link:../ui '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/hooks': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) '@mantine/spotlight': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tabler/icons-react': specifier: 'catalog:' version: 3.44.0(react@19.2.7) @@ -2409,7 +2409,7 @@ importers: version: 4.3.1 mantine-react-table: specifier: 'catalog:' - version: mantine-react-table-open@9.0.3(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: mantine-react-table-open@9.0.3(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next: specifier: 'catalog:' version: 16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0) @@ -2449,13 +2449,13 @@ importers: version: link:../validation '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/dates': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/hooks': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) '@tabler/icons-react': specifier: 'catalog:' version: 3.44.0(react@19.2.7) @@ -2464,7 +2464,7 @@ importers: version: 4.2.0 mantine-react-table: specifier: 'catalog:' - version: mantine-react-table-open@9.0.3(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: mantine-react-table-open@9.0.3(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next: specifier: 'catalog:' version: 16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0) @@ -2586,16 +2586,16 @@ importers: version: link:../validation '@mantine/charts': specifier: 'catalog:' - version: 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(recharts@3.8.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1)) + version: 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(recharts@3.8.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1)) '@mantine/core': specifier: 'catalog:' - version: 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@mantine/form': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) '@mantine/hooks': specifier: 'catalog:' - version: 9.3.0(react@19.2.7) + version: 9.3.1(react@19.2.7) '@tabler/icons-react': specifier: 'catalog:' version: 3.44.0(react@19.2.7) @@ -2661,10 +2661,10 @@ importers: version: 1.17.0(react@19.2.7) mantine-datatable: specifier: 'catalog:' - version: 9.2.2(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(clsx@2.1.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.2.2(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(clsx@2.1.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) mantine-react-table: specifier: 'catalog:' - version: mantine-react-table-open@9.0.3(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: mantine-react-table-open@9.0.3(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next: specifier: 'catalog:' version: 16.2.9(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0) @@ -5460,88 +5460,88 @@ packages: '@libsql/core@0.14.0': resolution: {integrity: sha512-nhbuXf7GP3PSZgdCY2Ecj8vz187ptHlZQ0VRc751oB2C1W8jQUXKKklvt7t1LJiUTQBVJuadF628eUk+3cRi4Q==} - '@mantine/charts@9.3.0': - resolution: {integrity: sha512-ofqpHVEPbzebnLziFluWc0DF5QEPan3WdeAQxRyY0eZ5YZoulAwEq5nMhClQ+haEladVvfoovOuqS4jozWMFNw==} + '@mantine/charts@9.3.1': + resolution: {integrity: sha512-T09AnLs+yhjw97kqC2RHC58yj7YzaGRDRbANOONt7DPsRcSxHq8snQ9Qo4GrzxjcLUgXlN2eR1UfM4QHlY6y9A==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 react: ^19.2.0 react-dom: ^19.2.0 recharts: '>=3.2.1' - '@mantine/colors-generator@9.3.0': - resolution: {integrity: sha512-pm0jIqW4r0nq91pAZPf3ANNtPRY+V0Op0XZSqEwXGIv5FbNqQAb6Xqc6hJShXynanORlHTdtdwzveYZKDhFF9Q==} + '@mantine/colors-generator@9.3.1': + resolution: {integrity: sha512-BkY9JSAZq5J9yw/58WWqRHlt8pFJpu2MhN1bzILVTdu3sLVpYhJP0LnCvFb5InqYZQ6VLSui06B/Px4pObYRuw==} peerDependencies: chroma-js: '>=2.4.2' - '@mantine/core@9.3.0': - resolution: {integrity: sha512-mHVCm61YVW9ipy9eHiKMqsRUm3TkOErbdw7zHs0HRw5g403nf7tSTqNGvaYE+aX1Py874qMkrUzeQfj4bjiiBA==} + '@mantine/core@9.3.1': + resolution: {integrity: sha512-4rBoHpggSohayE+Os7lKdbsHTw7m/uZRKYjk9DN6cKBhQIjdiULdcG+b4b5CMVZSqZDHPgT70uWGI7yqkn8Ufw==} peerDependencies: - '@mantine/hooks': 9.3.0 + '@mantine/hooks': 9.3.1 react: ^19.2.0 react-dom: ^19.2.0 - '@mantine/dates@9.3.0': - resolution: {integrity: sha512-THGHMi32EqhyU46i6d5wRAWrWKJ21MnOO/eZ9u/SwkgZBDbkzduouLYSrBprAPhD68dflz15nGCfVZ0rYSgT0w==} + '@mantine/dates@9.3.1': + resolution: {integrity: sha512-Nh4XQyyV1AM0XRnUJugb1kNfCm++nkticsO8aroKmFPZ7cpGV7cjdL+4MJNPu1atS1EUePXl2VLLpKnrclpqOQ==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 dayjs: '>=1.0.0' react: ^19.2.0 react-dom: ^19.2.0 - '@mantine/dropzone@9.3.0': - resolution: {integrity: sha512-YnAO0+HGnHTSsQ44DJC5bWF4kcnuAiVUVottTjZJ1nsWnoagE+W5/XFWiFMtK0aep4btRFS8EHdoFai7YIFkvA==} + '@mantine/dropzone@9.3.1': + resolution: {integrity: sha512-WsjsMH9NUz5y9XAR/vOUbyb+y0X0Kud6KV1K/xURwrbxena2b1tl+WvjPO/6vRiJeTpFlMuoWF/8nwqnxoGUeg==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 react: ^19.2.0 react-dom: ^19.2.0 - '@mantine/form@9.3.0': - resolution: {integrity: sha512-1jFQgz1XURfNw0v+/h+T4d05+4gxrRk9C+EgrNNz+7/2Hg7y7WQRXrQxcYvxmp8/k0bv659i2ZyJYj3neA8qjg==} + '@mantine/form@9.3.1': + resolution: {integrity: sha512-dGJPcYGkfDnAPKHcnzgf2mSlnjwymaOyROiTEJNbbjE3OVoWbPaw8HwEKav1wATdxNYR9Bw1GHtbfbGTsm1Jww==} peerDependencies: react: ^19.2.0 - '@mantine/hooks@9.3.0': - resolution: {integrity: sha512-QoSr9WI4WsKWrM3qFYYizHUn3+n+CVcFMYe4sdlnmFPStvs6BacPODKJSbFlYl73Z20t82JIy0eKqt4noHQI2g==} + '@mantine/hooks@9.3.1': + resolution: {integrity: sha512-zAOlxV59j5CDgAnExN+ypaR6dVW1vwMKDvKUxIlUVd/e52qxYnPyYRD+shJmyOLaZRuQmVF0R/7mJAjt2jw9cA==} peerDependencies: react: ^19.2.0 - '@mantine/modals@9.3.0': - resolution: {integrity: sha512-Hnj7Mm89VW4kTnHrzaB3UARZg3qgaRr0jvSqSvwJMdgpY6WykyqLKeMyFE+7YQNq44gQ46yV3Q3VOyZsHchaew==} + '@mantine/modals@9.3.1': + resolution: {integrity: sha512-JqP1o6hrfQJ5UCq97HJsI7uIPDy+WwvWwkkfctWqcYkFJA2DU8dVoPqvEhnhDQUIXNci3sx6XDoQZAq1Qg57mw==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 react: ^19.2.0 react-dom: ^19.2.0 - '@mantine/notifications@9.3.0': - resolution: {integrity: sha512-ikkgAVGccbyU/WLru6b2cZsVqky8iEvduCPAPBnSPd5UQBEjcZr11F0LFUGAHwBM94InDzdf4HQiSBMG2lXRRw==} + '@mantine/notifications@9.3.1': + resolution: {integrity: sha512-irkD9uszgVftthbCLd+4lS7M1jkhL4sVrZASv8+43aIkncW+oQkCFpBuaOAf6uRcMjmQ7XnKs2O8bXYzl/DqgQ==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 react: ^19.2.0 react-dom: ^19.2.0 - '@mantine/spotlight@9.3.0': - resolution: {integrity: sha512-l8TRLjSLwilTQwzvZ8AS2L0Xwj1lTIjynXiegbZOKiUk0ga5yYhj5w77jsxGX9sy+LTYmkqYwRGhnSUujKB6Jg==} + '@mantine/spotlight@9.3.1': + resolution: {integrity: sha512-NsNXywYhKuHnESoMCX1+e2za0GQJYQ43zMUIkr8XwHiyIzdU/tmFJh+k+jfUeFKPfnS5uRy+vJvC6z320aNbTw==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 react: ^19.2.0 react-dom: ^19.2.0 - '@mantine/store@9.3.0': - resolution: {integrity: sha512-hTSXBRCPiQeVcoYRTAOtlSPFHcD6Iexf1Sbhjk/Ne5k+LUdeYAaDOZHdyljepO0+8HVavR31ZvkNCQXuoV793w==} + '@mantine/store@9.3.1': + resolution: {integrity: sha512-UZ8wGKfF/NFMHCDl1Rd0M8XtHdt4W1w3V4ohNpFSaP3nSjJ+Cu1FT+iIWtXwC0Ogha8XnuVQ7vm/dw2BUfpw+g==} peerDependencies: react: ^19.2.0 - '@mantine/tiptap@9.3.0': - resolution: {integrity: sha512-NO7fe901b0eF6UoxGy6qhMyZMdcV2d548HLnA4FM/UD0WY4VIThslo5FoxsUQc6JhUbNczAvgibPd4PGjaczbw==} + '@mantine/tiptap@9.3.1': + resolution: {integrity: sha512-trXZ6mN6p3ukxudOuC8r+PapA7UVQ/az4cSOmiE3m8n1s3IcoyaiwAB5l2iM/Df9lByhs1B6ka8SG1F6Wda2NQ==} peerDependencies: - '@mantine/core': 9.3.0 - '@mantine/hooks': 9.3.0 + '@mantine/core': 9.3.1 + '@mantine/hooks': 9.3.1 '@tiptap/extension-link': '>=3.3.0' '@tiptap/react': '>=3.3.0' react: ^19.2.0 @@ -14183,9 +14183,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - tabbable@6.4.0: resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} @@ -18318,7 +18315,7 @@ snapshots: '@floating-ui/utils': 0.2.11 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - tabbable: 6.2.0 + tabbable: 6.4.0 '@floating-ui/utils@0.2.10': {} @@ -18374,10 +18371,10 @@ snapshots: '@formatjs/fast-memoize': 3.1.0 tslib: 2.8.1 - '@gfazioli/mantine-onboarding-tour@4.0.4(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@gfazioli/mantine-onboarding-tour@4.0.4(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) '@tabler/icons-react': 3.44.0(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -19003,22 +19000,22 @@ snapshots: js-base64: 3.7.8 optional: true - '@mantine/charts@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(recharts@3.8.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1))': + '@mantine/charts@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(recharts@3.8.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1))': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) recharts: 3.8.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1) - '@mantine/colors-generator@9.3.0(chroma-js@3.2.0)': + '@mantine/colors-generator@9.3.1(chroma-js@3.2.0)': dependencies: chroma-js: 3.2.0 - '@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@floating-ui/react': 0.27.19(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) clsx: 2.1.1 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -19028,66 +19025,66 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@mantine/dates@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/dates@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) clsx: 2.1.1 dayjs: 1.11.21 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@mantine/dropzone@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/dropzone@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-dropzone: 15.0.0(react@19.2.7) - '@mantine/form@9.3.0(react@19.2.7)': + '@mantine/form@9.3.1(react@19.2.7)': dependencies: '@standard-schema/spec': 1.1.0 fast-deep-equal: 3.1.3 klona: 2.0.6 react: 19.2.7 - '@mantine/hooks@9.3.0(react@19.2.7)': + '@mantine/hooks@9.3.1(react@19.2.7)': dependencies: react: 19.2.7 - '@mantine/modals@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/modals@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@mantine/notifications@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/notifications@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) - '@mantine/store': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) + '@mantine/store': 9.3.1(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-transition-group: 4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/spotlight@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/spotlight@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) - '@mantine/store': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) + '@mantine/store': 9.3.1(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@mantine/store@9.3.0(react@19.2.7)': + '@mantine/store@9.3.1(react@19.2.7)': dependencies: react: 19.2.7 - '@mantine/tiptap@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tiptap/extension-link@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))(@tiptap/react@3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@mantine/tiptap@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tiptap/extension-link@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))(@tiptap/react@3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) '@tiptap/extension-link': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0) '@tiptap/react': 3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 @@ -25539,19 +25536,19 @@ snapshots: dependencies: semver: 7.7.4 - mantine-datatable@9.2.2(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(clsx@2.1.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + mantine-datatable@9.2.2(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(clsx@2.1.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) clsx: 2.1.1 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - mantine-react-table-open@9.0.3(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + mantine-react-table-open@9.0.3(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@mantine/core': 9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/dates': 9.3.0(@mantine/core@9.3.0(@mantine/hooks@9.3.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mantine/hooks': 9.3.0(react@19.2.7) + '@mantine/core': 9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/dates': 9.3.1(@mantine/core@9.3.1(@mantine/hooks@9.3.1(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.3.1(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@mantine/hooks': 9.3.1(react@19.2.7) '@tabler/icons-react': 3.44.0(react@19.2.7) '@tanstack/match-sorter-utils': 8.19.4 '@tanstack/react-table': 8.20.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -28908,8 +28905,6 @@ snapshots: symbol-tree@3.2.4: {} - tabbable@6.2.0: {} - tabbable@6.4.0: {} tagged-tag@1.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1129b10a2..a20d71b32 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -22,17 +22,17 @@ catalog: "@jellyfin/sdk": ^0.13.0 "@kubernetes/client-node": ^1.4.0 "@modelcontextprotocol/sdk": 1.29.0 - "@mantine/charts": ^9.3.0 - "@mantine/colors-generator": ^9.3.0 - "@mantine/core": ^9.3.0 - "@mantine/dates": ^9.3.0 - "@mantine/dropzone": ^9.3.0 - "@mantine/form": ^9.3.0 - "@mantine/hooks": ^9.3.0 - "@mantine/modals": ^9.3.0 - "@mantine/notifications": ^9.3.0 - "@mantine/spotlight": ^9.3.0 - "@mantine/tiptap": ^9.3.0 + "@mantine/charts": ^9.3.1 + "@mantine/colors-generator": ^9.3.1 + "@mantine/core": ^9.3.1 + "@mantine/dates": ^9.3.1 + "@mantine/dropzone": ^9.3.1 + "@mantine/form": ^9.3.1 + "@mantine/hooks": ^9.3.1 + "@mantine/modals": ^9.3.1 + "@mantine/notifications": ^9.3.1 + "@mantine/spotlight": ^9.3.1 + "@mantine/tiptap": ^9.3.1 "@next/eslint-plugin-next": 16.2.9 "@octokit/auth-app": ^8.2.0 "@paralleldrive/cuid2": ^3.3.0 From a4f0eda4a0bf0bec35401b6e3bda2174f532fd07 Mon Sep 17 00:00:00 2001 From: "homarr-crowdin[bot]" <190541745+homarr-crowdin[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:27:23 +0200 Subject: [PATCH 12/17] chore(lang): updated translations from crowdin (#5965) Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com> --- packages/translation/src/lang/de.json | 512 +++++++++++++------------- packages/translation/src/lang/uk.json | 100 ++--- 2 files changed, 306 insertions(+), 306 deletions(-) diff --git a/packages/translation/src/lang/de.json b/packages/translation/src/lang/de.json index ddfb61227..06e5c52a1 100644 --- a/packages/translation/src/lang/de.json +++ b/packages/translation/src/lang/de.json @@ -856,37 +856,37 @@ "title": "Vorschau", "test": "Test", "hint": "Klicken Sie auf Test, um eine Vorschau der API Antwort mit Ihrer aktuellen Konfiguration zu erhalten.", - "rawResponse": "" + "rawResponse": "Rohe JSON-Antwort" }, "notification": { - "created": "", - "updated": "", - "deleted": "", - "imported": "", - "aiPromptCopied": "", - "aiPromptDescription": "", - "aiPromptCopy": "", - "aiPromptNoResponse": "", - "aiPromptCopyError": "", - "createError": "", - "updateError": "", - "importError": "", - "exportError": "", - "duplicated": "", - "duplicateError": "", - "toggleError": "", - "deleteError": "", - "previewError": "", - "executed": "", - "executeError": "" + "created": "Widget \"{name}\" erfolgreich erstellt.", + "updated": "Widget \"{name}\" erfolgreich aktualisiert.", + "deleted": "Widget \"{name}\" gelöscht.", + "imported": "Widget erfolgreich importiert.", + "aiPromptCopied": "KI-Prompt in Zwischenablage kopiert", + "aiPromptDescription": "Kopieren Sie eine vorgefertigte Eingabeaufforderung, um sie in jeden AI-Chatbot einzufügen (ChatGPT, Claude, Gemini...). Es enthält das komplette Widget-Schema und Ihre API-Antwort, so dass die KI eine fertige JSON-Konfiguration für Sie generieren kann.", + "aiPromptCopy": "In die Zwischenablage kopieren", + "aiPromptNoResponse": "Tipp: Führen Sie zuerst einen Test aus, um Ihre API Antwort in die Eingabeaufforderung einzubinden, um bessere Ergebnisse zu erzielen.", + "aiPromptCopyError": "Fehler beim Kopieren der Eingabeaufforderung. Möglicherweise unterstützt Ihr Browser diese Funktion nicht.", + "createError": "Fehler beim Erstellen des Widgets. Überprüfen Sie, ob Ihr API Endpunkt erreichbar ist.", + "updateError": "Fehler beim Aktualisieren des Widgets. Ihre Änderungen wurden nicht gespeichert.", + "importError": "Fehler beim Importieren des Widgets. Stellen Sie sicher, dass die Datei ein gültiger JSON Export ist.", + "exportError": "Fehler beim Exportieren des Widgets.", + "duplicated": "Widget \"{name}\" dupliziert.", + "duplicateError": "Fehler beim Duplizieren des Widgets.", + "toggleError": "Fehler beim Umschalten des Widgets Status.", + "deleteError": "Fehler beim Löschen des Widgets.", + "previewError": "Fehler beim Abrufen der Vorschau. Überprüfen Sie die URL und versuchen Sie es erneut.", + "executed": "Aktion erfolgreich ausgeführt.", + "executeError": "Aktion fehlgeschlagen: {error}" }, "table": { "name": "Name", "url": "URL", - "display": "" + "display": "Anzeige" }, "widget": { - "disabled": "" + "disabled": "Dieses Widget ist deaktiviert" } }, "integration": { @@ -983,20 +983,20 @@ "placeholder": "Benutzerdefinierte App URL" }, "apiKeySettings": { - "label": "" + "label": "API-Schlüssel holen" } }, "action": { "create": "Neue Integration" }, "grid": { - "widgetCount": "", - "noWidgets": "", - "detected": "" + "widgetCount": "{count} Widgets", + "noWidgets": "Keine Widgets", + "detected": "Über Docker erkannt" }, "category": { "dnsHole": "DNS", - "mediaService": "", + "mediaService": "Medien", "calendar": "Kalender", "mediaSearch": "Suche", "mediaRelease": "Veröffentlichungen", @@ -1014,15 +1014,15 @@ "releasesProvider": "Veröffentlichungen", "notifications": "Benachrichtigungen", "firewall": "Firewall", - "timetable": "", + "timetable": "Zeitplan", "photoService": "Fotos", - "documents": "", - "notes": "", - "mediaMonitoring": "", - "speedtest": "", - "analytics": "", - "mediaLibrary": "", - "uptimeMonitoring": "" + "documents": "Dokumente", + "notes": "Notizen", + "mediaMonitoring": "Überwachung", + "speedtest": "Geschwindigkeitstest", + "analytics": "Nutzungsdaten", + "mediaLibrary": "Medienbibliothek", + "uptimeMonitoring": "Überwachung der Betriebszeit" }, "testConnection": { "action": { @@ -1297,8 +1297,8 @@ "newLabel": "Neuer privater Schlüssel" }, "slug": { - "label": "", - "newLabel": "" + "label": "Status Page Slug", + "newLabel": "Neuer Status Page Slug" } } }, @@ -1308,28 +1308,28 @@ "full": "Voller Zugriff auf Integrationen" }, "audiobookshelf": { - "name": "", - "description": "" + "name": "Hörbuchregal", + "description": "Selbstgehosteter Audio- und Podcast-Server" }, "navidrome": { - "name": "", - "description": "" + "name": "Navidrome", + "description": "Selbstgehosteter Musik-Streaming-Server mit Subsonic API Kompatibilität" }, "uptimeKuma": { - "name": "", - "description": "", + "name": "Uptime Kuma", + "description": "Selbstgehostete Uptime Überwachung mit öffentlichen Statusseiten", "secret": { "slug": { - "description": "" + "description": "Der Slug der öffentlichen Statusseite. Standardwert ist \"Standard\", wenn nicht anderswertig gesetzt." }, "apiKey": { - "description": "" + "description": "Optionaler API Schlüssel für Authentifizeriungs /Metriken Zugriff" } } }, "paperlessNgx": { - "name": "", - "description": "" + "name": "Paperless-ngx", + "description": "Dokumentenverwaltungssystem zum Archivieren und Organisieren gescannter Dokumente" } }, "media": { @@ -1373,7 +1373,7 @@ } }, "noResults": { - "title": "" + "title": "Keine Medien hochgeladen" } }, "common": { @@ -1384,7 +1384,7 @@ "add": "Hinzufügen", "apply": "Übernehmen", "backToOverview": "Zurück zur Übersicht", - "skip": "", + "skip": "Überspringen", "change": "Verändern", "create": "Erstellen", "createAnother": "Erstellen und neu starten", @@ -1625,7 +1625,7 @@ "app": "App bearbeiten" }, "app": { - "propagationNotice": "" + "propagationNotice": "Änderungen an dieser App werden überall dort übernommen, wo sie über alle Dashboards hinweg verwendet wird." }, "advancedOptions": { "label": "Erweiterte Einstellungen", @@ -1731,7 +1731,7 @@ "label": "In neuem Tab öffnen" }, "withBorder": { - "label": "" + "label": "Rahmen anzeigen" }, "items": { "label": "Lesezeichen", @@ -1874,13 +1874,13 @@ } }, "timetable": { - "name": "", - "description": "", - "title": "", - "noStation": "", + "name": "Zeitplan", + "description": "Zeigt Abfahrtszeiten für eine Station an", + "title": "{station} Abfahrt", + "noStation": "Keine Station ausgewählt", "option": { "station": { - "label": "" + "label": "Station" } } }, @@ -1955,32 +1955,32 @@ } }, "anchorNote": { - "name": "", - "description": "", + "name": "Anchor Notiz", + "description": "Zeigt eine ausgewählte Notiz von Anchor an", "option": { "noteId": { - "label": "" + "label": "Notiz" }, "showTitle": { - "label": "" + "label": "Titel anzeigen" }, "showUpdatedAt": { - "label": "" + "label": "Letzte Aktualisierungen anzeigen" } }, - "empty": "", - "emptyContent": "", - "notFound": "", - "noNotes": "", - "integrationRequired": "", - "updatedAt": "", - "untitled": "", - "edit": "", - "save": "", - "cancel": "", - "readOnlyViewer": "", - "saveForbidden": "", - "saveFailed": "" + "empty": "Notiz zum Anzeigen auswählen", + "emptyContent": "Diese Notiz hat keinen Inhalt", + "notFound": "Notiz wurde nicht gefunden", + "noNotes": "Keine Notizen gefunden", + "integrationRequired": "Wählen Sie zuerst eine Anchor Integration aus", + "updatedAt": "Aktualisiert {date}", + "untitled": "Unbenannt", + "edit": "Bearbeiten", + "save": "Speichern", + "cancel": "Abbrechen", + "readOnlyViewer": "Sie haben Leserechte auf diese Notiz. Die Bearbeitung ist deaktiviert.", + "saveForbidden": "Du hast keine Berechtigung diese Notiz zu bearbeiten.", + "saveFailed": "Fehler beim Speichern dieser Notiz. Bitte versuchen Sie es erneut." }, "iframe": { "name": "iFrame", @@ -2014,7 +2014,7 @@ "label": "Geolokalisierung zulassen" }, "allowModals": { - "label": "" + "label": "Modale erlauben" } }, "error": { @@ -2249,7 +2249,7 @@ "label": "Speicher-Info anzeigen" }, "gpu": { - "label": "" + "label": "GPU Info anzeigen" }, "showUptime": { "label": "Laufzeit anzeigen" @@ -2390,187 +2390,187 @@ } }, "ups": { - "name": "", - "description": "", + "name": "UPS", + "description": "Überwachen Sie den Status Ihrer UPS Geräte über einen NUT Server", "option": { "showBattery": { - "label": "" + "label": "Batterie anzeigen" }, "showLoad": { - "label": "" + "label": "Laden anzeigen" }, "showVoltage": { - "label": "" + "label": "Spannung anzeigen" } }, "status": { - "online": "", - "onBattery": "", - "lowBattery": "", - "charging": "", - "unknown": "" + "online": "Online", + "onBattery": "Im Akkubetrieb", + "lowBattery": "Niedriger Akkustand", + "charging": "Lade", + "unknown": "Unbekannt" }, "field": { - "runtime": "", - "load": "", - "input": "", - "output": "" + "runtime": "Laufzeit", + "load": "Laden", + "input": "Eingabe", + "output": "Ausgabe" }, "error": { - "internalServerError": "" + "internalServerError": "Fehler beim Abrufen der UPS Daten" } }, "tracearr": { - "name": "", - "description": "", + "name": "Tracearr", + "description": "Verbesserte Überwachung von Medienservern mit detaillierten Stream- und Transcodierungsdaten", "option": { "showStreams": { "label": "Aktive Streams anzeigen" }, "showStats": { - "label": "" + "label": "Statistikübersicht anzeigen" }, "showRecentActivity": { - "label": "" + "label": "Letzte Aktivitäten anzeigen" }, "showViolations": { - "label": "" + "label": "Verstöße anzeigen" } }, "stats": { "activeStreams": "Aktive Streams", - "totalUsers": "", - "transcodes": "", - "bandwidth": "" + "totalUsers": "Anzahl der Benutzer", + "transcodes": "Transkodierungen", + "bandwidth": "Bandbreite" }, "streams": { - "empty": "", - "user": "", - "media": "", - "state": "", - "quality": "", - "device": "", - "progress": "", + "empty": "Keine aktiven Streams", + "user": "Benutzer", + "media": "Medien", + "state": "Status", + "quality": "Qualität", + "device": "Gerät", + "progress": "Fortschritt", "videoDecision": { - "directPlay": "", - "transcode": "", - "directStream": "" + "directPlay": "Direkte Wiedergabe", + "transcode": "Transkodieren", + "directStream": "Direkter Stream" } }, "recentActivity": { - "title": "", - "empty": "", - "watched": "", - "partial": "" + "title": "Kürzliche Aktivitäten", + "empty": "Keine kürzliche Aktivitäten", + "watched": "Gesehen", + "partial": "Teilweise" }, "violations": { - "title": "", - "empty": "", - "rule": "", - "user": "" + "title": "Verstöße", + "empty": "Keine kürzlichen Verstöße", + "rule": "Regel", + "user": "Benutzer" }, - "noSectionsEnabled": "", + "noSectionsEnabled": "Keine Abschnitte aktiviert", "error": { - "noIntegration": "", - "internalServerError": "" + "noIntegration": "Keine Tracearr Integration konfiguriert", + "internalServerError": "Fehler beim Abrufen der Tracearr Daten" } }, "umami": { - "name": "", - "description": "", - "visitors": "", - "active": "", - "pageviews": "", - "visits": "", - "bounceRate": "", - "avgDuration": "", + "name": "Umami Ergebnisse", + "description": "Besucherstatistiken aus Ihrer Umami Ergebnisinstanz anzeigen", + "visitors": "Besucher", + "active": "Aktiv", + "pageviews": "Seitenaufrufe", + "visits": "Besuche", + "bounceRate": "abprallen", + "avgDuration": "Durchschnitt", "option": { "websiteId": { - "label": "", - "description": "", - "placeholder": "", - "loading": "", - "noWebsites": "", - "configureIntegrationFirst": "" + "label": "Webseite", + "description": "Wählen Sie eine Website, die von Ihrer Umami Instanz verfolgt wird", + "placeholder": "Wähle eine Website", + "loading": "Website wird geladen...", + "noWebsites": "Keine Webseiten gefunden", + "configureIntegrationFirst": "Verbinde zuerst eine Umami Integration" }, "timeFrame": { - "label": "", - "description": "", + "label": "Zeitfenster", + "description": "Der Zeitraum, in dem Besucherdaten angezeigt werden sollen", "option": { - "today": "", - "24h": "", - "7d": "", - "30d": "", - "month": "", - "lastMonth": "" + "today": "Heute", + "24h": "Letzte 24 Stunden", + "7d": "Letzte 7 Tage", + "30d": "Letzte 30 Tage", + "month": "Dieser Monat", + "lastMonth": "Letzter Monat" } }, "eventName": { - "label": "", - "description": "", - "placeholder": "", - "loading": "", - "noEvents": "", - "none": "", - "configureWebsiteFirst": "" + "label": "Veranstaltung", + "description": "Gesamte Vorkommnisse eines bestimmten Ereignisses anzeigen", + "placeholder": "Wählen Sie ein Ereignis aus", + "loading": "Ereignisse werden geladen...", + "noEvents": "Keine Ereignisse gefunden", + "none": "Kein Ereignis", + "configureWebsiteFirst": "Wählen Sie zuerst eine Webseite aus, um Ereignisse zu laden" }, "chartType": { - "label": "", - "description": "", + "label": "Diagrammtyp", + "description": "Stil für das Diagramm anzeigen", "option": { - "bar": "", - "sparkline": "" + "bar": "Balkendiagramm", + "sparkline": "Sparkline" } }, "chartStyle": { - "label": "", - "description": "", + "label": "Diagrammstil", + "description": "Wie die Ereignisserie im Diagramm angezeigt wird", "option": { - "grouped": "", - "overlay": "" + "grouped": "Seite an Seite", + "overlay": "Overlay (subset)" } }, "viewMode": { - "label": "", - "description": "", + "label": "Ansicht", + "description": "Was im Widget Körper angezeigt werden soll", "option": { - "chart": "", - "events": "", - "topPages": "", - "topReferrers": "" + "chart": "Diagramm", + "events": "Ereignis Diagramm", + "topPages": "Top Seiten", + "topReferrers": "Top Referrer" } }, "eventNames": { - "label": "", - "description": "", - "placeholder": "", - "loading": "", - "noEvents": "", - "configureWebsiteFirst": "" + "label": "Ereignisse", + "description": "Wählen Sie eine oder mehrere Ereignisse zum Diagramm", + "placeholder": "Ereignisse auswählen...", + "loading": "Ereignisse werden geladen...", + "noEvents": "Keine Ereignisse gefunden", + "configureWebsiteFirst": "Wählen Sie zuerst eine Webseite aus, um Ereignisse zu laden" }, "topCount": { - "label": "", - "description": "" + "label": "Elemente anzeigen", + "description": "Anzahl der anzuzeigenden Top Elemente (max. 500)" } }, "event": { - "occurrences": "", - "label": "" + "occurrences": "Ereignisse", + "label": "Ereignisse" }, "topPages": { - "direct": "" + "direct": "Direkt" }, "topReferrers": { - "direct": "" + "direct": "Direkt" }, "events": { - "selectPrompt": "" + "selectPrompt": "Wählen Sie Ereignisse zum Diagramm" }, "error": { - "noIntegration": "", - "noWebsite": "", - "noData": "", - "internalServerError": "" + "noIntegration": "Keine Umami Integration konfiguriert", + "noWebsite": "Keine Webseite konfiguriert", + "noData": "Keine Daten verfügbar", + "internalServerError": "Fehler beim Abrufen der Umami-Daten" } }, "common": { @@ -2669,95 +2669,95 @@ } }, "paperlessNgx": { - "name": "", - "description": "", - "documentsTotal": "", - "documentsInbox": "", - "correspondents": "", - "tags": "", - "documentTypes": "", - "inboxRatio": "", - "inbox": "", - "total": "", + "name": "Paperless-ngx", + "description": "Übersicht des Paperless-ngx Dokumentenarchivs", + "documentsTotal": "Dokumente insgesamt", + "documentsInbox": "Posteingang", + "correspondents": "Beteiligte", + "tags": "Tags", + "documentTypes": "Dokumententypen", + "inboxRatio": "Eingangs-Verhältnis", + "inbox": "Eingang", + "total": "insgesamt", "option": { "showInboxRatio": { - "label": "", - "description": "" + "label": "Zeige Eingangs Verhältnis", + "description": "Zeigt das Dokumentenverhältnis zwischen Eingang und Gesamtsumme im Heldenbereich an" }, "showInboxRing": { - "label": "", - "description": "" + "label": "Inbox Ring anzeigen", + "description": "Fortschrittsring neben dem Eingangs-Verhältnis anzeigen" }, "showDocumentsTotal": { - "label": "", - "description": "" + "label": "Gesamte Dokumente anzeigen", + "description": "Zeigt die Gesamtzahl der Dokumente im Archiv an" }, "showDocumentsInbox": { - "label": "", - "description": "" + "label": "Eingangszähler anzeigen", + "description": "Zeige die Anzahl der nicht verarbeiteten Dokumente im Posteingang" }, "showCorrespondents": { - "label": "", - "description": "" + "label": "Korrespondenten anzeigen", + "description": "Gesamtzahl der Korrespondenten anzeigen" }, "showTags": { - "label": "", - "description": "" + "label": "Tags anzeigen", + "description": "Gesamtzahl der Tags anzeigen" }, "showDocumentTypes": { - "label": "", - "description": "" + "label": "Dokumententypen anzeigen", + "description": "Gesamtzahl der Dokumenttypen anzeigen" } }, "error": { - "internalServerError": "" + "internalServerError": "Fehler beim Abrufen der Paperless-ngx-Daten" } }, "immich-serverStats": { - "name": "", - "description": "", + "name": "Immich Serverstatistik einblenden", + "description": "Informationen über Ihre Immich Instanz", "option": { "showUsers": { "label": "Benutzer anzeigen", - "description": "" + "description": "Anzahl der Benutzer Ihrer Instanz anzeigen" }, "showPhotos": { - "label": "", - "description": "" + "label": "Fotos anzeigen", + "description": "Anzahl der Fotos in deiner Instanz anzeigen" }, "showVideos": { - "label": "", - "description": "" + "label": "Videos anzeigen", + "description": "Anzahl der Videos in deiner Instanz anzeigen" }, "showStorage": { - "label": "", - "description": "" + "label": "Speicher anzeigen", + "description": "Gesamtanzahl des verwendeten Speichers anzeigen" } }, - "users": "", - "photos": "", - "videos": "", - "storage": "" + "users": "Benutzer", + "photos": "Fotos", + "videos": "Videos", + "storage": "Verwendeter Speicher" }, "immich-albumCarousel": { - "name": "", + "name": "Immich Album", "option": { "albumId": { - "label": "", - "description": "" + "label": "Album ID", + "description": "Dies ist eine eindeutige ID, die sich auf Ihr Immich Album bezieht. Sie können diese ID finden, indem Sie ein Album öffnen und die einzigartige Zeichenkette von der URL kopieren, zum Beispiel 'a21aca09-9f05-47dc-b1a1-7b53c547226e'" }, "rotationIntervalSeconds": { - "label": "", - "description": "" + "label": "Intervall in Sekunden", + "description": "Rotieren Sie alle X Sekunden zwischen den Bildern im Album. Muss mindestens 1 sein und unter 3600 liegen." }, "showPhotoInfo": { - "label": "", - "description": "" + "label": "Foto Infos anzeigen", + "description": "Datum des Bildes und des aktuellen Indexes am unteren Rand der Diashow anzeigen" } }, - "description": "", - "noAlbumSelected": "", - "noPhotos": "" + "description": "Diashow der Bilder in Ihrem Immich Album", + "noAlbumSelected": "Kein Album ausgewählt, Widget bearbeiten und Album auswählen", + "noPhotos": "Keine Fotos in diesem Album" }, "downloads": { "name": "Download Client", @@ -3068,7 +3068,7 @@ "label": "Beschreibung ausblenden" }, "showPosterImage": { - "label": "" + "label": "Poster Bild anzeigen" } } }, @@ -3283,46 +3283,46 @@ "option": {} }, "speedtestTracker": { - "name": "", - "description": "", + "name": "Speedtest Tracker", + "description": "Zeigt die Ergebnisse des Speedtest Trackers an.", "option": { "showLatestResult": { - "label": "" + "label": "Letztes Ergebnis anzeigen" }, "showStats": { - "label": "" + "label": "Durchschnitt anzeigen" }, "showRecentResults": { - "label": "" + "label": "Letzte Ergebnisse anzeigen" }, "showPingGraph": { - "label": "" + "label": "Ping Grafik anzeigen" } }, - "latestResult": "", - "averages": "", - "tests": "", - "recentResults": "", - "recentResultsSpeed": "", - "recentResultsPing": "", - "download": "", - "upload": "", - "ping": "", - "status": "", - "avgDownload": "", - "avgUpload": "", - "avgPing": "", - "healthy": "", - "unhealthy": "", - "ok": "", - "fail": "", - "noSectionsEnabled": "", + "latestResult": "Letztes Ergebnis", + "averages": "Durchschnitt", + "tests": "{count} Versuche", + "recentResults": "Neueste Ergebnisse", + "recentResultsSpeed": "Geschwindigkeit", + "recentResultsPing": "Ping", + "download": "Download", + "upload": "Upload", + "ping": "Ping", + "status": "Status", + "avgDownload": "Durchschnittlicher Download", + "avgUpload": "Durchschnittlicher Upload", + "avgPing": "Durchschnittlicher Ping", + "healthy": "Gesund", + "unhealthy": "Fehlerhaft", + "ok": "Ok", + "fail": "Fehlgeschlagen", + "noSectionsEnabled": "Aktivieren Sie mindestens einen Abschnitt in den Widget Optionen.", "error": { - "internalServerError": "" + "internalServerError": "Fehler beim Abrufen der Speedtest Daten" } }, "uptimeKuma": { - "name": "", + "name": "Uptime Kuma", "description": "", "option": { "showAverageUptime": { diff --git a/packages/translation/src/lang/uk.json b/packages/translation/src/lang/uk.json index e192469aa..592019dfe 100644 --- a/packages/translation/src/lang/uk.json +++ b/packages/translation/src/lang/uk.json @@ -8,9 +8,9 @@ "action": { "scratch": "Почати з нуля", "importOldmarr": "Імпорт з Homarr пре-1.0", - "restoreSqlite": "", + "restoreSqlite": "Відновлення з резервної копії SQLite", "restoreSqliteDescription": "Завантажте повну резервну копію бази даних SQLite з попереднього екземпляра Homarr. Це відновить усі дошки, користувачів, програми, інтеграції та налаштування.", - "backToOptions": "" + "backToOptions": "Назад до параметрів налаштування" } }, "import": { @@ -94,45 +94,45 @@ "subtitle": "Налаштування сервера." }, "integrations": { - "title": "", - "subtitle": "", - "description": "", + "title": "Інтеграції", + "subtitle": "Виберіть сервіси, якими ви користуєтеся.", + "description": "Виберіть інтеграції, які ви хочете під'єднати. Ми автоматично налаштуємо вашу інформаційну панель з відповідними віджетами.", "action": { - "continue": "", - "skip": "" + "continue": "Продовжити", + "skip": "Пропустити поки що" }, "configure": { - "progress": "", - "skip": "", - "finishing": "", + "progress": "{поточний} з {загальний}", + "skip": "Пропустити цю інтеграцію", + "finishing": "Налаштування панелі інструментів..", "error": { - "title": "", - "message": "" + "title": "Не вдалося налаштувати", + "message": "Під час налаштування вашої інформаційної панелі сталася помилка. Спробуйте ще раз." }, "helpers": { "tooltip": "Помічники URL-адрес", - "description": "" + "description": "Ці помічники намагаються вгадати URL-адресу ваших послуг. Якщо це не працює, просто введіть адресу вручну." } }, "urlTemplate": { - "label": "", - "placeholder": "", - "hint": "", - "warning": "", - "preview": "", + "label": "Базовий URL", + "placeholder": "homelab.local чи 192.168.1.1", + "hint": "Введіть свій базовий домен або IP-адресу для автоматичного заповнення URL-адрес інтеграції", + "warning": "Якщо встановити базову URL-адресу зараз, усі URL-адреси інтеграції будуть автоматично заповнені, що заощадить ваш час під час налаштування", + "preview": "напр. {example}", "mode": { - "subdomain": "", - "hostPort": "" + "subdomain": "Субдомен", + "hostPort": "Хост:Порт" } }, "docker": { - "detected": "", - "detectedBreakdown": "", - "summary": "", - "appBadge": "", - "appSectionLabel": "", - "scanning": "", - "scanningDescription": "" + "detected": "Docker виявив {count} служб", + "detectedBreakdown": "{integrations} інтеграцій - {apps} програм", + "summary": "Homarr виявив {apps} програми, {integrations} з яких мають вбудовану інтеграцію", + "appBadge": "Додаток", + "appSectionLabel": "Додатки для додавання на інформаційну панель (вибрано {count})", + "scanning": "Сканування Docker...", + "scanningDescription": "Пошук запущених контейнерів у вашій системі" } }, "finish": { @@ -177,7 +177,7 @@ }, "password": { "label": "Пароль", - "suggestionsTitle": "", + "suggestionsTitle": "Пропозиції паролів", "suggestion": { "length": "Включає принаймні 8 символів", "lowercase": "Включає малу літеру", @@ -205,7 +205,7 @@ "label": "Відкрити результати пошуку в новій вкладці" }, "ddgBangs": { - "label": "" + "label": "Увімкніть чубчик DuckDuckGo" } }, "error": { @@ -298,10 +298,10 @@ "changeDdgBangs": { "notification": { "success": { - "message": "" + "message": "Чубчик DuckDuckGo успішно змінено" }, "error": { - "message": "" + "message": "Неможливо перемкнути чубчик DuckDuckGo" } } }, @@ -367,11 +367,11 @@ "members": "Учасники", "homeBoard": { "label": "Домашня дошка", - "description": "" + "description": "Можна вибрати лише дошки, доступні групі" }, "mobileBoard": { "label": "Мобільна дошка", - "description": "" + "description": "Можна вибрати лише дошки, доступні групі" } }, "permission": { @@ -492,8 +492,8 @@ } }, "memberNotice": { - "mixed": "", - "external": "" + "mixed": "Деякі учасники належать до зовнішніх постачальників, і ними не можна керувати тут", + "external": "Усі учасники належать до зовнішніх постачальників, і ними не можна керувати тут" }, "reservedNotice": { "message": "Ця група зарезервована для використання системою та обмежує деякі дії. " @@ -573,8 +573,8 @@ "board": { "notification": { "success": { - "title": "", - "message": "" + "title": "Налаштування збережено", + "message": "Налаштування дошки успішно збережено" }, "error": { "title": "Не вдалося зберегти налаштування", @@ -586,17 +586,17 @@ "changePosition": { "notification": { "success": { - "message": "" + "message": "Позицію успішно змінено" }, "error": { - "message": "" + "message": "Неможливо змінити позицію" } } } }, "defaultGroup": { "name": "Група за замовчуванням", - "description": "" + "description": "{name} – усі авторизовані користувачі" } }, "app": { @@ -662,8 +662,8 @@ }, "useDifferentUrlForPing": { "checkbox": { - "label": "", - "description": "" + "label": "Використовуйте іншу URL-адресу для ping", + "description": "Корисно, якщо Homarr може отримати прямий доступ за допомогою внутрішньої назви хоста або мережі, щоб уникнути використання пропускної здатності провайдера" } } }, @@ -671,17 +671,17 @@ "select": { "label": "Оберіть додаток", "notFound": "Додатків не знайдено", - "search": "", - "noResults": "", - "action": "", - "title": "" + "search": "Пошук програми", + "noResults": "Результатів немає", + "action": "Оберіть {app}", + "title": "Оберіть додаток, щоб додати на цю дошку" }, "create": { - "title": "", - "description": "", - "action": "" + "title": "Створити новий додаток", + "description": "Створити новий додаток", + "action": "Відкрийте створення додатку" }, - "add": "" + "add": "Додати додаток" } }, "customWidget": { From 5a1967bdd4c1af2b30ff2ddc005516e70d16c1ee Mon Sep 17 00:00:00 2001 From: Nerdy Technician Date: Tue, 16 Jun 2026 09:29:48 +0100 Subject: [PATCH 13/17] feat(integrations): add Gotify notifications integration (#5847) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(integrations): add Gotify notifications integration Closes #4186 > "It would be really useful if Homarr could integrate with Gotify — > view notifications from Gotify right inside the Homarr dashboard, > maybe as a widget or a small panel. A widget that displays the latest > messages from Gotify (with title, message, time)." > — #4186: feat: Gotify Integration - display notifications in Homarr - `gotify/gotify-schema.ts` — Zod schema validating the `GET /message` API response from the Gotify server, including id, date, title, message body, and optional priority fields. - `gotify/gotify-integration.ts` — `GotifyIntegration` class extending the base `Integration` and implementing `INotificationsIntegration`. Authenticates via HTTP Basic Auth (username + password) and fetches the latest 100 messages from the `/message` endpoint. Connection testing hits `/health`. - `index.ts` — exports `GotifyIntegration` from the package public API. - `base/creator.ts` — registers `GotifyIntegration` in the integration creator map so it can be instantiated at runtime. - `integration.ts` — registers the `gotify` integration definition with: - `secretKinds: [["username", "password"]]` (Basic Auth credentials) - `category: ["notifications"]` (picked up automatically by the Notifications widget via `getIntegrationKindsByCategory`) - Icon sourced from `homarr-labs/dashboard-icons` - Documentation URL pointing to `/docs/integrations/gotify` - `docs/homarr-docs-sitemap.ts` — adds `/docs/integrations/gotify` to the `HomarrDocumentationPath` union type so the documentation link passes the TypeScript path validation check. Because `gotify` is registered under the `notifications` category, the existing Notifications widget automatically supports it with no widget code changes required. Users add the integration via Settings → Integrations, supply their Gotify server URL, username, and password, then attach it to a Notifications widget on any board. * refactor(integrations): inline json parse and add error handling in Gotify integration Address PR review feedback: remove intermediate `as unknown` cast by inlining response.json() directly, and use safeParseAsync to handle deserialization failures gracefully instead of throwing unhandled. --- .../src/docs/homarr-docs-sitemap.ts | 1 + packages/definitions/src/integration.ts | 7 ++++ packages/integrations/src/base/creator.ts | 2 + .../src/gotify/gotify-integration.ts | 41 +++++++++++++++++++ .../integrations/src/gotify/gotify-schema.ts | 14 +++++++ packages/integrations/src/index.ts | 1 + 6 files changed, 66 insertions(+) create mode 100644 packages/integrations/src/gotify/gotify-integration.ts create mode 100644 packages/integrations/src/gotify/gotify-schema.ts diff --git a/packages/definitions/src/docs/homarr-docs-sitemap.ts b/packages/definitions/src/docs/homarr-docs-sitemap.ts index 39ee81c08..158cc58b9 100644 --- a/packages/definitions/src/docs/homarr-docs-sitemap.ts +++ b/packages/definitions/src/docs/homarr-docs-sitemap.ts @@ -178,6 +178,7 @@ export type HomarrDocumentationPath = | "/docs/integrations/emby" | "/docs/integrations/github-containerregistry" | "/docs/integrations/github" + | "/docs/integrations/gotify" | "/docs/integrations/gitlab" | "/docs/integrations/glances" | "/docs/integrations/home-assistant" diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 5a91bef72..195fc81c5 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -355,6 +355,13 @@ export const integrationDefs = { documentationUrl: createDocumentationLink("/docs/integrations/ntfy"), defaultPort: 80, }, + gotify: { + name: "Gotify", + secretKinds: [["username", "password"]], + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gotify.svg", + category: ["notifications"], + documentationUrl: createDocumentationLink("/docs/integrations/gotify"), + }, ical: { name: "iCal", secretKinds: [["url"]], diff --git a/packages/integrations/src/base/creator.ts b/packages/integrations/src/base/creator.ts index f2d9547fc..ca0595651 100644 --- a/packages/integrations/src/base/creator.ts +++ b/packages/integrations/src/base/creator.ts @@ -35,6 +35,7 @@ import { MockIntegration } from "../mock/mock-integration"; import { NavidromeIntegration } from "../navidrome/navidrome-integration"; import { NextcloudIntegration } from "../nextcloud/nextcloud.integration"; import { NPMIntegration } from "../npm/npm-integration"; +import { GotifyIntegration } from "../gotify/gotify-integration"; import { NTFYIntegration } from "../ntfy/ntfy-integration"; import { OpenMediaVaultIntegration } from "../openmediavault/openmediavault-integration"; import { OPNsenseIntegration } from "../opnsense/opnsense-integration"; @@ -121,6 +122,7 @@ export const integrationCreators = { ical: ICalIntegration, quay: QuayIntegration, ntfy: NTFYIntegration, + gotify: GotifyIntegration, mock: MockIntegration, truenas: TrueNasIntegration, unraid: UnraidIntegration, diff --git a/packages/integrations/src/gotify/gotify-integration.ts b/packages/integrations/src/gotify/gotify-integration.ts new file mode 100644 index 000000000..b7fd9361e --- /dev/null +++ b/packages/integrations/src/gotify/gotify-integration.ts @@ -0,0 +1,41 @@ +import { ResponseError } from "@homarr/common/server"; +import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http"; + +import { Integration } from "../base/integration"; +import type { IntegrationTestingInput } from "../base/integration"; +import type { TestingResult } from "../base/test-connection/test-connection-service"; +import type { Notification } from "../interfaces/notifications/notification-types"; +import type { INotificationsIntegration } from "../interfaces/notifications/notifications-integration"; +import { gotifyMessagesResponseSchema } from "./gotify-schema"; + +export class GotifyIntegration extends Integration implements INotificationsIntegration { + public async testingAsync(input: IntegrationTestingInput): Promise { + await input.fetchAsync(this.url("/health"), { headers: this.getHeaders() }); + return { success: true }; + } + + private getHeaders() { + const credentials = Buffer.from( + `${super.getSecretValue("username")}:${super.getSecretValue("password")}`, + ).toString("base64"); + return { Authorization: `Basic ${credentials}` }; + } + + public async getNotificationsAsync(): Promise { + const url = this.url("/message", { limit: 100 }); + const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getHeaders() }); + + if (!response.ok) throw new ResponseError(response); + + const result = await gotifyMessagesResponseSchema.safeParseAsync(await response.json()); + if (!result.success) throw new Error(`Failed to parse Gotify response: ${result.error.message}`); + const parsed = result.data; + + return parsed.messages.map((message): Notification => ({ + id: String(message.id), + time: new Date(message.date), + title: message.title, + body: message.message, + })); + } +} diff --git a/packages/integrations/src/gotify/gotify-schema.ts b/packages/integrations/src/gotify/gotify-schema.ts new file mode 100644 index 000000000..5c89774ec --- /dev/null +++ b/packages/integrations/src/gotify/gotify-schema.ts @@ -0,0 +1,14 @@ +import { z } from "zod/v4"; + +// See: https://gotify.net/api-docs#/message/getMessages +export const gotifyMessageSchema = z.object({ + id: z.number(), + date: z.string(), + title: z.string(), + message: z.string(), + priority: z.number().optional(), +}); + +export const gotifyMessagesResponseSchema = z.object({ + messages: z.array(gotifyMessageSchema), +}); diff --git a/packages/integrations/src/index.ts b/packages/integrations/src/index.ts index 34c994ad3..64d8776bc 100644 --- a/packages/integrations/src/index.ts +++ b/packages/integrations/src/index.ts @@ -17,6 +17,7 @@ export { RadarrIntegration } from "./media-organizer/radarr/radarr-integration"; export { ReadarrIntegration } from "./media-organizer/readarr/readarr-integration"; export { SonarrIntegration } from "./media-organizer/sonarr/sonarr-integration"; export { NextcloudIntegration } from "./nextcloud/nextcloud.integration"; +export { GotifyIntegration } from "./gotify/gotify-integration"; export { NTFYIntegration } from "./ntfy/ntfy-integration"; export { OpenMediaVaultIntegration } from "./openmediavault/openmediavault-integration"; export { GlancesIntegration } from "./glances/glances-integration"; From 2ae83458e65ba06ce7d64b787dcc2e53d4d744e1 Mon Sep 17 00:00:00 2001 From: "homarr-update-contributors[bot]" <210161987+homarr-update-contributors[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:40:19 +0200 Subject: [PATCH 14/17] chore: update integrations list readme (#5972) Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com> --- docs/README.md | 47 +++++++++++-------- .../src/docs/homarr-docs-sitemap.ts | 1 - 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/docs/README.md b/docs/README.md index cdebccc8f..d185a5432 100644 --- a/docs/README.md +++ b/docs/README.md @@ -165,6 +165,13 @@ + + Gotify +
+

Gotify

+
+ + Home Assistant
@@ -198,15 +205,15 @@

Jellyseerr

- - + + Lidarr

Lidarr

- - + + LinuxServer.io
@@ -247,15 +254,15 @@

NZBGet

- - + + OpenMediaVault

OpenMediaVault

- - + + OPNsense
@@ -296,15 +303,15 @@

Plex

- - + + Prowlarr

Prowlarr

- - + + Proxmox
@@ -345,15 +352,15 @@

SABnzbd

- - + + Search.ch

Search.ch

- - + + Seerr
@@ -394,15 +401,15 @@

Technitium
DNS

- - + + Tracearr

Tracearr

- - + + Transmission
diff --git a/packages/definitions/src/docs/homarr-docs-sitemap.ts b/packages/definitions/src/docs/homarr-docs-sitemap.ts index 158cc58b9..39ee81c08 100644 --- a/packages/definitions/src/docs/homarr-docs-sitemap.ts +++ b/packages/definitions/src/docs/homarr-docs-sitemap.ts @@ -178,7 +178,6 @@ export type HomarrDocumentationPath = | "/docs/integrations/emby" | "/docs/integrations/github-containerregistry" | "/docs/integrations/github" - | "/docs/integrations/gotify" | "/docs/integrations/gitlab" | "/docs/integrations/glances" | "/docs/integrations/home-assistant" From dd57ee21023f00a2622d90b4638da0e399fe34a3 Mon Sep 17 00:00:00 2001 From: Glare Date: Tue, 16 Jun 2026 03:43:55 -0500 Subject: [PATCH 15/17] feat(integrations): add ArchiveTeam Warrior widget (#5784) * feat(integrations): add ArchiveTeam Warrior widget * feat(integrations): add support for basic auth for archive warrior * refactor(integrations): address ArchiveTeam Warrior review feedback * Increase cache duration from 1 to 5 minutes --------- Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com> --- .../router/widgets/archive-team-warrior.ts | 39 ++ packages/api/src/router/widgets/index.ts | 1 + packages/definitions/src/integration.ts | 9 + packages/definitions/src/widget.ts | 1 + .../archive-team-warrior-integration.ts | 386 ++++++++++++++++++ .../archive-team-warrior-schemas.ts | 61 +++ .../archive-team-warrior-types.ts | 37 ++ packages/integrations/src/base/creator.ts | 2 + packages/integrations/src/index.ts | 6 + .../src/archive-team-warrior.ts | 20 + packages/translation/src/lang/en.json | 9 + .../src/archive-team-warrior/component.tsx | 100 +++++ .../widgets/src/archive-team-warrior/index.ts | 16 + packages/widgets/src/index.tsx | 2 + 14 files changed, 689 insertions(+) create mode 100644 packages/api/src/router/widgets/archive-team-warrior.ts create mode 100644 packages/integrations/src/archive-team-warrior/archive-team-warrior-integration.ts create mode 100644 packages/integrations/src/archive-team-warrior/archive-team-warrior-schemas.ts create mode 100644 packages/integrations/src/archive-team-warrior/archive-team-warrior-types.ts create mode 100644 packages/request-handler/src/archive-team-warrior.ts create mode 100644 packages/widgets/src/archive-team-warrior/component.tsx create mode 100644 packages/widgets/src/archive-team-warrior/index.ts diff --git a/packages/api/src/router/widgets/archive-team-warrior.ts b/packages/api/src/router/widgets/archive-team-warrior.ts new file mode 100644 index 000000000..0824c165a --- /dev/null +++ b/packages/api/src/router/widgets/archive-team-warrior.ts @@ -0,0 +1,39 @@ +import { observable } from "@trpc/server/observable"; + +import type { ArchiveTeamWarriorStatus } from "@homarr/integrations"; +import { archiveTeamWarriorRequestHandler } from "@homarr/request-handler/archive-team-warrior"; + +import { createOneIntegrationMiddleware } from "../../middlewares/integration"; +import { createTRPCRouter, publicProcedure } from "../../trpc"; + +export const archiveTeamWarriorRouter = createTRPCRouter({ + getStatus: publicProcedure + .concat(createOneIntegrationMiddleware("query", "archiveTeamWarrior")) + .query(async ({ ctx }) => { + const handler = archiveTeamWarriorRequestHandler.handler(ctx.integration, {}); + const { data, timestamp } = await handler.getCachedOrUpdatedDataAsync({ forceUpdate: false }); + + return { + status: data, + updatedAt: timestamp, + }; + }), + + subscribeStatus: publicProcedure + .concat(createOneIntegrationMiddleware("query", "archiveTeamWarrior")) + .subscription(({ ctx }) => { + return observable<{ status: ArchiveTeamWarriorStatus; updatedAt: Date }>((emit) => { + const handler = archiveTeamWarriorRequestHandler.handler(ctx.integration, {}); + const unsubscribe = handler.subscribe((status) => { + emit.next({ + status, + updatedAt: new Date(), + }); + }); + + return () => { + unsubscribe(); + }; + }); + }), +}); diff --git a/packages/api/src/router/widgets/index.ts b/packages/api/src/router/widgets/index.ts index c1c574646..70c1aa997 100644 --- a/packages/api/src/router/widgets/index.ts +++ b/packages/api/src/router/widgets/index.ts @@ -3,6 +3,7 @@ import { lazy } from "@trpc/server"; import { createTRPCRouter } from "../../trpc"; export const widgetRouter = createTRPCRouter({ + archiveTeamWarrior: lazy(() => import("./archive-team-warrior").then((mod) => mod.archiveTeamWarriorRouter)), beszel: lazy(() => import("./beszel").then((mod) => mod.beszelRouter)), anchorNotes: lazy(() => import("./anchor-notes").then((mod) => mod.anchorNotesRouter)), coolify: lazy(() => import("./coolify").then((mod) => mod.coolifyRouter)), diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 195fc81c5..8efa552e4 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -489,6 +489,14 @@ export const integrationDefs = { documentationUrl: createDocumentationLink("/docs/integrations/beszel"), defaultPort: 8090, }, + archiveTeamWarrior: { + name: "ArchiveTeam Warrior", + secretKinds: [[], ["username", "password"]], + iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/archiveteam-warrior.png", + category: ["archiving"], + defaultUrl: "http://localhost:8001", + documentationUrl: null, + }, // This integration only returns mock data, it is used during development (but can also be used in production by directly going to the create page) mock: { name: "Mock", @@ -596,6 +604,7 @@ export const integrationCategories = [ "mediaMonitoring", "speedtest", "analytics", + "archiving", "ups", "documents", "mediaLibrary", diff --git a/packages/definitions/src/widget.ts b/packages/definitions/src/widget.ts index 1498cbffb..5fbaaacf8 100644 --- a/packages/definitions/src/widget.ts +++ b/packages/definitions/src/widget.ts @@ -41,6 +41,7 @@ export const widgetKinds = [ "uptimeKuma", "audioStats", "umami", + "archiveTeamWarrior", "ups", "beszelSystemTable", "beszelSystemGrid", diff --git a/packages/integrations/src/archive-team-warrior/archive-team-warrior-integration.ts b/packages/integrations/src/archive-team-warrior/archive-team-warrior-integration.ts new file mode 100644 index 000000000..5f95b6d34 --- /dev/null +++ b/packages/integrations/src/archive-team-warrior/archive-team-warrior-integration.ts @@ -0,0 +1,386 @@ +import { Buffer } from "node:buffer"; +import { randomUUID } from "node:crypto"; +import WebSocket from "ws"; +import type { RawData } from "ws"; +import { z } from "zod/v4"; + +import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http"; + +import type { IntegrationTestingInput } from "../base/integration"; +import { Integration } from "../base/integration"; +import { TestConnectionError } from "../base/test-connection/test-connection-error"; +import type { TestingResult } from "../base/test-connection/test-connection-service"; +import { + bandwidthMessageSchema, + itemStatusMessageSchema, + pipelineStartItemMessageSchema, + projectRefreshMessageSchema, + seesawFrameSchema, + warriorBroadcastMessageSchema, + warriorProjectSelectedMessageSchema, + warriorStatusMessageSchema, +} from "./archive-team-warrior-schemas"; +import type { SeesawFrame, WarriorItem, WarriorProject } from "./archive-team-warrior-schemas"; +import type { ArchiveTeamWarriorItem, ArchiveTeamWarriorStatus } from "./archive-team-warrior-types"; + +const SOCKJS_SNAPSHOT_TIMEOUT_MS = 2500; + +interface WarriorSnapshotState { + status: string; + runnerStatus?: string; + selectedProject: string | null; + broadcastMessage: string | null; + project: ArchiveTeamWarriorStatus["project"]; + bandwidth: ArchiveTeamWarriorStatus["bandwidth"]; + items: Map; +} + +export class ArchiveTeamWarriorIntegration extends Integration { + protected async testingAsync(input: IntegrationTestingInput): Promise { + const response = await input.fetchAsync(this.url("/index.html"), { + headers: this.createAuthHeaders(), + }); + + if (!response.ok) return TestConnectionError.StatusResult(response); + + return { success: true }; + } + + public async getStatusAsync(): Promise { + const baseUrl = this.url("/").toString().replace(/\/$/, ""); + + await fetchWithTrustedCertificatesAsync(`${baseUrl}/index.html`, { + headers: this.createAuthHeaders(), + }); + + return await this.readSockJsSnapshotAsync(baseUrl); + } + + private async readSockJsSnapshotAsync(baseUrl: string): Promise { + const snapshot: WarriorSnapshotState = { + status: "Unknown", + selectedProject: null, + broadcastMessage: null, + project: null, + bandwidth: null, + items: new Map(), + }; + + await new Promise((resolve, reject) => { + let isSettled = false; + + const socket = new WebSocket(this.createSockJsWebSocketUrl(baseUrl), { + headers: this.createAuthHeaders(), + }); + + const finish = () => { + if (isSettled) return; + + isSettled = true; + clearTimeout(timeout); + + if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) { + socket.close(); + } + + resolve(); + }; + + const fail = () => { + if (isSettled) return; + + isSettled = true; + clearTimeout(timeout); + reject(new Error("Failed to connect to ArchiveTeam Warrior websocket")); + }; + + const timeout = setTimeout(finish, SOCKJS_SNAPSHOT_TIMEOUT_MS); + + socket.on("message", (data: RawData) => { + const frames = this.parseSockJsMessage(this.decodeWebSocketData(data)); + + for (const frame of frames) { + this.applyFrameToSnapshot(frame, snapshot); + } + }); + + socket.on("error", fail); + socket.on("close", finish); + }); + + return this.createStatusFromSnapshot(snapshot); + } + + private applyFrameToSnapshot(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + switch (frame.event_name) { + case "warrior.status": + this.applyWarriorStatusFrame(frame, snapshot); + break; + + case "warrior.project_selected": + this.applyProjectSelectedFrame(frame, snapshot); + break; + + case "warrior.broadcast_message": + this.applyBroadcastMessageFrame(frame, snapshot); + break; + + case "runner.status": + this.applyRunnerStatusFrame(frame, snapshot); + break; + + case "project.refresh": + this.applyProjectRefreshFrame(frame, snapshot); + break; + + case "pipeline.start_item": + this.applyPipelineStartItemFrame(frame, snapshot); + break; + + case "item.complete": + this.applyItemStatusFrame(frame, snapshot, "completed"); + break; + + case "item.fail": + this.applyItemStatusFrame(frame, snapshot, "failed"); + break; + + case "item.cancel": + this.applyItemStatusFrame(frame, snapshot, "canceled"); + break; + + case "bandwidth": + this.applyBandwidthFrame(frame, snapshot); + break; + } + } + + private applyWarriorStatusFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = warriorStatusMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.status = this.normalizeDisplayStatus(message.data.status); + } + + private applyProjectSelectedFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = warriorProjectSelectedMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.selectedProject = message.data.project; + } + + private applyBroadcastMessageFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = warriorBroadcastMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.broadcastMessage = message.data.message; + } + + private applyRunnerStatusFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = warriorStatusMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.runnerStatus = this.normalizeDisplayStatus(message.data.status); + } + + private applyProjectRefreshFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = projectRefreshMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.project = this.mapProject(message.data?.project); + snapshot.runnerStatus = message.data?.status + ? this.normalizeDisplayStatus(message.data.status) + : snapshot.runnerStatus; + + for (const item of message.data?.items ?? []) { + snapshot.items.set(item.id, this.mapItem(item)); + } + } + + private applyPipelineStartItemFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = pipelineStartItemMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.items.set(message.data.item.id, this.mapItem(message.data.item)); + } + + private applyItemStatusFrame( + frame: SeesawFrame, + snapshot: WarriorSnapshotState, + status: ArchiveTeamWarriorItem["status"], + ) { + const message = itemStatusMessageSchema.safeParse(frame.message); + if (!message.success) return; + + this.updateItemStatus(snapshot.items, message.data.item_id, status); + } + + private applyBandwidthFrame(frame: SeesawFrame, snapshot: WarriorSnapshotState) { + const message = bandwidthMessageSchema.safeParse(frame.message); + if (!message.success) return; + + snapshot.bandwidth = message.data; + } + + private createStatusFromSnapshot(snapshot: WarriorSnapshotState): ArchiveTeamWarriorStatus { + const itemList = Array.from(snapshot.items.values()); + + return { + status: snapshot.status, + runnerStatus: snapshot.runnerStatus, + project: snapshot.project, + selectedProject: snapshot.selectedProject, + broadcastMessage: snapshot.broadcastMessage, + bandwidth: snapshot.bandwidth, + items: itemList, + counts: { + running: itemList.filter((item) => item.status === "running").length, + completed: itemList.filter((item) => item.status === "completed").length, + failed: itemList.filter((item) => item.status === "failed").length, + canceled: itemList.filter((item) => item.status === "canceled").length, + }, + updatedAt: new Date().toISOString(), + }; + } + + private createAuthHeaders() { + const authorization = this.createBasicAuthHeader(); + + return authorization ? { Authorization: authorization } : undefined; + } + + private createBasicAuthHeader() { + if (!this.hasSecretValue("username") || !this.hasSecretValue("password")) return undefined; + + const username = this.getSecretValue("username"); + const password = this.getSecretValue("password"); + const credentials = Buffer.from(`${username}:${password}`, "utf8").toString("base64"); + + return `Basic ${credentials}`; + } + + private decodeWebSocketData(data: RawData) { + if (typeof data === "string") return data; + if (Buffer.isBuffer(data)) return data.toString("utf8"); + if (Array.isArray(data)) return Buffer.concat(data).toString("utf8"); + + return Buffer.from(data).toString("utf8"); + } + + private parseSockJsMessage(raw: string): SeesawFrame[] { + if (raw === "o" || raw === "h" || raw.length === 0) return []; + if (!raw.startsWith("a")) return []; + + const rawPayload = this.parseJsonSafely(raw.slice(1)); + const payload = z.array(z.string()).safeParse(rawPayload); + if (!payload.success) return []; + + return payload.data.flatMap((frame) => { + const rawFrame = this.parseJsonSafely(frame); + const result = seesawFrameSchema.safeParse(rawFrame); + + return result.success ? [result.data] : []; + }); + } + + private parseJsonSafely(value: string) { + try { + return JSON.parse(value) as unknown; + } catch { + return null; + } + } + + private createSockJsWebSocketUrl(baseUrl: string) { + const url = new URL(baseUrl); + url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; + url.pathname = `/${Date.now()}/${randomUUID()}/websocket`; + return url.toString(); + } + + private mapProject(project?: WarriorProject): ArchiveTeamWarriorStatus["project"] { + if (!project) return null; + + return { + id: project.project_id?.toString(), + title: project.title ?? "Unknown Project", + }; + } + + private mapItem(item: WarriorItem): ArchiveTeamWarriorItem { + return { + id: item.id, + name: this.formatItemName(item.name), + status: this.normalizeItemStatus(item.status), + project: item.project, + startTime: item.start_time, + }; + } + + private normalizeDisplayStatus(status?: string | null) { + if (!status) return "Unknown"; + + switch (status) { + case "RUNNING_PROJECT": + return "Running"; + case "STOPPED": + return "Stopped"; + case "IDLE": + return "Idle"; + default: + return status + .replaceAll("_", " ") + .toLowerCase() + .replace(/\b\w/g, (character) => character.toUpperCase()); + } + } + + private formatItemName(name: string) { + const cleaned = name.replace(/^Item\s+/i, ""); + const parts = cleaned.split("\u0000").filter(Boolean); + + if (parts.length === 0) return "Item"; + + const first = parts[0]; + + if (!first) return "Item"; + + const label = parts.length === 1 ? first : `${first} + ${parts.length - 1} more`; + + return label.length <= 80 ? label : `${label.slice(0, 77)}...`; + } + + private normalizeItemStatus(status?: string | null): ArchiveTeamWarriorItem["status"] { + if (!status) return "running"; + + switch (status.toLowerCase()) { + case "running": + case "running_project": + return "running"; + case "completed": + return "completed"; + case "failed": + case "fail": + return "failed"; + case "canceled": + case "cancel": + return "canceled"; + default: + return "unknown"; + } + } + + private updateItemStatus( + items: Map, + itemId: string, + status: ArchiveTeamWarriorItem["status"], + ) { + const item = items.get(itemId); + if (!item) return; + + items.set(itemId, { + ...item, + status, + }); + } +} diff --git a/packages/integrations/src/archive-team-warrior/archive-team-warrior-schemas.ts b/packages/integrations/src/archive-team-warrior/archive-team-warrior-schemas.ts new file mode 100644 index 000000000..3c5f47fab --- /dev/null +++ b/packages/integrations/src/archive-team-warrior/archive-team-warrior-schemas.ts @@ -0,0 +1,61 @@ +import { z } from "zod/v4"; + +export const seesawFrameSchema = z.object({ + event_name: z.string(), + message: z.unknown(), +}); + +export const warriorStatusMessageSchema = z.object({ + status: z.string(), +}); + +export const warriorBroadcastMessageSchema = z.object({ + message: z.string().nullable(), +}); + +export const warriorProjectSelectedMessageSchema = z.object({ + project: z.string().nullable(), +}); + +export const warriorProjectSchema = z.object({ + project_id: z.number().optional(), + title: z.string().optional(), + project_html: z.string().optional(), + utc_deadline: z.string().nullable().optional(), +}); + +export const warriorItemSchema = z.object({ + id: z.string(), + name: z.string(), + status: z.string().nullable().optional(), + project: z.string().nullable().optional(), + start_time: z.number().optional(), +}); + +export const projectRefreshMessageSchema = z + .object({ + project: warriorProjectSchema.optional(), + status: z.string().optional(), + items: z.array(warriorItemSchema).optional(), + }) + .nullable(); + +export const itemStatusMessageSchema = z.object({ + item_id: z.string(), +}); + +export const pipelineStartItemMessageSchema = z.object({ + item: warriorItemSchema, +}); + +export const bandwidthMessageSchema = z.object({ + received: z.number().optional(), + sent: z.number().optional(), + receiving: z.number().optional(), + sending: z.number().optional(), + session_id: z.string().optional(), +}); + +export type WarriorProject = z.infer; +export type WarriorItem = z.infer; +export type SeesawFrame = z.infer; diff --git a/packages/integrations/src/archive-team-warrior/archive-team-warrior-types.ts b/packages/integrations/src/archive-team-warrior/archive-team-warrior-types.ts new file mode 100644 index 000000000..a21df642d --- /dev/null +++ b/packages/integrations/src/archive-team-warrior/archive-team-warrior-types.ts @@ -0,0 +1,37 @@ +export interface ArchiveTeamWarriorItem { + id: string; + name: string; + status: "running" | "completed" | "failed" | "canceled" | "unknown"; + project?: string | null; + startTime?: number; +} + +export interface ArchiveTeamWarriorBandwidth { + received?: number; + sent?: number; + receiving?: number; + sending?: number; + session_id?: string; +} + +export interface ArchiveTeamWarriorProject { + id?: string; + title: string; +} + +export interface ArchiveTeamWarriorStatus { + status: string; + runnerStatus?: string; + project?: ArchiveTeamWarriorProject | null; + selectedProject?: string | null; + broadcastMessage?: string | null; + bandwidth?: ArchiveTeamWarriorBandwidth | null; + items: ArchiveTeamWarriorItem[]; + counts: { + running: number; + completed: number; + failed: number; + canceled: number; + }; + updatedAt: string; +} diff --git a/packages/integrations/src/base/creator.ts b/packages/integrations/src/base/creator.ts index ca0595651..9d997e7c3 100644 --- a/packages/integrations/src/base/creator.ts +++ b/packages/integrations/src/base/creator.ts @@ -3,6 +3,7 @@ import type { IntegrationKind } from "@homarr/definitions"; import { AdGuardHomeIntegration } from "../adguard-home/adguard-home-integration"; import { BeszelIntegration } from "../beszel/beszel-integration"; import { AnchorIntegration } from "../anchor/anchor-integration"; +import { ArchiveTeamWarriorIntegration } from "../archive-team-warrior/archive-team-warrior-integration"; import { AudiobookshelfIntegration } from "../audiobookshelf/audiobookshelf-integration"; import { CodebergIntegration } from "../codeberg/codeberg-integration"; import { CoolifyIntegration } from "../coolify/coolify-integration"; @@ -136,6 +137,7 @@ export const integrationCreators = { audiobookshelf: AudiobookshelfIntegration, navidrome: NavidromeIntegration, umami: UmamiIntegration, + archiveTeamWarrior: ArchiveTeamWarriorIntegration, uptimeKuma: UptimeKumaIntegration, peaNut: PeaNutIntegration, beszel: BeszelIntegration, diff --git a/packages/integrations/src/index.ts b/packages/integrations/src/index.ts index 64d8776bc..66cf4be51 100644 --- a/packages/integrations/src/index.ts +++ b/packages/integrations/src/index.ts @@ -1,5 +1,6 @@ // General integrations export { AdGuardHomeIntegration } from "./adguard-home/adguard-home-integration"; +export { ArchiveTeamWarriorIntegration } from "./archive-team-warrior/archive-team-warrior-integration"; export { TechnitiumDnsIntegration } from "./technitium/technitium-integration"; export { AnchorIntegration } from "./anchor/anchor-integration"; export { Aria2Integration } from "./download-client/aria2/aria2-integration"; @@ -74,6 +75,11 @@ export type { AnchorNotesListInput, AnchorNoteSummary, } from "./anchor/anchor-types"; +export type { + ArchiveTeamWarriorBandwidth, + ArchiveTeamWarriorItem, + ArchiveTeamWarriorStatus, +} from "./archive-team-warrior/archive-team-warrior-types"; export type { TracearrDashboardData } from "./tracearr/tracearr-types"; export type { SpeedtestTrackerDashboardData } from "./speedtest-tracker/speedtest-tracker-types"; export type { AudiobookshelfDashboardData } from "./audiobookshelf/audiobookshelf-types"; diff --git a/packages/request-handler/src/archive-team-warrior.ts b/packages/request-handler/src/archive-team-warrior.ts new file mode 100644 index 000000000..4038ec9dc --- /dev/null +++ b/packages/request-handler/src/archive-team-warrior.ts @@ -0,0 +1,20 @@ +import dayjs from "dayjs"; + +import type { IntegrationKindByCategory } from "@homarr/definitions"; +import type { ArchiveTeamWarriorStatus } from "@homarr/integrations"; +import { createIntegrationAsync } from "@homarr/integrations"; + +import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-request-handler"; + +export const archiveTeamWarriorRequestHandler = createCachedIntegrationRequestHandler< + ArchiveTeamWarriorStatus, + IntegrationKindByCategory<"archiving">, + Record +>({ + async requestAsync(integration) { + const integrationInstance = await createIntegrationAsync(integration); + return await integrationInstance.getStatusAsync(); + }, + cacheDuration: dayjs.duration(5, "minute"), + queryKey: "archiveTeamWarriorStatus", +}); diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 98be3be86..b0a126733 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -1739,6 +1739,15 @@ } } }, + "archiveTeamWarrior": { + "name": "ArchiveTeam Warrior", + "description": "Monitor ArchiveTeam Warrior status, projects, and active archival jobs.", + "option": { + "showBroadcastMessage": { + "label": "Show broadcast messages" + } + } + }, "dnsHoleSummary": { "name": "DNS Hole Summary", "description": "Displays the summary of your DNS Hole", diff --git a/packages/widgets/src/archive-team-warrior/component.tsx b/packages/widgets/src/archive-team-warrior/component.tsx new file mode 100644 index 000000000..dacac283d --- /dev/null +++ b/packages/widgets/src/archive-team-warrior/component.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { Avatar, Badge, Card, Group, SimpleGrid, Stack, Text } from "@mantine/core"; + +import { clientApi } from "@homarr/api/client"; +import { humanFileSize } from "@homarr/common"; +import { getIconUrl } from "@homarr/definitions"; + +import type { WidgetComponentProps } from "../definition"; + +export default function ArchiveTeamWarriorWidget({ + integrationIds, + options, +}: WidgetComponentProps<"archiveTeamWarrior">) { + const integrationId = integrationIds[0]; + + if (!integrationId) { + return null; + } + + return ; +} + +const ArchiveTeamWarriorWidgetContent = ({ + integrationId, + options, +}: { + integrationId: string; + options: WidgetComponentProps<"archiveTeamWarrior">["options"]; +}) => { + const [data] = clientApi.widget.archiveTeamWarrior.getStatus.useSuspenseQuery({ + integrationId, + }); + + const utils = clientApi.useUtils(); + + clientApi.widget.archiveTeamWarrior.subscribeStatus.useSubscription( + { integrationId }, + { + onData: (next) => { + utils.widget.archiveTeamWarrior.getStatus.setData({ integrationId }, next); + }, + }, + ); + + const status = data.status; + const projectName = status.project?.title ?? status.selectedProject ?? "No project selected"; + + return ( + + + + + + {projectName} + + + + + {status.status} + + + + {options.showBroadcastMessage && status.broadcastMessage && ( + + + {status.broadcastMessage} + + + )} + + + + + + + + + {status.bandwidth && ( + + + + + )} + + ); +}; + +const Metric = ({ label, value }: { label: string; value: number | string }) => ( + + + {value} + + + {label} + + +); + +const formatBandwidth = (value?: number) => humanFileSize(Math.round(value ?? 0), "/s"); diff --git a/packages/widgets/src/archive-team-warrior/index.ts b/packages/widgets/src/archive-team-warrior/index.ts new file mode 100644 index 000000000..5e0998ec2 --- /dev/null +++ b/packages/widgets/src/archive-team-warrior/index.ts @@ -0,0 +1,16 @@ +import { IconArchive } from "@tabler/icons-react"; + +import { createWidgetDefinition } from "../definition"; +import { optionsBuilder } from "../options"; + +export const { definition, componentLoader } = createWidgetDefinition("archiveTeamWarrior", { + icon: IconArchive, + createOptions() { + return optionsBuilder.from((factory) => ({ + showBroadcastMessage: factory.switch({ + defaultValue: true, + }), + })); + }, + supportedIntegrations: ["archiveTeamWarrior"], +}).withDynamicImport(() => import("./component")); diff --git a/packages/widgets/src/index.tsx b/packages/widgets/src/index.tsx index dd389d719..628c67e32 100644 --- a/packages/widgets/src/index.tsx +++ b/packages/widgets/src/index.tsx @@ -10,6 +10,7 @@ import type { SettingsContextProps } from "@homarr/settings/creator"; import * as anchorNote from "./anchor-note"; import * as audioStats from "./audio-stats"; import * as app from "./app"; +import * as archiveTeamWarrior from "./archive-team-warrior"; import * as bookmarks from "./bookmarks"; import * as calendar from "./calendar"; import * as clock from "./clock"; @@ -67,6 +68,7 @@ export const widgetImports = { clock, weather, app, + archiveTeamWarrior, anchorNote, notebook, iframe, From 4692e7ce3aba3a606714e9fa0e1500df2f4f1185 Mon Sep 17 00:00:00 2001 From: "homarr-update-contributors[bot]" <210161987+homarr-update-contributors[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:45:49 +0200 Subject: [PATCH 16/17] chore: update integrations list readme (#5973) Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com> --- docs/README.md | 67 ++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/docs/README.md b/docs/README.md index d185a5432..8e37dfdc6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -74,6 +74,13 @@
+ + ArchiveTeam Warrior +
+

ArchiveTeam
Warrior

+
+ + Aria2
@@ -100,15 +107,15 @@

Codeberg

- - + + Coolify

Coolify

- - + + Dash.
@@ -149,15 +156,15 @@

GitHub
Container
Registry

- - + + Gitlab

Gitlab

- - + + Glances
@@ -198,15 +205,15 @@

Jellyfin

- - + + Jellyseerr

Jellyseerr

- - + + Lidarr
@@ -247,15 +254,15 @@

ntfy

- - + + NZBGet

NZBGet

- - + + OpenMediaVault
@@ -296,15 +303,15 @@

Pi-hole

- - + + Plex

Plex

- - + + Prowlarr
@@ -345,15 +352,15 @@

Readarr

- - + + SABnzbd

SABnzbd

- - + + Search.ch
@@ -394,15 +401,15 @@

Tdarr

- - + + Technitium DNS

Technitium
DNS

- - + + Tracearr
@@ -443,8 +450,8 @@

Unraid

- - + + Uptime Kuma
From fb18b975b25b059f9223d4aac13f2e9475b27cc0 Mon Sep 17 00:00:00 2001 From: Louis Bailleau Date: Tue, 16 Jun 2026 13:45:44 +0200 Subject: [PATCH 17/17] feat: add local group support for OIDC users (#5952) --- .vscode/settings.json | 2 +- .../docs/advanced/single-sign-on/index.mdx | 3 + .../groups/[id]/members/_add-group-member.tsx | 8 +- .../manage/users/groups/[id]/members/page.tsx | 72 +++- .../components/access/user-select-modal.tsx | 5 +- packages/api/src/router/group.ts | 26 +- packages/api/src/router/test/group.spec.ts | 42 +- packages/api/src/router/user.ts | 8 +- packages/auth/env.ts | 1 + packages/auth/events.ts | 7 +- packages/auth/providers/check-provider.ts | 48 +++ .../providers/test/check-provider.spec.ts | 105 +++++ packages/auth/server.ts | 9 +- packages/auth/test/events.spec.ts | 32 +- packages/integrations/test/technitium.spec.ts | 363 ++++++++++-------- 15 files changed, 540 insertions(+), 191 deletions(-) create mode 100644 packages/auth/providers/test/check-provider.spec.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index aa5d827ff..844d89828 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "typescript.tsdk": "node_modules\\typescript\\lib", + "js/ts.tsdk.path": "node_modules\\typescript\\lib", "js/ts.implicitProjectConfig.experimentalDecorators": true, "cSpell.words": [ "ajnart", diff --git a/apps/docs/docs/advanced/single-sign-on/index.mdx b/apps/docs/docs/advanced/single-sign-on/index.mdx index b81772480..7dab32729 100644 --- a/apps/docs/docs/advanced/single-sign-on/index.mdx +++ b/apps/docs/docs/advanced/single-sign-on/index.mdx @@ -124,6 +124,8 @@ Homarr supports multiple authentication options, from internal userbase (credent Roles are fetched from group claims. This can also be changed to roles for example added to a azure app registration by using the `AUTH_OIDC_GROUPS_ATTRIBUTE`. + If you'd rather manage group memberships for OIDC users locally (instead of via the IdP), set `AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT=true`. While enabled, Homarr stops syncing OIDC users' group memberships from the groups claim, and admins can add/remove OIDC users on a group's members page. If you turn this back to `false`, the next login of each user resumes the regular sync and reconciles their groups with the IdP's claim again, which may remove memberships that were added manually while local management was enabled. +
Example Setup @@ -197,6 +199,7 @@ Homarr supports multiple authentication options, from internal userbase (credent | ``AUTH_OIDC_AUTO_LOGIN`` | Automatically redirect to OIDC login | false | | ``AUTH_OIDC_SCOPE_OVERWRITE`` | Overwrite default scopes (openid, profile, email) | openid email profile groups | | ``AUTH_OIDC_GROUPS_ATTRIBUTE`` | Attribute used for groups (roles) claim | groups | + | ``AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT`` | Stop syncing group memberships of OIDC users from the groups claim and manage them locally via the group members page instead | false | | ``AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE`` | Overwrite name attribute. By default it will use preferred_username if it does not contain a `@` and otherwise name. | --- | | ``AUTH_OIDC_FORCE_USERINFO`` | Force userinfo endpoint to be used for user information. | false | | ``AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING`` | Enable account linking for OIDC provider. This will link the OIDC accounts by email. Be sure that you have verified emails to prevent stealing accounts. | false | diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx index 2b304a1f9..578a6402f 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx @@ -4,6 +4,7 @@ import { useCallback } from "react"; import { clientApi } from "@homarr/api/client"; import { revalidatePathActionAsync } from "@homarr/common/client"; +import type { SupportedAuthProvider } from "@homarr/definitions"; import { useModalAction } from "@homarr/modals"; import { useScopedI18n } from "@homarr/translation/client"; @@ -13,9 +14,10 @@ import { MobileAffixButton } from "~/components/manage/mobile-affix-button"; interface AddGroupMemberProps { groupId: string; presentUserIds: string[]; + allowedProviders: SupportedAuthProvider[]; } -export const AddGroupMember = ({ groupId, presentUserIds }: AddGroupMemberProps) => { +export const AddGroupMember = ({ groupId, presentUserIds, allowedProviders }: AddGroupMemberProps) => { const tMembersAdd = useScopedI18n("group.action.addMember"); const { mutateAsync } = clientApi.group.addMember.useMutation(); const { openModal } = useModalAction(UserSelectModal); @@ -32,13 +34,13 @@ export const AddGroupMember = ({ groupId, presentUserIds }: AddGroupMemberProps) await revalidatePathActionAsync(`/manage/users/groups/${groupId}}/members`); }, presentUserIds, - excludeExternalProviders: true, + allowedProviders, }, { title: tMembersAdd("label"), }, ); - }, [openModal, presentUserIds, groupId, mutateAsync, tMembersAdd]); + }, [openModal, presentUserIds, groupId, mutateAsync, tMembersAdd, allowedProviders]); return {tMembersAdd("label")}; }; diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx index f0a6d879a..08ef65a49 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx @@ -1,12 +1,27 @@ import { notFound } from "next/navigation"; -import { Alert, Anchor, Center, Group, Stack, Table, TableTbody, TableTd, TableTr, Text, Title } from "@mantine/core"; +import { + Alert, + Anchor, + Center, + Group, + Stack, + Table, + TableTbody, + TableTd, + TableTr, + Text, + Title, +} from "@mantine/core"; import { IconExclamationCircle } from "@tabler/icons-react"; import type { RouterOutputs } from "@homarr/api"; import { api } from "@homarr/api/server"; -import { env } from "@homarr/auth/env"; import { auth } from "@homarr/auth/next"; -import { isProviderEnabled } from "@homarr/auth/server"; +import { + getGroupMemberManagementType, + getLocallyManageableProviders, + isGroupMembershipManagedLocally, +} from "@homarr/auth/server"; import { everyoneGroup } from "@homarr/definitions"; import { getI18n, getScopedI18n } from "@homarr/translation/server"; import { Link, SearchInput, UserAvatar } from "@homarr/ui"; @@ -40,14 +55,17 @@ export default async function GroupsDetailPage(props: GroupsDetailPageProps) { const filteredMembers = searchParams.search ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - group.members.filter((member) => member.name?.toLowerCase().includes(searchParams.search!.trim().toLowerCase())) + group.members.filter((member) => + member.name + ?.toLowerCase() + .includes(searchParams.search!.trim().toLowerCase()), + ) : group.members; - const providerTypes = isProviderEnabled("credentials") - ? env.AUTH_PROVIDERS.length > 1 - ? "mixed" - : "credentials" - : "external"; + // "local" = every enabled provider managed locally, "external" = none, "mixed" = some. + const managementType = getGroupMemberManagementType(); + const canManageMembers = managementType !== "external"; + const allowedProviders = getLocallyManageableProviders(); return ( @@ -56,17 +74,28 @@ export default async function GroupsDetailPage(props: GroupsDetailPageProps) { {isReserved ? ( ) : ( - providerTypes !== "credentials" && ( - }> - {t(`group.memberNotice.${providerTypes}`)} + managementType !== "local" && ( + } + > + {t(`group.memberNotice.${managementType}`)} ) )} - - {isProviderEnabled("credentials") && !isReserved && ( - member.id)} /> + + {canManageMembers && !isReserved && ( + member.id)} + allowedProviders={allowedProviders} + /> )} {filteredMembers.length === 0 && ( @@ -79,7 +108,12 @@ export default async function GroupsDetailPage(props: GroupsDetailPageProps) { {filteredMembers.map((member) => ( - + ))}
@@ -94,6 +128,8 @@ interface RowProps { } const Row = ({ member, groupId, disabled }: RowProps) => { + const canBeRemoved = isGroupMembershipManagedLocally(member.provider); + return ( @@ -105,7 +141,9 @@ const Row = ({ member, groupId, disabled }: RowProps) => { - {member.provider === "credentials" && !disabled && } + {canBeRemoved && !disabled && ( + + )} ); diff --git a/apps/nextjs/src/components/access/user-select-modal.tsx b/apps/nextjs/src/components/access/user-select-modal.tsx index 099054747..21f35e4eb 100644 --- a/apps/nextjs/src/components/access/user-select-modal.tsx +++ b/apps/nextjs/src/components/access/user-select-modal.tsx @@ -5,6 +5,7 @@ import { IconCheck } from "@tabler/icons-react"; import type { RouterOutputs } from "@homarr/api"; import { clientApi } from "@homarr/api/client"; +import type { SupportedAuthProvider } from "@homarr/definitions"; import { useForm } from "@homarr/form"; import { createModal } from "@homarr/modals"; import { useI18n } from "@homarr/translation/client"; @@ -12,7 +13,7 @@ import { UserAvatar } from "@homarr/ui"; interface InnerProps { presentUserIds: string[]; - excludeExternalProviders?: boolean; + allowedProviders?: SupportedAuthProvider[]; onSelect: (props: { id: string; name: string; image: string; email: string | null }) => void | Promise; confirmLabel?: string; } @@ -24,7 +25,7 @@ interface UserSelectFormType { export const UserSelectModal = createModal(({ actions, innerProps }) => { const t = useI18n(); const { data: users, isPending } = clientApi.user.selectable.useQuery({ - excludeExternalProviders: innerProps.excludeExternalProviders, + providers: innerProps.allowedProviders, }); const [loading, setLoading] = useState(false); const form = useForm(); diff --git a/packages/api/src/router/group.ts b/packages/api/src/router/group.ts index 13fb4fdd3..7c00ff125 100644 --- a/packages/api/src/router/group.ts +++ b/packages/api/src/router/group.ts @@ -1,11 +1,12 @@ import { TRPCError } from "@trpc/server"; import { z } from "zod/v4"; +import { canManageGroupMembersLocally, isGroupMembershipManagedLocally } from "@homarr/auth/server"; import { createId } from "@homarr/common"; import type { Database } from "@homarr/db"; import { and, eq, handleTransactionsAsync, like, not } from "@homarr/db"; import { getMaxGroupPositionAsync } from "@homarr/db/queries"; -import { groupMembers, groupPermissions, groups } from "@homarr/db/schema"; +import { groupMembers, groupPermissions, groups, users } from "@homarr/db/schema"; import { everyoneGroup } from "@homarr/definitions"; import { byIdSchema, paginatedSchema } from "@homarr/validation/common"; import { @@ -18,7 +19,6 @@ import { } from "@homarr/validation/group"; import { createTRPCRouter, onboardingProcedure, permissionRequiredProcedure, protectedProcedure } from "../trpc"; -import { throwIfCredentialsDisabled } from "./invite/checks"; import { nextOnboardingStepAsync } from "./onboard/onboard-queries"; export const groupRouter = createTRPCRouter({ @@ -315,10 +315,10 @@ export const groupRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { await throwIfGroupNotFoundAsync(ctx.db, input.groupId); await throwIfGroupNameIsReservedAsync(ctx.db, input.groupId); - throwIfCredentialsDisabled(); + throwIfGroupMembersCannotBeManagedLocally(); const user = await ctx.db.query.users.findFirst({ - where: eq(groups.id, input.userId), + where: eq(users.id, input.userId), }); if (!user) { @@ -328,6 +328,13 @@ export const groupRouter = createTRPCRouter({ }); } + if (!isGroupMembershipManagedLocally(user.provider)) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "User's provider is not managed locally", + }); + } + await ctx.db.insert(groupMembers).values({ groupId: input.groupId, userId: input.userId, @@ -339,7 +346,7 @@ export const groupRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { await throwIfGroupNotFoundAsync(ctx.db, input.groupId); await throwIfGroupNameIsReservedAsync(ctx.db, input.groupId); - throwIfCredentialsDisabled(); + throwIfGroupMembersCannotBeManagedLocally(); await ctx.db .delete(groupMembers) @@ -371,6 +378,15 @@ const throwIfGroupNameIsReservedAsync = async (db: Database, id: string) => { } }; +const throwIfGroupMembersCannotBeManagedLocally = () => { + if (!canManageGroupMembersLocally()) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Group members cannot be managed locally", + }); + } +}; + const throwIfGroupNotFoundAsync = async (db: Database, id: string) => { const group = await db.query.groups.findFirst({ where: eq(groups.id, id), diff --git a/packages/api/src/router/test/group.spec.ts b/packages/api/src/router/test/group.spec.ts index 1f5b461cc..5deeb3e2f 100644 --- a/packages/api/src/router/test/group.spec.ts +++ b/packages/api/src/router/test/group.spec.ts @@ -748,7 +748,45 @@ describe("addMember should add member to group", () => { }); // Assert - await expect(actAsync()).rejects.toThrow("Credentials provider is disabled"); + await expect(actAsync()).rejects.toThrow("Group members cannot be managed locally"); + }); + + test("with a user from an externally managed provider it should throw FORBIDDEN error", async () => { + // Arrange + const db = createDb(); + const spy = vi.spyOn(env, "env", "get"); + spy.mockReturnValue({ AUTH_PROVIDERS: ["credentials", "ldap"] } as never); + const caller = groupRouter.createCaller({ db, deviceType: undefined, session: adminSession }); + + const groupId = createId(); + const userId = createId(); + await db.insert(users).values([ + { + id: userId, + name: "User", + provider: "ldap", + }, + { + id: defaultOwnerId, + name: "Creator", + }, + ]); + await db.insert(groups).values({ + id: groupId, + name: "Group", + ownerId: defaultOwnerId, + position: 1, + }); + + // Act + const actAsync = async () => + await caller.addMember({ + groupId, + userId, + }); + + // Assert + await expect(actAsync()).rejects.toThrow("User's provider is not managed locally"); }); }); @@ -872,7 +910,7 @@ describe("removeMember should remove member from group", () => { }); // Assert - await expect(actAsync()).rejects.toThrow("Credentials provider is disabled"); + await expect(actAsync()).rejects.toThrow("Group members cannot be managed locally"); }); }); diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index eee626026..c349f6cec 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -7,12 +7,12 @@ import { comparePasswordsAsync, hashPasswordAsync } from "@homarr/auth"; import { createId } from "@homarr/common"; import { createLogger } from "@homarr/core/infrastructure/logs"; import type { Database } from "@homarr/db"; -import { and, eq, handleTransactionsAsync, like } from "@homarr/db"; +import { and, eq, handleTransactionsAsync, inArray, like } from "@homarr/db"; import { getMaxGroupPositionAsync } from "@homarr/db/queries"; import { boards, groupMembers, groupPermissions, groups, invites, users } from "@homarr/db/schema"; import { selectUserSchema } from "@homarr/db/validationSchemas"; import type { SupportedAuthProvider } from "@homarr/definitions"; -import { credentialsAdminGroup } from "@homarr/definitions"; +import { credentialsAdminGroup, supportedAuthProviders } from "@homarr/definitions"; import { byIdSchema } from "@homarr/validation/common"; import type { userBaseCreateSchema } from "@homarr/validation/user"; import { @@ -257,7 +257,7 @@ export const userRouter = createTRPCRouter({ }), // Is protected because also used in board access / integration access forms selectable: protectedProcedure - .input(z.object({ excludeExternalProviders: z.boolean().default(false) }).optional()) + .input(z.object({ providers: z.array(z.enum(supportedAuthProviders)).optional() }).optional()) .output( z.array( selectUserSchema.pick({ @@ -284,7 +284,7 @@ export const userRouter = createTRPCRouter({ image: true, email: true, }, - where: input?.excludeExternalProviders ? eq(users.provider, "credentials") : undefined, + where: input?.providers ? inArray(users.provider, input.providers) : undefined, }); }), search: permissionRequiredProcedure diff --git a/packages/auth/env.ts b/packages/auth/env.ts index 6fb7048dd..62a018d46 100644 --- a/packages/auth/env.ts +++ b/packages/auth/env.ts @@ -37,6 +37,7 @@ export const env = createEnv({ AUTH_OIDC_AUTO_LOGIN: createBooleanSchema(false), AUTH_OIDC_SCOPE_OVERWRITE: z.string().min(1).default("openid email profile groups"), AUTH_OIDC_GROUPS_ATTRIBUTE: z.string().default("groups"), // Is used in the signIn event to assign the correct groups, key is from object of decoded id_token + AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT: createBooleanSchema(false), // When enabled, group memberships of oidc users are managed locally instead of synced from the groups claim AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE: z.string().optional(), AUTH_OIDC_FORCE_USERINFO: createBooleanSchema(false), AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING: createBooleanSchema(false), diff --git a/packages/auth/events.ts b/packages/auth/events.ts index 2593c418b..7f8ecdc33 100644 --- a/packages/auth/events.ts +++ b/packages/auth/events.ts @@ -31,7 +31,12 @@ export const createSignInEventHandler = (db: Database): Exclude { // The question mark is placed there because isProviderEnabled is called during static build of about page // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return env.AUTH_PROVIDERS?.includes(provider); }; + +/** + * Whether a single provider's group memberships are managed locally (manually via UI/API). + * True for credentials users, and for oidc users when AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT is enabled. + */ +export const isGroupMembershipManagedLocally = (provider: SupportedAuthProvider): boolean => { + switch (provider) { + case "credentials": + return true; + case "oidc": + return env.AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT; + default: + // ldap + any provider added later: externally managed (synced, not editable + // here) until explicitly given a case above. New providers safe-by-default. + return false; + } +}; + +export const getEnabledProviders = (): SupportedAuthProvider[] => supportedAuthProviders.filter(isProviderEnabled); + +/** + * The enabled providers whose group memberships can be managed locally. + */ +export const getLocallyManageableProviders = (): SupportedAuthProvider[] => + getEnabledProviders().filter(isGroupMembershipManagedLocally); + +/** + * Classifies group-member management across all enabled providers: + * - "local": every enabled provider is managed locally (no member is synced externally) + * - "external": no enabled provider is managed locally (every member is synced externally) + * - "mixed": some are managed locally, some externally (only a subset of members is editable here) + */ +export const getGroupMemberManagementType = (): GroupMemberManagementType => { + const enabled = getEnabledProviders(); + const manageable = enabled.filter(isGroupMembershipManagedLocally); + + if (manageable.length === enabled.length) return "local"; + if (manageable.length === 0) return "external"; + return "mixed"; +}; + +/** + * Whether group memberships can be managed locally for at least one enabled provider. + */ +export const canManageGroupMembersLocally = (): boolean => getLocallyManageableProviders().length > 0; diff --git a/packages/auth/providers/test/check-provider.spec.ts b/packages/auth/providers/test/check-provider.spec.ts new file mode 100644 index 000000000..fa7f2ffb2 --- /dev/null +++ b/packages/auth/providers/test/check-provider.spec.ts @@ -0,0 +1,105 @@ +import { beforeEach, describe, expect, test, vi } from "vitest"; + +import type { SupportedAuthProvider } from "@homarr/definitions"; + +import * as env from "../../env"; +import { + canManageGroupMembersLocally, + getGroupMemberManagementType, + getLocallyManageableProviders, + isGroupMembershipManagedLocally, +} from "../check-provider"; + +const mockEnv = (providers: SupportedAuthProvider[], oidcLocalManagement = false) => { + vi.spyOn(env, "env", "get").mockReturnValue({ + AUTH_PROVIDERS: providers, + AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT: oidcLocalManagement, + } as never); +}; + +beforeEach(() => { + vi.restoreAllMocks(); +}); + +describe("isGroupMembershipManagedLocally", () => { + test("credentials is always managed locally", () => { + mockEnv(["credentials"]); + expect(isGroupMembershipManagedLocally("credentials")).toBe(true); + }); + + test("oidc follows AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT", () => { + mockEnv(["oidc"], true); + expect(isGroupMembershipManagedLocally("oidc")).toBe(true); + + mockEnv(["oidc"], false); + expect(isGroupMembershipManagedLocally("oidc")).toBe(false); + }); + + test("ldap is never managed locally", () => { + mockEnv(["ldap"]); + expect(isGroupMembershipManagedLocally("ldap")).toBe(false); + }); +}); + +describe("getGroupMemberManagementType", () => { + test("credentials only is local", () => { + mockEnv(["credentials"]); + expect(getGroupMemberManagementType()).toBe("local"); + }); + + test("credentials + oidc with local management is local", () => { + mockEnv(["credentials", "oidc"], true); + expect(getGroupMemberManagementType()).toBe("local"); + }); + + test("credentials + oidc without local management is mixed", () => { + mockEnv(["credentials", "oidc"], false); + expect(getGroupMemberManagementType()).toBe("mixed"); + }); + + test("credentials + ldap is mixed", () => { + mockEnv(["credentials", "ldap"]); + expect(getGroupMemberManagementType()).toBe("mixed"); + }); + + test("ldap only is external", () => { + mockEnv(["ldap"]); + expect(getGroupMemberManagementType()).toBe("external"); + }); + + test("oidc only without local management is external", () => { + mockEnv(["oidc"], false); + expect(getGroupMemberManagementType()).toBe("external"); + }); +}); + +describe("getLocallyManageableProviders", () => { + test("returns only the enabled, locally managed providers", () => { + mockEnv(["credentials", "oidc", "ldap"], true); + expect(getLocallyManageableProviders()).toEqual(["credentials", "oidc"]); + + mockEnv(["credentials", "oidc", "ldap"], false); + expect(getLocallyManageableProviders()).toEqual(["credentials"]); + + mockEnv(["ldap"]); + expect(getLocallyManageableProviders()).toEqual([]); + }); +}); + +describe("canManageGroupMembersLocally", () => { + test("is true when at least one enabled provider is managed locally", () => { + mockEnv(["credentials", "ldap"]); + expect(canManageGroupMembersLocally()).toBe(true); + + mockEnv(["oidc"], true); + expect(canManageGroupMembersLocally()).toBe(true); + }); + + test("is false when every enabled provider is external", () => { + mockEnv(["ldap"]); + expect(canManageGroupMembersLocally()).toBe(false); + + mockEnv(["oidc"], false); + expect(canManageGroupMembersLocally()).toBe(false); + }); +}); diff --git a/packages/auth/server.ts b/packages/auth/server.ts index 913086ece..be063a769 100644 --- a/packages/auth/server.ts +++ b/packages/auth/server.ts @@ -1,4 +1,11 @@ export { hasQueryAccessToIntegrationsAsync } from "./permissions/integration-query-permissions"; export { getIntegrationsWithPermissionsAsync } from "./permissions/integrations-with-permissions"; -export { isProviderEnabled } from "./providers/check-provider"; +export { + isProviderEnabled, + isGroupMembershipManagedLocally, + getLocallyManageableProviders, + getGroupMemberManagementType, + canManageGroupMembersLocally, +} from "./providers/check-provider"; +export type { GroupMemberManagementType } from "./providers/check-provider"; export { createSessionCallback, createSessionAsync } from "./callbacks"; diff --git a/packages/auth/test/events.spec.ts b/packages/auth/test/events.spec.ts index ad6178ab2..c11eebdff 100644 --- a/packages/auth/test/events.spec.ts +++ b/packages/auth/test/events.spec.ts @@ -12,11 +12,13 @@ import { colorSchemeCookieKey, everyoneGroup } from "@homarr/definitions"; import { createSignInEventHandler } from "../events"; vi.mock("next-auth", () => ({})); +const mockEnv = vi.hoisted(() => ({ + AUTH_OIDC_GROUPS_ATTRIBUTE: "someRandomGroupsKey", + AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT: false, +})); vi.mock("../env", () => { return { - env: { - AUTH_OIDC_GROUPS_ATTRIBUTE: "someRandomGroupsKey", - }, + env: mockEnv, }; }); // eslint-disable-next-line @typescript-eslint/consistent-type-imports @@ -198,6 +200,30 @@ describe("createSignInEventHandler should create signInEventHandler", () => { }); expect(dbGroupMembers?.groupId).toBe("1"); }); + test("should not synchronize groups when AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT is enabled", async () => { + // Arrange + mockEnv.AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT = true; + const db = createDb(); + await createUserAsync(db); + await createGroupAsync(db); + const eventHandler = createSignInEventHandler(db); + + // Act + await eventHandler?.({ + user: { id: "1", name: "test" }, + profile: { preferred_username: "test", someRandomGroupsKey: ["test"] }, + account: null, + }); + + // Assert + const dbGroupMembers = await db.query.groupMembers.findFirst({ + where: eq(groupMembers.userId, "1"), + }); + expect(dbGroupMembers).toBeUndefined(); + + // Cleanup + mockEnv.AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT = false; + }); }); test.each([ ["ldap" as const, { name: "test-new" }, undefined], diff --git a/packages/integrations/test/technitium.spec.ts b/packages/integrations/test/technitium.spec.ts index e8750af0b..99c9d2610 100644 --- a/packages/integrations/test/technitium.spec.ts +++ b/packages/integrations/test/technitium.spec.ts @@ -2,7 +2,15 @@ import type { StartedTestContainer } from "testcontainers"; import { GenericContainer, Wait } from "testcontainers"; -import { beforeAll, beforeEach, afterAll, describe, expect, test, vi } from "vitest"; +import { + beforeAll, + beforeEach, + afterAll, + describe, + expect, + test, + vi, +} from "vitest"; import { createDb } from "@homarr/db/test"; @@ -23,10 +31,15 @@ vi.mock("@homarr/db", async (importActual) => { vi.mock("@homarr/core/infrastructure/certificates", async (importActual) => { // eslint-disable-next-line @typescript-eslint/consistent-type-imports - const actual = await importActual(); + const actual = + await importActual< + typeof import("@homarr/core/infrastructure/certificates") + >(); return { ...actual, - getTrustedCertificateHostnamesAsync: vi.fn().mockImplementation(() => Promise.resolve([])), + getTrustedCertificateHostnamesAsync: vi + .fn() + .mockImplementation(() => Promise.resolve([])), }; }); @@ -77,7 +90,9 @@ const INTEGRATION_ID = "test-technitium"; const makeInput = ( baseUrl: string, - auth: { kind: "credentials"; password?: string } | { kind: "apiKey"; value: string }, + auth: + | { kind: "credentials"; password?: string } + | { kind: "apiKey"; value: string }, ): IntegrationInput => ({ id: INTEGRATION_ID, name: "Technitium DNS", @@ -94,7 +109,10 @@ const makeInput = ( // Fetch an API token appropriate for the server version. // v15 has a dedicated createToken endpoint; legacy reuses the session token from login. -async function fetchApiToken(baseUrl: string, apiVersion: TechnitiumVersion): Promise { +async function fetchApiToken( + baseUrl: string, + apiVersion: TechnitiumVersion, +): Promise { if (apiVersion === "v15") { // v15 exposes a dedicated endpoint for non-expiring API tokens. const res = await fetch( @@ -104,7 +122,9 @@ async function fetchApiToken(baseUrl: string, apiVersion: TechnitiumVersion): Pr } // Legacy: /api/user/login exists on all tested versions (v11–v14) and returns a session // token that can be used as a long-lived API key via the ?token= query parameter. - const res = await fetch(`${baseUrl}/api/user/login?user=${DEFAULT_USERNAME}&pass=${DEFAULT_PASSWORD}`); + const res = await fetch( + `${baseUrl}/api/user/login?user=${DEFAULT_USERNAME}&pass=${DEFAULT_PASSWORD}`, + ); return ((await res.json()) as { token: string }).token; } @@ -124,12 +144,16 @@ const pool: Record = {}; beforeAll(async () => { await Promise.all( VERSIONS.map(async ({ tag, apiVersion }) => { - const container = await new GenericContainer(`technitium/dns-server:${tag}`) + const container = await new GenericContainer( + `technitium/dns-server:${tag}`, + ) .withExposedPorts(5380) // Explicitly set the admin password so all versions use the same credentials. // Without this, older versions may generate a random password on first run. .withEnvironment({ DNS_SERVER_ADMIN_PASSWORD: DEFAULT_PASSWORD }) - .withWaitStrategy(Wait.forLogMessage("Technitium DNS Server was started successfully.")) + .withWaitStrategy( + Wait.forLogMessage("Technitium DNS Server was started successfully."), + ) .start(); const baseUrl = `http://${container.getHost()}:${container.getMappedPort(5380)}`; const apiToken = await fetchApiToken(baseUrl, apiVersion); @@ -139,203 +163,238 @@ beforeAll(async () => { }, 120_000); afterAll(async () => { - await Promise.all(Object.values(pool).map(({ container }) => container.stop())); + await Promise.all( + Object.values(pool).map(({ container }) => container.stop()), + ); }); // ─── version-parameterised suites ──────────────────────────────────────────── -describe.each(VERSIONS)("Technitium DNS $tag ($apiVersion)", ({ tag, apiVersion }) => { - let baseUrl: string; - let apiToken: string; +describe.each(VERSIONS)( + "Technitium DNS $tag ($apiVersion)", + ({ tag, apiVersion }) => { + let baseUrl: string; + let apiToken: string; - beforeAll(() => { - const entry = pool[tag]; - if (!entry) throw new Error(`Container pool entry for ${tag} was not initialized`); - ({ baseUrl, apiToken } = entry); - }); + beforeAll(() => { + const entry = pool[tag]; + if (!entry) + throw new Error(`Container pool entry for ${tag} was not initialized`); + ({ baseUrl, apiToken } = entry); + }); - beforeEach(() => { - sessionState[INTEGRATION_ID] = null; - }); + beforeEach(() => { + sessionState[INTEGRATION_ID] = null; + }); - // ── factory ──────────────────────────────────────────────────────────────── + // ── factory ──────────────────────────────────────────────────────────────── - describe("createTechnitiumDnsIntegrationAsync (factory)", () => { - test("factory creates a working integration (auto-detects version)", async () => { - const integration = await createTechnitiumDnsIntegrationAsync(makeInput(baseUrl, { kind: "credentials" })); + describe("createTechnitiumDnsIntegrationAsync (factory)", () => { + test("factory creates a working integration (auto-detects version)", async () => { + const integration = await createTechnitiumDnsIntegrationAsync( + makeInput(baseUrl, { kind: "credentials" }), + ); - const result = await integration.getSummaryAsync(); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("enabled"); - expect(result.dnsQueriesToday).toBeGreaterThanOrEqual(0); - }, 20_000); - }); + expect(result.status).toBe("enabled"); + expect(result.dnsQueriesToday).toBeGreaterThanOrEqual(0); + }, 20_000); + }); - // ── getSummaryAsync ──────────────────────────────────────────────────────── + // ── getSummaryAsync ──────────────────────────────────────────────────────── - describe("getSummaryAsync", () => { - test("returns valid summary with credentials", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + describe("getSummaryAsync", () => { + test("returns valid summary with credentials", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - const result = await integration.getSummaryAsync(); + const result = await integration.getSummaryAsync(); - expect(result.dnsQueriesToday).toBeGreaterThanOrEqual(0); - expect(result.adsBlockedToday).toBeGreaterThanOrEqual(0); - expect(result.adsBlockedTodayPercentage).toBeGreaterThanOrEqual(0); - expect(result.domainsBeingBlocked).toBeGreaterThanOrEqual(0); - expect(result.status).toBe("enabled"); - }, 20_000); + expect(result.dnsQueriesToday).toBeGreaterThanOrEqual(0); + expect(result.adsBlockedToday).toBeGreaterThanOrEqual(0); + expect(result.adsBlockedTodayPercentage).toBeGreaterThanOrEqual(0); + expect(result.domainsBeingBlocked).toBeGreaterThanOrEqual(0); + expect(result.status).toBe("enabled"); + }, 20_000); - test("returns valid summary with API key", async () => { - const integration = new TechnitiumDnsIntegration( - makeInput(baseUrl, { kind: "apiKey", value: apiToken }), - apiVersion, - ); + test("returns valid summary with API key", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "apiKey", value: apiToken }), + apiVersion, + ); - const result = await integration.getSummaryAsync(); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("enabled"); - expect(result.dnsQueriesToday).toBeGreaterThanOrEqual(0); - }, 20_000); + expect(result.status).toBe("enabled"); + expect(result.dnsQueriesToday).toBeGreaterThanOrEqual(0); + }, 20_000); - test("domainsBeingBlocked is a non-negative number (blockedZones + blockListZones)", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + test("domainsBeingBlocked is a non-negative number (blockedZones + blockListZones)", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - const result = await integration.getSummaryAsync(); + const result = await integration.getSummaryAsync(); - expect(typeof result.domainsBeingBlocked).toBe("number"); - expect(result.domainsBeingBlocked).toBeGreaterThanOrEqual(0); - }, 20_000); - }); + expect(typeof result.domainsBeingBlocked).toBe("number"); + expect(result.domainsBeingBlocked).toBeGreaterThanOrEqual(0); + }, 20_000); + }); - // ── session caching ──────────────────────────────────────────────────────── + // ── session caching ──────────────────────────────────────────────────────── - describe("session caching", () => { - test("login is performed once and token is cached for subsequent calls", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + describe("session caching", () => { + test("login is performed once and token is cached for subsequent calls", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - await integration.getSummaryAsync(); - const tokenAfterFirst = sessionState[INTEGRATION_ID]?.token; - expect(tokenAfterFirst).toBeDefined(); + await integration.getSummaryAsync(); + const tokenAfterFirst = sessionState[INTEGRATION_ID]?.token; + expect(tokenAfterFirst).toBeDefined(); - await integration.getSummaryAsync(); - const tokenAfterSecond = sessionState[INTEGRATION_ID]?.token; + await integration.getSummaryAsync(); + const tokenAfterSecond = sessionState[INTEGRATION_ID]?.token; - expect(tokenAfterSecond).toBe(tokenAfterFirst); - }, 20_000); + expect(tokenAfterSecond).toBe(tokenAfterFirst); + }, 20_000); - test("recovers from an expired/invalid stored token by re-logging in", async () => { - // Plant a bad token with the correct version so it hits the right API path before failing - sessionState[INTEGRATION_ID] = { token: "deliberately-invalid-token", version: apiVersion }; + test("recovers from an expired/invalid stored token by re-logging in", async () => { + // Plant a bad token with the correct version so it hits the right API path before failing + sessionState[INTEGRATION_ID] = { + token: "deliberately-invalid-token", + version: apiVersion, + }; - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" }), apiVersion); + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + apiVersion, + ); - const result = await integration.getSummaryAsync(); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("enabled"); - expect(sessionState[INTEGRATION_ID]).not.toBeNull(); - expect(sessionState[INTEGRATION_ID]?.token).not.toBe("deliberately-invalid-token"); - }, 20_000); - }); + expect(result.status).toBe("enabled"); + expect(sessionState[INTEGRATION_ID]).not.toBeNull(); + expect(sessionState[INTEGRATION_ID]?.token).not.toBe( + "deliberately-invalid-token", + ); + }, 20_000); + }); - // ── blocking state changes ───────────────────────────────────────────────── + // ── blocking state changes ───────────────────────────────────────────────── - describe("blocking state changes", () => { - test("disableAsync permanently disables blocking", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + describe("blocking state changes", () => { + test("disableAsync permanently disables blocking", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - await integration.disableAsync(); - const result = await integration.getSummaryAsync(); + await integration.disableAsync(); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("disabled"); + expect(result.status).toBe("disabled"); - await integration.enableAsync(); // restore - }, 20_000); + await integration.enableAsync(); // restore + }, 20_000); - test("enableAsync re-enables blocking after permanent disable", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + test("enableAsync re-enables blocking after permanent disable", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - await integration.disableAsync(); - await integration.enableAsync(); - const result = await integration.getSummaryAsync(); + await integration.disableAsync(); + await integration.enableAsync(); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("enabled"); - }, 20_000); + expect(result.status).toBe("enabled"); + }, 20_000); - test("disableAsync with duration temporarily disables blocking", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + test("disableAsync with duration temporarily disables blocking", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - await integration.disableAsync(120); // 120s → 2 min - const result = await integration.getSummaryAsync(); + await integration.disableAsync(120); // 120s → 2 min + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("disabled"); + expect(result.status).toBe("disabled"); - await integration.enableAsync(); // restore - }, 20_000); + await integration.enableAsync(); // restore + }, 20_000); - test("disableAsync with small duration (1s) rounds up to 1 minute and disables", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + test("disableAsync with small duration (1s) rounds up to 1 minute and disables", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - await integration.disableAsync(1); - const result = await integration.getSummaryAsync(); + await integration.disableAsync(1); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("disabled"); + expect(result.status).toBe("disabled"); - await integration.enableAsync(); // restore - }, 20_000); + await integration.enableAsync(); // restore + }, 20_000); - test("enableAsync cancels an active timed disable immediately", async () => { - const integration = new TechnitiumDnsIntegration(makeInput(baseUrl, { kind: "credentials" })); + test("enableAsync cancels an active timed disable immediately", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ); - await integration.disableAsync(600); // 10 minutes - await integration.enableAsync(); - const result = await integration.getSummaryAsync(); + await integration.disableAsync(600); // 10 minutes + await integration.enableAsync(); + const result = await integration.getSummaryAsync(); - expect(result.status).toBe("enabled"); - }, 20_000); - }); + expect(result.status).toBe("enabled"); + }, 20_000); + }); - // ── testConnectionAsync ──────────────────────────────────────────────────── + // ── testConnectionAsync ──────────────────────────────────────────────────── - describe("testConnectionAsync", () => { - test("succeeds with valid credentials", async () => { - const result = await new TechnitiumDnsIntegration( - makeInput(baseUrl, { kind: "credentials" }), - ).testConnectionAsync(); - expect(result.success).toBe(true); - }, 20_000); + describe("testConnectionAsync", () => { + test("succeeds with valid credentials", async () => { + const result = await new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "credentials" }), + ).testConnectionAsync(); + expect(result.success).toBe(true); + }, 20_000); - test("succeeds with valid API key", async () => { - const result = await new TechnitiumDnsIntegration( - makeInput(baseUrl, { kind: "apiKey", value: apiToken }), - apiVersion, - ).testConnectionAsync(); - expect(result.success).toBe(true); - }, 20_000); + test("succeeds with valid API key", async () => { + const result = await new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "apiKey", value: apiToken }), + apiVersion, + ).testConnectionAsync(); + expect(result.success).toBe(true); + }, 20_000); - test("fails with wrong password — returns authorization error", async () => { - const integration = new TechnitiumDnsIntegration( - makeInput(baseUrl, { kind: "credentials", password: "wrong-password" }), - ); + test("fails with wrong password — returns authorization error", async () => { + const integration = new TechnitiumDnsIntegration( + makeInput(baseUrl, { + kind: "credentials", + password: "wrong-password", + }), + ); - const result = await integration.testConnectionAsync(); + const result = await integration.testConnectionAsync(); - expect(result.success).toBe(false); - if (result.success) return; - expect(result.error).toBeInstanceOf(TestConnectionError); - expect(result.error.type).toBe("authorization"); - }, 20_000); + expect(result.success).toBe(false); + if (result.success) return; + expect(result.error).toBeInstanceOf(TestConnectionError); + expect(result.error.type).toBe("authorization"); + }, 20_000); - test("fails with invalid API key — returns authorization error", async () => { - const result = await new TechnitiumDnsIntegration( - makeInput(baseUrl, { kind: "apiKey", value: "invalid-api-key-xyz" }), - apiVersion, - ).testConnectionAsync(); + test("fails with invalid API key — returns authorization error", async () => { + const result = await new TechnitiumDnsIntegration( + makeInput(baseUrl, { kind: "apiKey", value: "invalid-api-key-xyz" }), + apiVersion, + ).testConnectionAsync(); - expect(result.success).toBe(false); - if (result.success) return; - expect(result.error).toBeInstanceOf(TestConnectionError); - expect(result.error.type).toBe("authorization"); - }, 20_000); - }); -}); + expect(result.success).toBe(false); + if (result.success) return; + expect(result.error).toBeInstanceOf(TestConnectionError); + expect(result.error.type).toBe("authorization"); + }, 20_000); + }); + }, +);