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 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/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", }, 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/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/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/docs/README.md b/docs/README.md index cdebccc8f..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
@@ -165,6 +172,13 @@
+ + Gotify +
+

Gotify

+
+ + Home Assistant
@@ -191,8 +205,8 @@

Jellyfin

- - + + Jellyseerr
@@ -205,8 +219,8 @@

Lidarr

- - + + LinuxServer.io
@@ -240,8 +254,8 @@

ntfy

- - + + NZBGet
@@ -254,8 +268,8 @@

OpenMediaVault

- - + + OPNsense
@@ -289,8 +303,8 @@

Pi-hole

- - + + Plex
@@ -303,8 +317,8 @@

Prowlarr

- - + + Proxmox
@@ -338,8 +352,8 @@

Readarr

- - + + SABnzbd
@@ -352,8 +366,8 @@

Search.ch

- - + + Seerr
@@ -387,8 +401,8 @@

Tdarr

- - + + Technitium DNS
@@ -401,8 +415,8 @@

Tracearr

- - + + Transmission
@@ -436,8 +450,8 @@

Unraid

- - + + Uptime Kuma
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": { 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/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/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/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 5a91bef72..8efa552e4 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"]], @@ -482,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", @@ -589,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 f2d9547fc..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"; @@ -35,6 +36,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 +123,7 @@ export const integrationCreators = { ical: ICalIntegration, quay: QuayIntegration, ntfy: NTFYIntegration, + gotify: GotifyIntegration, mock: MockIntegration, truenas: TrueNasIntegration, unraid: UnraidIntegration, @@ -134,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/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/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..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"; @@ -17,6 +18,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"; @@ -73,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/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(); + }); +}); 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); + }); + }, +); 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/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/en.json b/packages/translation/src/lang/en.json index 381bbeca1..e59c38dae 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -1746,6 +1746,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/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": { 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, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7797baf4d..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 @@ -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.18 + version: 2.9.18 '@types/adm-zip': specifier: 0.5.8 version: 0.5.8 @@ -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 @@ -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.18 + version: 2.9.18 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -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' @@ -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 @@ -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: @@ -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 @@ -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) @@ -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 @@ -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)) @@ -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) @@ -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 @@ -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] @@ -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==} @@ -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'} @@ -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==} @@ -14447,6 +14444,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 @@ -14489,8 +14487,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: @@ -14546,9 +14544,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==} @@ -16295,12 +16290,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: @@ -18320,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': {} @@ -18376,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) @@ -18581,12 +18576,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)': @@ -18602,17 +18597,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)': @@ -18711,9 +18706,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': @@ -18918,10 +18913,10 @@ 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 + form-data: 4.0.6 hpagent: 1.2.0 isomorphic-ws: 5.0.0(ws@8.20.1) js-yaml: 4.1.1 @@ -19005,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) @@ -19030,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 @@ -21213,29 +21208,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': @@ -21251,7 +21246,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: @@ -21279,14 +21274,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': {} @@ -21425,13 +21420,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': @@ -21511,7 +21506,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)': @@ -21524,19 +21519,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 @@ -21635,7 +21630,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': {} @@ -21656,11 +21651,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': {} @@ -21732,7 +21727,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: @@ -21743,15 +21738,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 @@ -21779,7 +21784,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: @@ -22223,7 +22228,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 @@ -22329,10 +22334,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) @@ -22351,13 +22356,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' @@ -23567,6 +23572,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) @@ -23663,7 +23681,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: {} @@ -23842,7 +23860,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: {} @@ -24191,7 +24209,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: {} @@ -24260,7 +24286,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: {} @@ -24436,10 +24462,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 @@ -25514,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) @@ -26217,9 +26239,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 @@ -26270,6 +26292,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 @@ -27531,7 +27566,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: @@ -28870,8 +28905,6 @@ snapshots: symbol-tree@3.2.4: {} - tabbable@6.2.0: {} - tabbable@6.4.0: {} tagged-tag@1.0.0: {} @@ -29104,22 +29137,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: @@ -29187,14 +29220,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: {} @@ -29236,8 +29269,6 @@ snapshots: undici-types@5.26.5: {} - undici-types@7.16.0: {} - undici-types@7.18.2: {} undici@6.23.0: {} @@ -29538,10 +29569,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 @@ -29569,6 +29619,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 a11bec68a..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 @@ -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.18 "@types/adm-zip": 0.5.8 "@types/bcrypt": 6.0.0 "@types/better-sqlite3": 7.6.13 @@ -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 @@ -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.18 typescript: ^6.0.3 vite: ^8.0.16 undici: 7.27.2 @@ -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" 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