diff --git a/app/client/lib/auth-errors.ts b/app/client/lib/auth-errors.ts index 025ac021..a2f0f4c6 100644 --- a/app/client/lib/auth-errors.ts +++ b/app/client/lib/auth-errors.ts @@ -5,43 +5,26 @@ export type LoginErrorCode = | "BANNED_USER" | "SSO_LOGIN_FAILED"; +const VALID_ERROR_CODES: Set = new Set([ + "ACCOUNT_LINK_REQUIRED", + "EMAIL_NOT_VERIFIED", + "INVITE_REQUIRED", + "BANNED_USER", + "SSO_LOGIN_FAILED", +]); + export function decodeLoginError(error?: string): LoginErrorCode | null { if (!error) { return null; } - let decoded = ""; + const code = decodeURIComponent(error); - try { - decoded = decodeURIComponent(error); - } catch { - decoded = error; + if (VALID_ERROR_CODES.has(code as LoginErrorCode)) { + return code as LoginErrorCode; } - decoded = decoded.toLowerCase().replace(/[-_\s]+/g, "_"); - - if (decoded.includes("account_not_linked")) { - return "ACCOUNT_LINK_REQUIRED"; - } - - if (decoded.includes("email_not_verified")) { - return "EMAIL_NOT_VERIFIED"; - } - - if (decoded.includes("banned_user") || decoded.includes("banned")) { - return "BANNED_USER"; - } - - if ( - decoded.includes("access_denied") || - decoded.includes("must_be_invited") || - decoded.includes("unable_to_create_session") || - decoded.includes("invite") - ) { - return "INVITE_REQUIRED"; - } - - return "SSO_LOGIN_FAILED"; + return null; } export function getLoginErrorDescription(errorCode: LoginErrorCode | null): string | null { diff --git a/app/client/modules/auth/routes/__tests__/login.test.tsx b/app/client/modules/auth/routes/__tests__/login.test.tsx index f8f56678..88bf7faa 100644 --- a/app/client/modules/auth/routes/__tests__/login.test.tsx +++ b/app/client/modules/auth/routes/__tests__/login.test.tsx @@ -1,5 +1,5 @@ -import { describe, expect, mock, test } from "bun:test"; -import { render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, mock, test } from "bun:test"; +import { cleanup, render, screen } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; await mock.module("@tanstack/react-router", () => ({ @@ -28,30 +28,98 @@ await mock.module("~/client/lib/auth-client", () => ({ import { LoginPage } from "../login"; -const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false } } }); +const createTestQueryClient = () => + new QueryClient({ + defaultOptions: { + queries: { + retry: false, + gcTime: Infinity, + }, + mutations: { + gcTime: Infinity, + }, + }, + }); const inviteOnlyMessage = "Access is invite-only. Ask an organization admin to send you an invitation before signing in with SSO."; +afterEach(() => { + cleanup(); +}); + describe("LoginPage", () => { - test("shows an invite-only message when SSO returns access_denied", async () => { + test("shows an invite-only message when SSO returns INVITE_REQUIRED code", async () => { const queryClient = createTestQueryClient(); render( - + , ); expect(await screen.findByText(inviteOnlyMessage)).toBeTruthy(); }); - test("shows an invite-only message for URL-encoded invitation errors", async () => { + test("shows account link required message when SSO returns ACCOUNT_LINK_REQUIRED code", async () => { const queryClient = createTestQueryClient(); render( - + , ); - expect(await screen.findByText(inviteOnlyMessage)).toBeTruthy(); + expect( + await screen.findByText( + "Your account exists but is not linked to this SSO provider. Sign in with username/password first, then enable auto linking in your provider settings or contact your administrator.", + ), + ).toBeTruthy(); + }); + + test("shows banned message when SSO returns BANNED_USER code", async () => { + const queryClient = createTestQueryClient(); + render( + + + , + ); + + expect( + await screen.findByText( + "You have been banned from this application. Please contact support if you believe this is an error.", + ), + ).toBeTruthy(); + }); + + test("shows email not verified message when SSO returns EMAIL_NOT_VERIFIED code", async () => { + const queryClient = createTestQueryClient(); + render( + + + , + ); + + expect(await screen.findByText("Your identity provider did not mark your email as verified.")).toBeTruthy(); + }); + + test("shows generic SSO error message when SSO returns SSO_LOGIN_FAILED code", async () => { + const queryClient = createTestQueryClient(); + render( + + + , + ); + + expect(await screen.findByText("SSO authentication failed. Please try again.")).toBeTruthy(); + }); + + test("does not show error message for invalid error codes", async () => { + const queryClient = createTestQueryClient(); + render( + + + , + ); + + expect(await screen.findByText("Login to your account")).toBeTruthy(); + expect(screen.queryByText(inviteOnlyMessage)).toBeNull(); }); }); diff --git a/app/client/modules/auth/routes/login.tsx b/app/client/modules/auth/routes/login.tsx index e9f05d80..b4b01cd6 100644 --- a/app/client/modules/auth/routes/login.tsx +++ b/app/client/modules/auth/routes/login.tsx @@ -135,7 +135,7 @@ export function LoginPage({ error }: LoginPageProps = {}) { const { data, error } = await authClient.signIn.sso({ providerId: providerId, callbackURL: callbackPath, - errorCallbackURL: callbackPath, + errorCallbackURL: "/api/v1/auth/login-error", }); if (error) throw error; diff --git a/app/server/__tests__/isolation.test.ts b/app/server/__tests__/isolation.test.ts index d294122c..020c7853 100644 --- a/app/server/__tests__/isolation.test.ts +++ b/app/server/__tests__/isolation.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; +import { createTestSession } from "~/test/helpers/auth"; import { db } from "~/server/db/db"; import { repositoriesTable, @@ -39,14 +39,13 @@ describe("multi-organization isolation", () => { }); // Force the session to point at the foreign organization - const rawSessionToken = decodeURIComponent(session.token).split(".")[0]; await db .update(sessionsTable) .set({ activeOrganizationId: foreignOrgId }) - .where(eq(sessionsTable.id, rawSessionToken)); + .where(eq(sessionsTable.id, session.session.id)); const res = await app.request("/api/v1/repositories", { - headers: getAuthHeaders(session.token), + headers: session.headers, }); expect(res.status).toBe(403); @@ -72,7 +71,7 @@ describe("multi-organization isolation", () => { }); const res = await app.request(`/api/v1/repositories/${repoShortId}`, { - headers: getAuthHeaders(session2.token), + headers: session2.headers, }); expect(res.status).toBe(404); @@ -80,7 +79,7 @@ describe("multi-organization isolation", () => { expect(body.message).toBe("Repository not found"); const resOk = await app.request(`/api/v1/repositories/${repoShortId}`, { - headers: getAuthHeaders(session1.token), + headers: session1.headers, }); expect(resOk.status).toBe(200); }); @@ -108,19 +107,19 @@ describe("multi-organization isolation", () => { }); const res1 = await app.request("/api/v1/repositories", { - headers: getAuthHeaders(session1.token), + headers: session1.headers, }); const list1 = await res1.json(); expect(list1.length).toBeGreaterThanOrEqual(1); - expect(list1.some((r: any) => r.name === "Org 2 Repo")).toBe(false); + expect(list1.some((r: { name: string }) => r.name === "Org 2 Repo")).toBe(false); const res2 = await app.request("/api/v1/repositories", { - headers: getAuthHeaders(session2.token), + headers: session2.headers, }); const list2 = await res2.json(); - expect(list2.some((r: any) => r.name === "Org 1 Repo")).toBe(false); - expect(list2.some((r: any) => r.name === "Org 2 Repo")).toBe(true); + expect(list2.some((r: { name: string }) => r.name === "Org 1 Repo")).toBe(false); + expect(list2.some((r: { name: string }) => r.name === "Org 2 Repo")).toBe(true); }); test("should not be able to access volumes from another organization", async () => { @@ -140,7 +139,7 @@ describe("multi-organization isolation", () => { }); const res = await app.request(`/api/v1/volumes/${volumeShortId}`, { - headers: getAuthHeaders(session2.token), + headers: session2.headers, }); expect(res.status).toBe(404); @@ -174,7 +173,7 @@ describe("multi-organization isolation", () => { const res = await app.request("/api/v1/backups", { method: "POST", headers: { - ...getAuthHeaders(session2.token), + ...session2.headers, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -226,13 +225,13 @@ describe("multi-organization isolation", () => { .returning(); const res = await app.request(`/api/v1/backups/${schedule.shortId}`, { - headers: getAuthHeaders(session2.token), + headers: session2.headers, }); expect(res.status).toBe(404); const resOk = await app.request(`/api/v1/backups/${schedule.shortId}`, { - headers: getAuthHeaders(session1.token), + headers: session1.headers, }); expect(resOk.status).toBe(200); }); @@ -295,14 +294,14 @@ describe("multi-organization isolation", () => { }); const resGet = await app.request(`/api/v1/backups/${schedule.shortId}/notifications`, { - headers: getAuthHeaders(session2.token), + headers: session2.headers, }); expect(resGet.status).toBe(404); const resPut = await app.request(`/api/v1/backups/${schedule.shortId}/notifications`, { method: "PUT", headers: { - ...getAuthHeaders(session2.token), + ...session2.headers, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/lib/auth.ts b/app/server/lib/auth.ts index 466557c1..bbcf61f3 100644 --- a/app/server/lib/auth.ts +++ b/app/server/lib/auth.ts @@ -7,11 +7,13 @@ import { type User, } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { admin, createAuthMiddleware, twoFactor, username, organization } from "better-auth/plugins"; +import { admin, twoFactor, username, organization, testUtils } from "better-auth/plugins"; +import { createAuthMiddleware } from "better-auth/api"; import { sso } from "@better-auth/sso"; import { config } from "../core/config"; import { db } from "../db/db"; import { cryptoUtils } from "../utils/crypto"; +import { logger } from "../utils/logger"; import { authService } from "../modules/auth/auth.service"; import { tanstackStartCookies } from "better-auth/tanstack-start"; import { isValidUsername, normalizeUsername } from "~/lib/username"; @@ -21,13 +23,24 @@ import { validateSsoCallbackUrls } from "./auth/middlewares/validate-sso-callbac import { validateSsoProviderId } from "./auth/middlewares/validate-sso-provider-id"; import { createUserDefaultOrg } from "./auth/helpers/create-default-org"; import { isSsoCallbackRequest, requireSsoInvitation } from "./auth/middlewares/require-sso-invitation"; -import { ssoTrustedProviderLinkingPlugin } from "./auth/plugins/sso-trusted-provider-linking"; +import { resolveTrustedProvidersForRequest } from "./auth/middlewares/trust-sso-provider-for-linking"; +import { buildAllowedHosts } from "./auth/base-url"; export type AuthMiddlewareContext = MiddlewareContext>; +const authOrigins = [config.baseUrl, ...config.trustedOrigins]; +const { allowedHosts, invalidOrigins } = buildAllowedHosts(authOrigins); + +for (const origin of invalidOrigins) { + logger.warn(`Ignoring invalid auth origin in configuration: ${origin}`); +} + export const auth = betterAuth({ secret: await cryptoUtils.deriveSecret("better-auth"), - baseURL: config.baseUrl, + baseURL: { + allowedHosts, + protocol: "auto", + }, trustedOrigins: config.trustedOrigins, advanced: { cookiePrefix: "zerobyte", @@ -91,6 +104,7 @@ export const auth = betterAuth({ account: { accountLinking: { enabled: true, + trustedProviders: resolveTrustedProvidersForRequest, }, }, user: { @@ -132,7 +146,6 @@ export const auth = betterAuth({ defaultRole: "member", }, }), - ssoTrustedProviderLinkingPlugin(), twoFactor({ backupCodeOptions: { storeBackupCodes: "encrypted", @@ -140,5 +153,6 @@ export const auth = betterAuth({ }, }), tanstackStartCookies(), + ...(process.env.NODE_ENV === "test" ? [testUtils()] : []), ], }); diff --git a/app/server/lib/auth/base-url.ts b/app/server/lib/auth/base-url.ts new file mode 100644 index 00000000..f80c8314 --- /dev/null +++ b/app/server/lib/auth/base-url.ts @@ -0,0 +1,22 @@ +type AllowedHostsResult = { + allowedHosts: string[]; + invalidOrigins: string[]; +}; + +export function buildAllowedHosts(origins: string[]): AllowedHostsResult { + const validHosts = new Set(); + const invalidOrigins: string[] = []; + + for (const origin of origins) { + try { + validHosts.add(new URL(origin).host); + } catch { + invalidOrigins.push(origin); + } + } + + return { + allowedHosts: Array.from(validHosts), + invalidOrigins, + }; +} diff --git a/app/server/lib/auth/middlewares/__tests__/trust-sso-provider-for-linking.test.ts b/app/server/lib/auth/middlewares/__tests__/trust-sso-provider-for-linking.test.ts index 6c487414..925ba218 100644 --- a/app/server/lib/auth/middlewares/__tests__/trust-sso-provider-for-linking.test.ts +++ b/app/server/lib/auth/middlewares/__tests__/trust-sso-provider-for-linking.test.ts @@ -1,9 +1,8 @@ import { beforeEach, describe, expect, test } from "bun:test"; -import type { GenericEndpointContext } from "@better-auth/core"; import { eq } from "drizzle-orm"; import { db } from "~/server/db/db"; import { account, member, organization, ssoProvider, usersTable } from "~/server/db/schema"; -import { isSsoCallbackPath, trustSsoProviderForLinking } from "../trust-sso-provider-for-linking"; +import { resolveTrustedProvidersForRequest } from "../trust-sso-provider-for-linking"; function randomId() { return Bun.randomUUIDv7(); @@ -13,38 +12,8 @@ function randomSlug(prefix: string) { return `${prefix}-${Math.random().toString(36).slice(2, 8)}`; } -function createMockContext(options: { - path: string; - method?: string; - params?: Record; - trustedProviders?: string[]; - enabled?: boolean; -}): GenericEndpointContext { - const { path, method = "GET", params = {}, trustedProviders = [], enabled = true } = options; - - const accountLinking = { - enabled, - trustedProviders, - }; - - const context = { - options: { - account: { - accountLinking, - }, - }, - }; - - return { - path, - body: {}, - query: {}, - headers: new Headers(), - request: new Request(`http://test.local${path}`, { method }), - params, - method, - context: context as GenericEndpointContext["context"], - } as GenericEndpointContext; +function createRequest(path: string): Request { + return new Request(`http://test.local${path}`); } async function createSsoProviderRecord( @@ -86,18 +55,7 @@ async function createSsoProviderRecord( return { organizationId, userId }; } -describe("isSsoCallbackPath", () => { - test("detects OIDC callback paths", () => { - expect(isSsoCallbackPath("/sso/callback/pocket-id")).toBe(true); - }); - - test("ignores non-callback paths", () => { - expect(isSsoCallbackPath("/sso/register")).toBe(false); - expect(isSsoCallbackPath("/sign-in/email")).toBe(false); - }); -}); - -describe("trustSsoProviderForLinking", () => { +describe("resolveTrustedProvidersForRequest", () => { beforeEach(async () => { await db.delete(member); await db.delete(account); @@ -106,110 +64,61 @@ describe("trustSsoProviderForLinking", () => { await db.delete(usersTable); }); - test("adds callback provider to trusted providers", async () => { + test("returns [] when request is missing", async () => { + expect(await resolveTrustedProvidersForRequest()).toEqual([]); + }); + + test("returns [] for non-callback paths", async () => { + expect(await resolveTrustedProvidersForRequest(createRequest("/sign-in/email"))).toEqual([]); + }); + + test("returns [] for unknown providers", async () => { + expect(await resolveTrustedProvidersForRequest(createRequest("/sso/callback/missing-provider"))).toEqual([]); + }); + + test("returns auto-link-enabled providers from the callback provider organization", async () => { + const { organizationId, userId } = await createSsoProviderRecord("pocket-id", true); + + await createSsoProviderRecord("acme-saml", true, { organizationId, userId }); + await createSsoProviderRecord("acme-disabled", false, { organizationId, userId }); + await createSsoProviderRecord("other-org-provider", true); + + const trustedProviders = await resolveTrustedProvidersForRequest(createRequest("/sso/callback/pocket-id")); + + expect([...trustedProviders].sort()).toEqual(["acme-saml", "pocket-id"]); + }); + + test("supports /sso/saml2/callback/:providerId paths", async () => { + await createSsoProviderRecord("saml-provider", true); + + expect(await resolveTrustedProvidersForRequest(createRequest("/sso/saml2/callback/saml-provider"))).toEqual([ + "saml-provider", + ]); + }); + + test("supports callback paths nested under /api/auth", async () => { + await createSsoProviderRecord("prefixed-provider", true); + + expect(await resolveTrustedProvidersForRequest(createRequest("/api/auth/sso/callback/prefixed-provider"))).toEqual([ + "prefixed-provider", + ]); + }); + + test("supports /sso/saml2/sp/acs/:providerId paths", async () => { + await createSsoProviderRecord("saml-acs-provider", true); + + expect(await resolveTrustedProvidersForRequest(createRequest("/sso/saml2/sp/acs/saml-acs-provider"))).toEqual([ + "saml-acs-provider", + ]); + }); + + test("removes providers from the result when auto-linking is disabled", async () => { await createSsoProviderRecord("pocket-id", true); - const ctx = createMockContext({ - path: "/sso/callback/pocket-id", - params: { providerId: "pocket-id" }, - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toContain("pocket-id"); - }); - - test("does not trust providers with disabled auto-linking", async () => { - await createSsoProviderRecord("pocket-id", false); - - const ctx = createMockContext({ - path: "/sso/callback/pocket-id", - params: { providerId: "pocket-id" }, - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual([]); - }); - - test("replaces stale trusted providers with database state", async () => { - await createSsoProviderRecord("pocket-id", true); - - const ctx = createMockContext({ - path: "/sso/callback/pocket-id", - params: { providerId: "pocket-id" }, - trustedProviders: ["stale-provider"], - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual(["pocket-id"]); - }); - - test("does not trust unknown providers", async () => { - const ctx = createMockContext({ - path: "/sso/callback/missing-provider", - params: { providerId: "missing-provider" }, - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual([]); - }); - - test("does not duplicate an existing provider", async () => { - await createSsoProviderRecord("pocket-id", true); - - const ctx = createMockContext({ - path: "/sso/callback/pocket-id", - params: { providerId: "pocket-id" }, - trustedProviders: ["pocket-id"], - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual(["pocket-id"]); - }); - - test("removes provider from trusted providers when auto-linking is disabled", async () => { - await createSsoProviderRecord("pocket-id", true); - - const ctx = createMockContext({ - path: "/sso/callback/pocket-id", - params: { providerId: "pocket-id" }, - }); - - await trustSsoProviderForLinking(ctx); - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual(["pocket-id"]); + expect(await resolveTrustedProvidersForRequest(createRequest("/sso/callback/pocket-id"))).toEqual(["pocket-id"]); await db.update(ssoProvider).set({ autoLinkMatchingEmails: false }).where(eq(ssoProvider.providerId, "pocket-id")); - await trustSsoProviderForLinking(ctx); - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual([]); - }); - - test("does nothing when account linking is disabled", async () => { - await createSsoProviderRecord("pocket-id", true); - - const ctx = createMockContext({ - path: "/sso/callback/pocket-id", - params: { providerId: "pocket-id" }, - enabled: false, - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual([]); - }); - - test("does nothing when provider id cannot be extracted", async () => { - const ctx = createMockContext({ - path: "/sso/callback/", - params: {}, - }); - - await trustSsoProviderForLinking(ctx); - - expect(ctx.context.options.account?.accountLinking?.trustedProviders).toEqual([]); + expect(await resolveTrustedProvidersForRequest(createRequest("/sso/callback/pocket-id"))).toEqual([]); }); }); diff --git a/app/server/lib/auth/middlewares/trust-sso-provider-for-linking.ts b/app/server/lib/auth/middlewares/trust-sso-provider-for-linking.ts index 233dda2f..eb00ad5f 100644 --- a/app/server/lib/auth/middlewares/trust-sso-provider-for-linking.ts +++ b/app/server/lib/auth/middlewares/trust-sso-provider-for-linking.ts @@ -1,26 +1,14 @@ -import type { GenericEndpointContext } from "@better-auth/core"; import { db } from "~/server/db/db"; -import { extractProviderIdFromContext } from "../utils/sso-context"; +import { extractProviderIdFromUrl } from "../utils/sso-context"; -export function isSsoCallbackPath(path?: string): boolean { - if (!path) { - return false; +export async function resolveTrustedProvidersForRequest(request?: Request): Promise { + if (!request) { + return []; } - return path.startsWith("/sso/callback/"); -} - -export async function trustSsoProviderForLinking(ctx: GenericEndpointContext): Promise { - const providerId = extractProviderIdFromContext(ctx); - + const providerId = extractProviderIdFromUrl(request.url); if (!providerId) { - return; - } - - const accountLinking = ctx.context.options.account?.accountLinking; - - if (!accountLinking || accountLinking.enabled === false) { - return; + return []; } const provider = await db.query.ssoProvider.findFirst({ @@ -28,7 +16,7 @@ export async function trustSsoProviderForLinking(ctx: GenericEndpointContext): P where: { providerId }, }); if (!provider) { - return; + return []; } const autoLinkingProviders = await db.query.ssoProvider.findMany({ @@ -39,5 +27,5 @@ export async function trustSsoProviderForLinking(ctx: GenericEndpointContext): P }, }); - accountLinking.trustedProviders = autoLinkingProviders.map((entry) => entry.providerId); + return autoLinkingProviders.map((entry) => entry.providerId); } diff --git a/app/server/lib/auth/middlewares/validate-sso-callback-urls.ts b/app/server/lib/auth/middlewares/validate-sso-callback-urls.ts index 873f3cdc..48f306f2 100644 --- a/app/server/lib/auth/middlewares/validate-sso-callback-urls.ts +++ b/app/server/lib/auth/middlewares/validate-sso-callback-urls.ts @@ -29,6 +29,7 @@ export const validateSsoCallbackUrls = async (ctx: AuthMiddlewareContext) => { if (value !== undefined && (typeof value !== "string" || !isValidCallbackPath(value))) { throw new APIError("BAD_REQUEST", { message: `Invalid ${field}. Only relative paths like /login are allowed.`, + code: `INVALID_${field.toUpperCase()}`, }); } } diff --git a/app/server/lib/auth/plugins/sso-trusted-provider-linking.ts b/app/server/lib/auth/plugins/sso-trusted-provider-linking.ts deleted file mode 100644 index 196dc03b..00000000 --- a/app/server/lib/auth/plugins/sso-trusted-provider-linking.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { BetterAuthPlugin } from "better-auth"; -import { createAuthMiddleware } from "better-auth/api"; -import { isSsoCallbackPath, trustSsoProviderForLinking } from "../middlewares/trust-sso-provider-for-linking"; - -export function ssoTrustedProviderLinkingPlugin(): BetterAuthPlugin { - return { - id: "sso-trusted-provider-linking", - hooks: { - before: [ - { - matcher(context) { - return isSsoCallbackPath(context.path); - }, - handler: createAuthMiddleware(async (ctx) => { - await trustSsoProviderForLinking(ctx); - }), - }, - ], - }, - }; -} diff --git a/app/server/lib/auth/utils/sso-context.ts b/app/server/lib/auth/utils/sso-context.ts index ab36b14a..7c8edac1 100644 --- a/app/server/lib/auth/utils/sso-context.ts +++ b/app/server/lib/auth/utils/sso-context.ts @@ -1,14 +1,30 @@ import type { GenericEndpointContext } from "@better-auth/core"; +const SSO_CALLBACK_PATH_SEGMENTS = ["/sso/callback/", "/sso/saml2/callback/", "/sso/saml2/sp/acs/"] as const; + +const SSO_CALLBACK_PATH_PATTERN = /\/sso\/(?:callback|saml2\/callback|saml2\/sp\/acs)\/([^/]+)$/; + export function normalizeEmail(email: string): string { return email.trim().toLowerCase(); } +export function isSsoCallbackPath(pathname: string): boolean { + return SSO_CALLBACK_PATH_SEGMENTS.some((segment) => pathname.includes(segment)); +} + +export function extractProviderIdFromPathname(pathname: string): string | null { + if (!isSsoCallbackPath(pathname)) { + return null; + } + + const match = pathname.match(SSO_CALLBACK_PATH_PATTERN); + return match?.[1] ?? null; +} + export function extractProviderIdFromUrl(url: string): string | null { try { const pathname = new URL(url, "http://localhost").pathname; - const match = pathname.match(/\/sso\/(?:saml2\/)?callback\/([^/]+)$/); - return match?.[1] ?? null; + return extractProviderIdFromPathname(pathname); } catch { return null; } diff --git a/app/server/modules/auth/__tests__/auth.sso-security.test.ts b/app/server/modules/auth/__tests__/auth.sso-security.test.ts index feb067f9..ad93a157 100644 --- a/app/server/modules/auth/__tests__/auth.sso-security.test.ts +++ b/app/server/modules/auth/__tests__/auth.sso-security.test.ts @@ -1,9 +1,21 @@ import { beforeEach, describe, expect, test } from "bun:test"; import { createApp } from "~/server/app"; +import { config } from "~/server/core/config"; import { account, invitation, member, organization, ssoProvider, usersTable } from "~/server/db/schema"; import { db } from "~/server/db/db"; const app = createApp(); +const ssoSignInUrl = new URL("/api/auth/sign-in/sso", config.baseUrl).toString(); + +function postSsoSignIn(body: Record) { + return app.request(ssoSignInUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); +} describe("auth SSO sign-in security", () => { beforeEach(async () => { @@ -16,15 +28,9 @@ describe("auth SSO sign-in security", () => { }); test("rejects malicious callback URL", async () => { - const response = await app.request("/api/auth/sign-in/sso", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - providerId: "missing-provider", - callbackURL: "https://evil.example", - }), + const response = await postSsoSignIn({ + providerId: "missing-provider", + callbackURL: "https://evil.example", }); expect(response.status).toBe(400); @@ -35,16 +41,10 @@ describe("auth SSO sign-in security", () => { }); test("rejects malicious error callback URL", async () => { - const response = await app.request("/api/auth/sign-in/sso", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - providerId: "missing-provider", - callbackURL: "/login", - errorCallbackURL: "https://evil.example", - }), + const response = await postSsoSignIn({ + providerId: "missing-provider", + callbackURL: "/login", + errorCallbackURL: "https://evil.example", }); expect(response.status).toBe(400); @@ -55,16 +55,10 @@ describe("auth SSO sign-in security", () => { }); test("rejects malicious new user callback URL", async () => { - const response = await app.request("/api/auth/sign-in/sso", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - providerId: "missing-provider", - callbackURL: "/login", - newUserCallbackURL: "https://evil.example", - }), + const response = await postSsoSignIn({ + providerId: "missing-provider", + callbackURL: "/login", + newUserCallbackURL: "https://evil.example", }); expect(response.status).toBe(400); @@ -75,20 +69,14 @@ describe("auth SSO sign-in security", () => { }); test("allows relative callback URL to continue normal flow", async () => { - const response = await app.request("/api/auth/sign-in/sso", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - providerId: "missing-provider", - callbackURL: "/login", - }), + const response = await postSsoSignIn({ + providerId: "missing-provider", + callbackURL: "/login", }); expect(response.status).toBe(404); const body = await response.json(); - expect(body.code).toBe("NO_PROVIDER_FOUND_FOR_THE_ISSUER"); + expect(body.message).toBe("No provider found for the issuer"); }); }); diff --git a/app/server/modules/auth/auth.controller.ts b/app/server/modules/auth/auth.controller.ts index dac195f9..476c66a2 100644 --- a/app/server/modules/auth/auth.controller.ts +++ b/app/server/modules/auth/auth.controller.ts @@ -25,6 +25,8 @@ import { import { authService } from "./auth.service"; import { requireAdmin, requireAuth, requireOrgAdmin } from "./auth.middleware"; import { auth } from "~/server/lib/auth"; +import { mapAuthErrorToCode } from "./auth.errors"; +import { config } from "~/server/core/config"; export const authController = new Hono() .get("/status", getStatusDto, async (c) => { @@ -201,4 +203,9 @@ export const authController = new Hono() } return c.json({ success: true }); + }) + .get("/login-error", async (c) => { + const error = c.req.query("error"); + const errorCode = error ? mapAuthErrorToCode(error) : "SSO_LOGIN_FAILED"; + return c.redirect(`${config.baseUrl}/login?error=${errorCode}`); }); diff --git a/app/server/modules/auth/auth.errors.ts b/app/server/modules/auth/auth.errors.ts new file mode 100644 index 00000000..9837461a --- /dev/null +++ b/app/server/modules/auth/auth.errors.ts @@ -0,0 +1,55 @@ +export type LoginErrorCode = + | "ACCOUNT_LINK_REQUIRED" + | "EMAIL_NOT_VERIFIED" + | "INVITE_REQUIRED" + | "BANNED_USER" + | "SSO_LOGIN_FAILED"; + +const INVITE_REQUIRED_ERRORS = new Set([ + "Access denied. You must be invited to this organization before you can sign in with SSO.", + "SSO sign-in is invite-only for this organization", + "unable to create session", +]); + +export function mapAuthErrorToCode(error: string): LoginErrorCode { + let decoded: string; + + try { + decoded = decodeURIComponent(error); + } catch { + return "SSO_LOGIN_FAILED"; + } + + if (decoded === "account not linked") { + return "ACCOUNT_LINK_REQUIRED"; + } + + if (decoded === "EMAIL_NOT_VERIFIED") { + return "EMAIL_NOT_VERIFIED"; + } + + if (decoded === "banned") { + return "BANNED_USER"; + } + + if (INVITE_REQUIRED_ERRORS.has(decoded)) { + return "INVITE_REQUIRED"; + } + + return "SSO_LOGIN_FAILED"; +} + +export function getLoginErrorDescription(errorCode: LoginErrorCode): string { + switch (errorCode) { + case "ACCOUNT_LINK_REQUIRED": + return "Your account exists but is not linked to this SSO provider. Sign in with username/password first, then enable auto linking in your provider settings or contact your administrator."; + case "EMAIL_NOT_VERIFIED": + return "Your identity provider did not mark your email as verified."; + case "INVITE_REQUIRED": + return "Access is invite-only. Ask an organization admin to send you an invitation before signing in with SSO."; + case "BANNED_USER": + return "You have been banned from this application. Please contact support if you believe this is an error."; + case "SSO_LOGIN_FAILED": + return "SSO authentication failed. Please try again."; + } +} diff --git a/app/server/modules/backups/__tests__/backups.controller.test.ts b/app/server/modules/backups/__tests__/backups.controller.test.ts index 26eb068a..89172d58 100644 --- a/app/server/modules/backups/__tests__/backups.controller.test.ts +++ b/app/server/modules/backups/__tests__/backups.controller.test.ts @@ -25,10 +25,10 @@ describe("backups security", () => { }); test("should return 200 if session is valid", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/backups", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -81,7 +81,7 @@ describe("backups security", () => { describe("input validation", () => { test("should return a schedule when queried by short id", async () => { - const { token, organizationId } = await createTestSession(); + const { headers, organizationId } = await createTestSession(); const volume = await createTestVolume({ organizationId }); const repository = await createTestRepository({ organizationId }); const schedule = await createTestBackupSchedule({ @@ -91,7 +91,7 @@ describe("backups security", () => { }); const res = await app.request(`/api/v1/backups/${schedule.shortId}`, { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -101,18 +101,18 @@ describe("backups security", () => { }); test("should return 404 for malformed schedule ID", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/backups/not-a-number", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); }); test("should return 404 for non-existent schedule ID", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/backups/999999", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); @@ -121,11 +121,11 @@ describe("backups security", () => { }); test("should return 400 for invalid payload on create", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/backups", { method: "POST", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/events/__tests__/events.controller.test.ts b/app/server/modules/events/__tests__/events.controller.test.ts index 20fb4789..d2af6a8e 100644 --- a/app/server/modules/events/__tests__/events.controller.test.ts +++ b/app/server/modules/events/__tests__/events.controller.test.ts @@ -23,10 +23,10 @@ describe("events security", () => { }); test("should return 200 if session is valid", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/events", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -35,11 +35,11 @@ describe("events security", () => { }); test("should cleanup SSE listeners when client disconnects", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const initialCount = serverEvents.listenerCount("doctor:cancelled"); const res = await app.request("/api/v1/events", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); diff --git a/app/server/modules/notifications/__tests__/notifications.controller.test.ts b/app/server/modules/notifications/__tests__/notifications.controller.test.ts index 2d3ec463..a6e6bfc1 100644 --- a/app/server/modules/notifications/__tests__/notifications.controller.test.ts +++ b/app/server/modules/notifications/__tests__/notifications.controller.test.ts @@ -22,10 +22,10 @@ describe("notifications security", () => { }); test("should return 200 if session is valid", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -62,18 +62,18 @@ describe("notifications security", () => { describe("input validation", () => { test("should return 404 for malformed destination ID", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations/not-a-number", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); }); test("should return 404 for non-existent destination ID", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations/999999", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); @@ -82,12 +82,12 @@ describe("notifications security", () => { }); test("should return 400 for invalid payload on create", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations", { method: "POST", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/repositories/__tests__/repositories.controller.test.ts b/app/server/modules/repositories/__tests__/repositories.controller.test.ts index 94210d52..95c7c78e 100644 --- a/app/server/modules/repositories/__tests__/repositories.controller.test.ts +++ b/app/server/modules/repositories/__tests__/repositories.controller.test.ts @@ -57,10 +57,10 @@ describe("repositories security", () => { }); test("should return 200 if session is valid", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/repositories", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -106,9 +106,9 @@ describe("repositories security", () => { describe("input validation", () => { test("should return 404 for non-existent repository", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/repositories/non-existent-repo", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); @@ -117,9 +117,9 @@ describe("repositories security", () => { }); test("should return 404 for stats of non-existent repository", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/repositories/non-existent-repo/stats", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); @@ -128,11 +128,11 @@ describe("repositories security", () => { }); test("should return 400 for invalid payload on create", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/repositories", { method: "POST", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -147,14 +147,14 @@ describe("repositories security", () => { describe("repositories updates", () => { test("PATCH updates full config and metadata using shortId", async () => { - const { token, organizationId } = await createTestSession(); + const { headers, organizationId } = await createTestSession(); const repository = await createRepositoryRecord(organizationId); const nextPath = `/tmp/updated-${crypto.randomUUID()}`; const res = await app.request(`/api/v1/repositories/${repository.shortId}`, { method: "PATCH", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -196,13 +196,13 @@ describe("repositories updates", () => { }); test("PATCH rejects backend changes", async () => { - const { token, organizationId } = await createTestSession(); + const { headers, organizationId } = await createTestSession(); const repository = await createRepositoryRecord(organizationId); const res = await app.request(`/api/v1/repositories/${repository.shortId}`, { method: "PATCH", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -222,13 +222,13 @@ describe("repositories updates", () => { }); test("PATCH rejects invalid config payload", async () => { - const { token, organizationId } = await createTestSession(); + const { headers, organizationId } = await createTestSession(); const repository = await createRepositoryRecord(organizationId); const res = await app.request(`/api/v1/repositories/${repository.shortId}`, { method: "PATCH", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -243,7 +243,7 @@ describe("repositories updates", () => { describe("delete snapshot", () => { test("should return 500 when restic deleteSnapshot throws ResticError", async () => { - const { token, organizationId } = await createTestSession(); + const { headers, organizationId } = await createTestSession(); const repository = await createRepositoryRecord(organizationId); const { restic } = await import("~/server/utils/restic"); @@ -256,7 +256,7 @@ describe("repositories updates", () => { try { const res = await app.request(`/api/v1/repositories/${repository.shortId}/snapshots/snap123`, { method: "DELETE", - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(500); diff --git a/app/server/modules/system/__tests__/system.controller.test.ts b/app/server/modules/system/__tests__/system.controller.test.ts index 0c0c2a2b..9bdbeb17 100644 --- a/app/server/modules/system/__tests__/system.controller.test.ts +++ b/app/server/modules/system/__tests__/system.controller.test.ts @@ -22,10 +22,10 @@ describe("system security", () => { }); test("should return 200 if session is valid", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/system/info", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -49,11 +49,11 @@ describe("system security", () => { describe("input validation", () => { test("should return 400 for invalid payload on restic-password", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/system/restic-password", { method: "POST", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({}), @@ -63,11 +63,11 @@ describe("system security", () => { }); test("should return 401 for incorrect password on restic-password", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/system/restic-password", { method: "POST", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/volumes/__tests__/volumes.controller.test.ts b/app/server/modules/volumes/__tests__/volumes.controller.test.ts index 30529b4f..ff94a73f 100644 --- a/app/server/modules/volumes/__tests__/volumes.controller.test.ts +++ b/app/server/modules/volumes/__tests__/volumes.controller.test.ts @@ -22,10 +22,10 @@ describe("volumes security", () => { }); test("should return 200 if session is valid", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/volumes", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(200); @@ -67,9 +67,9 @@ describe("volumes security", () => { describe("input validation", () => { test("should return 404 for non-existent volume", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/volumes/non-existent-volume", { - headers: getAuthHeaders(token), + headers, }); expect(res.status).toBe(404); @@ -78,11 +78,11 @@ describe("volumes security", () => { }); test("should return 400 for invalid payload on create", async () => { - const { token } = await createTestSession(); + const { headers } = await createTestSession(); const res = await app.request("/api/v1/volumes", { method: "POST", headers: { - ...getAuthHeaders(token), + ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/test/helpers/auth.ts b/app/test/helpers/auth.ts index e572043a..5e1b78a6 100644 --- a/app/test/helpers/auth.ts +++ b/app/test/helpers/auth.ts @@ -1,7 +1,4 @@ -import { db } from "~/server/db/db"; -import { sessionsTable, usersTable, account, organization, member } from "~/server/db/schema"; -import { hashPassword } from "better-auth/crypto"; -import { createHmac } from "node:crypto"; +import { auth } from "~/server/lib/auth"; export const COOKIE_PREFIX = "zerobyte"; @@ -12,60 +9,17 @@ export function getAuthHeaders(token: string): { Cookie: string } { } export async function createTestSession() { - const userId = crypto.randomUUID(); - const user = { - username: `testuser-${userId}`, - email: `${userId}@test.com`, - name: "Test User", - id: userId, + const ctx = await auth.$context; + const user = ctx.test.createUser(); + await ctx.test.saveUser(user); + const { headers, session } = await ctx.test.login({ userId: user.id }); + + const organizationId = (session as { activeOrganizationId?: string }).activeOrganizationId ?? ""; + + return { + headers: Object.fromEntries(headers.entries()) as Record, + session, + user, + organizationId, }; - await db.insert(usersTable).values(user); - - const token = crypto.randomUUID().replace(/-/g, ""); - const sessionId = token; - const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); - - const orgId = crypto.randomUUID(); - await db.insert(organization).values({ - id: orgId, - name: `Org ${orgId}`, - slug: `test-org-${orgId}`, - createdAt: new Date(), - }); - - await db.insert(member).values({ - id: crypto.randomUUID(), - userId: user.id, - organizationId: orgId, - role: "owner", - createdAt: new Date(), - }); - - await db.insert(sessionsTable).values({ - id: sessionId, - userId: user.id, - expiresAt, - token: token, - createdAt: new Date(), - updatedAt: new Date(), - activeOrganizationId: orgId, - }); - - const signature = createHmac("sha256", "test-secret").update(token).digest("base64"); - const signedToken = `${token}.${signature}`; - - await db - .insert(account) - .values({ - userId: user.id, - accountId: user.username, - password: await hashPassword("password123"), - id: crypto.randomUUID(), - providerId: "credentials", - createdAt: new Date(), - updatedAt: new Date(), - }) - .onConflictDoNothing(); - - return { token: encodeURIComponent(signedToken), user, organizationId: orgId }; } diff --git a/bun.lock b/bun.lock index f41da0f0..c64d8d45 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "": { "name": "zerobyte", "dependencies": { - "@better-auth/sso": "^1.4.18", + "@better-auth/sso": "1.5.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -34,7 +34,7 @@ "@tanstack/react-router-ssr-query": "^1.162.9", "@tanstack/react-start": "^1.162.9", "arktype": "^2.1.28", - "better-auth": "^1.4.19", + "better-auth": "1.5.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "commander": "^14.0.2", @@ -214,16 +214,34 @@ "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - "@better-auth/core": ["@better-auth/core@1.4.19", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.8", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA=="], + "@better-auth/core": ["@better-auth/core@1.5.0", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/utils": "0.3.1", "@better-fetch/fetch": "1.1.21", "@cloudflare/workers-types": ">=4", "better-call": "1.3.2", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" }, "optionalPeers": ["@cloudflare/workers-types"] }, "sha512-nDPmW7I9VGRACEei31fHaZxGwD/yICraDllZ/f25jbWXYaxDaW88RuH1ZhbOUKmGJlZtDxcjN1+YmcVIc1ioNw=="], - "@better-auth/sso": ["@better-auth/sso@1.4.19", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "fast-xml-parser": "^5.2.5", "jose": "^6.1.0", "samlify": "^2.10.1", "zod": "^4.3.5" }, "peerDependencies": { "better-auth": "1.4.19", "better-call": "1.1.8" } }, "sha512-4OVPii6DQgIRm/DqO74Q4tZHx0LlNHXif3qrW7/iDzRO507h/AVAoJ9/U2fGycsPxUtPh1BT3PGHfX09sbF9Bw=="], + "@better-auth/drizzle-adapter": ["@better-auth/drizzle-adapter@1.5.0", "", { "peerDependencies": { "@better-auth/core": "1.5.0", "@better-auth/utils": "^0.3.0", "drizzle-orm": ">=0.41.0" } }, "sha512-qNKAoe+ViiHznipkoOCLo3pDvQo4/6mYbCwnfo2mNbjjopqIn0c3IKW2t+e/Mg4Y18PJcEFeOiIRoY9pUyTtAg=="], - "@better-auth/telemetry": ["@better-auth/telemetry@1.4.19", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.19" } }, "sha512-ApGNS7olCTtDpKF8Ow3Z+jvFAirOj7c4RyFUpu8axklh3mH57ndpfUAUjhgA8UVoaaH/mnm/Tl884BlqiewLyw=="], + "@better-auth/kysely-adapter": ["@better-auth/kysely-adapter@1.5.0", "", { "peerDependencies": { "@better-auth/core": "1.5.0", "@better-auth/utils": "^0.3.0", "kysely": "^0.27.0 || ^0.28.0" } }, "sha512-dSC7yzF58YMrn39srrX/LwMLjd11PtTORjn3RKFwuzVCS07mqMZpPkk3XbQW/ZXxXbdUByrgYQo9z9zFCF37RQ=="], - "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="], + "@better-auth/memory-adapter": ["@better-auth/memory-adapter@1.5.0", "", { "peerDependencies": { "@better-auth/core": "1.5.0", "@better-auth/utils": "^0.3.0" } }, "sha512-fp0OtEpWi4RgfxrhhAI8tKW8yHTHx49V12UW1XyuUKrEK0jbu+CzVJSzoMkv/q3fJfjGqDe/vNvWtVBtpIRpQw=="], + + "@better-auth/mongo-adapter": ["@better-auth/mongo-adapter@1.5.0", "", { "peerDependencies": { "@better-auth/core": "1.5.0", "@better-auth/utils": "^0.3.0", "mongodb": "^6.0.0 || ^7.0.0" } }, "sha512-qI+MiewH6kzUbNkKBX4fdhPl1MkIXq8V7v3TEt/DLAHC4/QxmPqhxQHEDeZBxO+NH8+Zekr2sQzKRRFfbaQKbw=="], + + "@better-auth/prisma-adapter": ["@better-auth/prisma-adapter@1.5.0", "", { "dependencies": { "@prisma/client": "^7.4.1" }, "peerDependencies": { "@better-auth/core": "1.5.0", "@better-auth/utils": "^0.3.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-Sga8DTeCCsamGZ7/54kH0HKlrCrgW4EApadAgsufsIcRqHteeKC5rNCLIppfyRa/xJxm5xImUeks6Nvz3zL1tw=="], + + "@better-auth/sso": ["@better-auth/sso@1.5.0", "", { "dependencies": { "@better-auth/utils": "0.3.1", "@better-fetch/fetch": "1.1.21", "fast-xml-parser": "^5.4.1", "jose": "^6.1.3", "samlify": "^2.10.2", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/core": "1.5.0", "better-auth": "1.5.0", "better-call": "1.3.2" } }, "sha512-HuomPV25de9y2A/8WX4UtDHdHj4OUlwbnTH/NAuhmqUGyj7VZrOBwUaZhmD6roVhF18+YigOmGPetajukMZrQQ=="], + + "@better-auth/telemetry": ["@better-auth/telemetry@1.5.0", "", { "dependencies": { "@better-auth/utils": "0.3.1", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.5.0" } }, "sha512-/6ThGSnGPVTR4A/6F8kv65UomDHtM24y2yRZuJfWYbqkve0jn8+WVsxOfQN9bx7J16zIvYw5hJAYCwxoj20BTQ=="], + + "@better-auth/utils": ["@better-auth/utils@0.3.1", "", {}, "sha512-+CGp4UmZSUrHHnpHhLPYu6cV+wSUSvVbZbNykxhUDocpVNTo9uFFxw/NqJlh1iC4wQ9HKKWGCKuZ5wUgS0v6Kg=="], "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="], + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@10.5.0", "", { "dependencies": { "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "lodash": "4.17.21" } }, "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw=="], + + "@chevrotain/gast": ["@chevrotain/gast@10.5.0", "", { "dependencies": { "@chevrotain/types": "10.5.0", "lodash": "4.17.21" } }, "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A=="], + + "@chevrotain/types": ["@chevrotain/types@10.5.0", "", {}, "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A=="], + + "@chevrotain/utils": ["@chevrotain/utils@10.5.0", "", {}, "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ=="], + "@dnd-kit/accessibility": ["@dnd-kit/accessibility@3.1.1", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="], "@dnd-kit/core": ["@dnd-kit/core@6.3.1", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="], @@ -234,6 +252,12 @@ "@drizzle-team/brocli": ["@drizzle-team/brocli@0.11.0", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="], + "@electric-sql/pglite": ["@electric-sql/pglite@0.3.15", "", {}, "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ=="], + + "@electric-sql/pglite-socket": ["@electric-sql/pglite-socket@0.0.20", "", { "peerDependencies": { "@electric-sql/pglite": "0.3.15" }, "bin": { "pglite-server": "dist/scripts/server.js" } }, "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg=="], + + "@electric-sql/pglite-tools": ["@electric-sql/pglite-tools@0.2.20", "", { "peerDependencies": { "@electric-sql/pglite": "0.3.15" } }, "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A=="], + "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], @@ -314,6 +338,8 @@ "@hey-api/types": ["@hey-api/types@0.1.3", "", { "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-mZaiPOWH761yD4GjDQvtjS2ZYLu5o5pI1TVSvV/u7cmbybv51/FVtinFBeaE1kFQCKZ8OQpn2ezjLBJrKsGATw=="], + "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="], + "@hono/standard-validator": ["@hono/standard-validator@0.2.2", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "hono": ">=3.9.0" } }, "sha512-mJ7W84Bt/rSvoIl63Ynew+UZOHAzzRAoAXb3JaWuxAkM/Lzg+ZHTCUiz77KOtn2e623WNN8LkD57Dk0szqUrIw=="], "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="], @@ -392,6 +418,10 @@ "@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.22", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="], + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.6", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g=="], + + "@mrleebo/prisma-ast": ["@mrleebo/prisma-ast@0.13.1", "", { "dependencies": { "chevrotain": "^10.5.0", "lilconfig": "^2.1.0" } }, "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], @@ -578,6 +608,28 @@ "@playwright/test": ["@playwright/test@1.58.2", "", { "dependencies": { "playwright": "1.58.2" }, "bin": { "playwright": "cli.js" } }, "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA=="], + "@prisma/client": ["@prisma/client@7.4.2", "", { "dependencies": { "@prisma/client-runtime-utils": "7.4.2" }, "peerDependencies": { "prisma": "*", "typescript": ">=5.4.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-ts2mu+cQHriAhSxngO3StcYubBGTWDtu/4juZhXCUKOwgh26l+s4KD3vT2kMUzFyrYnll9u/3qWrtzRv9CGWzA=="], + + "@prisma/client-runtime-utils": ["@prisma/client-runtime-utils@7.4.2", "", {}, "sha512-cID+rzOEb38VyMsx5LwJMEY4NGIrWCNpKu/0ImbeooQ2Px7TI+kOt7cm0NelxUzF2V41UVVXAmYjANZQtCu1/Q=="], + + "@prisma/config": ["@prisma/config@7.4.2", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-CftBjWxav99lzY1Z4oDgomdb1gh9BJFAOmWF6P2v1xRfXqQb56DfBub+QKcERRdNoAzCb3HXy3Zii8Vb4AsXhg=="], + + "@prisma/debug": ["@prisma/debug@7.4.2", "", {}, "sha512-aP7qzu+g/JnbF6U69LMwHoUkELiserKmWsE2shYuEpNUJ4GrtxBCvZwCyCBHFSH2kLTF2l1goBlBh4wuvRq62w=="], + + "@prisma/dev": ["@prisma/dev@0.20.0", "", { "dependencies": { "@electric-sql/pglite": "0.3.15", "@electric-sql/pglite-socket": "0.0.20", "@electric-sql/pglite-tools": "0.2.20", "@hono/node-server": "1.19.9", "@mrleebo/prisma-ast": "0.13.1", "@prisma/get-platform": "7.2.0", "@prisma/query-plan-executor": "7.2.0", "foreground-child": "3.3.1", "get-port-please": "3.2.0", "hono": "4.11.4", "http-status-codes": "2.3.0", "pathe": "2.0.3", "proper-lockfile": "4.1.2", "remeda": "2.33.4", "std-env": "3.10.0", "valibot": "1.2.0", "zeptomatch": "2.1.0" } }, "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ=="], + + "@prisma/engines": ["@prisma/engines@7.4.2", "", { "dependencies": { "@prisma/debug": "7.4.2", "@prisma/engines-version": "7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919", "@prisma/fetch-engine": "7.4.2", "@prisma/get-platform": "7.4.2" } }, "sha512-B+ZZhI4rXlzjVqRw/93AothEKOU5/x4oVyJFGo9RpHPnBwaPwk4Pi0Q4iGXipKxeXPs/dqljgNBjK0m8nocOJA=="], + + "@prisma/engines-version": ["@prisma/engines-version@7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919", "", {}, "sha512-5FIKY3KoYQlBuZC2yc16EXfVRQ8HY+fLqgxkYfWCtKhRb3ajCRzP/rPeoSx11+NueJDANdh4hjY36mdmrTcGSg=="], + + "@prisma/fetch-engine": ["@prisma/fetch-engine@7.4.2", "", { "dependencies": { "@prisma/debug": "7.4.2", "@prisma/engines-version": "7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919", "@prisma/get-platform": "7.4.2" } }, "sha512-f/c/MwYpdJO7taLETU8rahEstLeXfYgQGlz5fycG7Fbmva3iPdzGmjiSWHeSWIgNnlXnelUdCJqyZnFocurZuA=="], + + "@prisma/get-platform": ["@prisma/get-platform@7.2.0", "", { "dependencies": { "@prisma/debug": "7.2.0" } }, "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA=="], + + "@prisma/query-plan-executor": ["@prisma/query-plan-executor@7.2.0", "", {}, "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ=="], + + "@prisma/studio-core": ["@prisma/studio-core@0.13.1", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], @@ -882,8 +934,12 @@ "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + "@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="], + "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], + "@types/whatwg-url": ["@types/whatwg-url@13.0.0", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.3", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA=="], @@ -926,6 +982,8 @@ "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], + "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], + "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], "babel-plugin-react-compiler": ["babel-plugin-react-compiler@1.0.0", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw=="], @@ -936,9 +994,9 @@ "baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="], - "better-auth": ["better-auth@1.4.19", "", { "dependencies": { "@better-auth/core": "1.4.19", "@better-auth/telemetry": "1.4.19", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.8", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.3.5" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-3RlZJcA0+NH25wYD85vpIGwW9oSTuEmLIaGbT8zg41w/Pa2hVWHKedjoUHHJtnzkBXzDb+CShkLnSw7IThDdqQ=="], + "better-auth": ["better-auth@1.5.0", "", { "dependencies": { "@better-auth/core": "1.5.0", "@better-auth/drizzle-adapter": "1.5.0", "@better-auth/kysely-adapter": "1.5.0", "@better-auth/memory-adapter": "1.5.0", "@better-auth/mongo-adapter": "1.5.0", "@better-auth/prisma-adapter": "1.5.0", "@better-auth/telemetry": "1.5.0", "@better-auth/utils": "0.3.1", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.1.1", "@noble/hashes": "^2.0.1", "better-call": "1.3.2", "defu": "^6.1.4", "jose": "^6.1.3", "kysely": "^0.28.11", "nanostores": "^1.1.1", "zod": "^4.3.6" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-vbRKSxDa1vwXzEmR7+kXJMDs2pRXLOvD4MiqQwL4r6kkjzok5kOyLD200ZUg24Jtx2Xho1oA2drlxT6GqknH1A=="], - "better-call": ["better-call@1.1.8", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw=="], + "better-call": ["better-call@1.3.2", "", { "dependencies": { "@better-auth/utils": "^0.3.1", "@better-fetch/fetch": "^1.1.21", "rou3": "^0.7.12", "set-cookie-parser": "^3.0.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-4cZIfrerDsNTn3cm+MhLbUePN0gdwkhSXEuG7r/zuQ8c/H7iU0/jSK5TD3FW7U0MgKHce/8jGpPYNO4Ve+4NBw=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -950,6 +1008,8 @@ "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "bson": ["bson@7.2.0", "", {}, "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ=="], + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], @@ -980,6 +1040,8 @@ "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], + "chevrotain": ["chevrotain@10.5.0", "", { "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "@chevrotain/utils": "10.5.0", "lodash": "4.17.21", "regexp-to-ast": "0.5.0" } }, "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A=="], + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], @@ -1056,6 +1118,8 @@ "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + "deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="], + "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], @@ -1064,6 +1128,8 @@ "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], @@ -1100,8 +1166,12 @@ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "effect": ["effect@3.18.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA=="], + "electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="], + "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], + "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], @@ -1132,13 +1202,17 @@ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], "fast-wrap-ansi": ["fast-wrap-ansi@0.2.0", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w=="], - "fast-xml-parser": ["fast-xml-parser@5.3.7", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-JzVLro9NQv92pOM/jTCR6mHlJh2FGwtomH8ZQjhFj/R29P2Fnj38OgPJVtcvYw6SuKClhgYuwUZf5b3rd8u2mA=="], + "fast-xml-builder": ["fast-xml-builder@1.0.0", "", {}, "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ=="], + + "fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], @@ -1146,14 +1220,20 @@ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="], + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + "get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="], + "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], "giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], @@ -1164,6 +1244,10 @@ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "grammex": ["grammex@3.1.12", "", {}, "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ=="], + + "graphmatch": ["graphmatch@1.1.1", "", {}, "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg=="], + "h3": ["h3@2.0.1-rc.14", "", { "dependencies": { "rou3": "^0.7.12", "srvx": "^0.11.2" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-163qbGmTr/9rqQRNuqMqtgXnOUAkE4KTdauiC9y0E5iG1I65kte9NyfWvZw5RTDMt6eY+DtyoNzrQ9wA2BfvGQ=="], "h3-v2": ["h3@2.0.1-rc.14", "", { "dependencies": { "rou3": "^0.7.12", "srvx": "^0.11.2" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-163qbGmTr/9rqQRNuqMqtgXnOUAkE4KTdauiC9y0E5iG1I65kte9NyfWvZw5RTDMt6eY+DtyoNzrQ9wA2BfvGQ=="], @@ -1188,6 +1272,8 @@ "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + "http-status-codes": ["http-status-codes@2.3.0", "", {}, "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], @@ -1228,6 +1314,8 @@ "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], + "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], "isbot": ["isbot@5.1.35", "", {}, "sha512-waFfC72ZNfwLLuJ2iLaoVaqcNo+CAaLR7xCpAn0Y5WfGzkNHv7ZN39Vbi1y+kb+Zs46XHOX3tZNExroFUPX+Kg=="], @@ -1308,6 +1396,10 @@ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="], + "lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], @@ -1322,10 +1414,14 @@ "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], + "lucide-react": ["lucide-react@0.575.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg=="], "luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="], @@ -1366,6 +1462,8 @@ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + "memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], @@ -1424,15 +1522,23 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "mongodb": ["mongodb@7.1.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.3.0", "bson": "^7.1.1", "mongodb-connection-string-url": "^7.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.806.0", "@mongodb-js/zstd": "^7.0.0", "gcp-metadata": "^7.0.1", "kerberos": "^7.0.0", "mongodb-client-encryption": ">=7.0.0 <7.1.0", "snappy": "^7.3.2", "socks": "^2.8.6" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg=="], + + "mongodb-connection-string-url": ["mongodb-connection-string-url@7.0.1", "", { "dependencies": { "@types/whatwg-url": "^13.0.0", "whatwg-url": "^14.1.0" } }, "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "mssql": ["mssql@11.0.1", "", { "dependencies": { "@tediousjs/connection-string": "^0.5.0", "commander": "^11.0.0", "debug": "^4.3.3", "rfdc": "^1.3.0", "tarn": "^3.0.2", "tedious": "^18.2.1" }, "bin": { "mssql": "bin/mssql" } }, "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w=="], "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + "mysql2": ["mysql2@3.15.3", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg=="], + + "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="], + "nanostores": ["nanostores@1.1.1", "", {}, "sha512-EYJqS25r2iBeTtGQCHidXl1VfZ1jXM7Q04zXJOrMlxVVmD0ptxJaNux92n1mJ7c5lN3zTq12MhH/8x59nP+qmg=="], "native-duplexpair": ["native-duplexpair@1.0.0", "", {}, "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA=="], @@ -1510,18 +1616,28 @@ "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], + "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], + "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "prisma": ["prisma@7.4.2", "", { "dependencies": { "@prisma/config": "7.4.2", "@prisma/dev": "0.20.0", "@prisma/engines": "7.4.2", "@prisma/studio-core": "0.13.1", "mysql2": "3.15.3", "postgres": "3.4.7" }, "peerDependencies": { "better-sqlite3": ">=9.0.0", "typescript": ">=5.4.0" }, "optionalPeers": ["better-sqlite3", "typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-2bP8Ruww3Q95Z2eH4Yqh4KAENRsj/SxbdknIVBfd6DmjPwmpsC4OVFMLOeHt6tM3Amh8ebjvstrUz3V/hOe1dA=="], + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], "promise-limit": ["promise-limit@2.7.0", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="], + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + "qrcode.react": ["qrcode.react@4.2.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA=="], "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], @@ -1560,6 +1676,8 @@ "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + "regexp-to-ast": ["regexp-to-ast@0.5.0", "", {}, "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="], + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], @@ -1568,10 +1686,14 @@ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "remeda": ["remeda@2.33.4", "", {}, "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ=="], + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], @@ -1590,11 +1712,13 @@ "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="], + "seroval": ["seroval@1.5.0", "", {}, "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw=="], "seroval-plugins": ["seroval-plugins@1.5.0", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA=="], - "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "set-cookie-parser": ["set-cookie-parser@3.0.1", "", {}, "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -1612,10 +1736,16 @@ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "^1.0.2" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="], + "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], + "sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="], + "srvx": ["srvx@0.10.1", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-A//xtfak4eESMWWydSRFUVvCTQbSwivnGCEf8YGPe2eHU0+Z6znfUTCPF0a7oV3sObSOcrXHlL6Bs9vVctfXdg=="], + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], @@ -1706,6 +1836,8 @@ "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "valibot": ["valibot@1.2.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg=="], + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], @@ -1754,6 +1886,8 @@ "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + "zeptomatch": ["zeptomatch@2.1.0", "", { "dependencies": { "grammex": "^3.1.11", "graphmatch": "^1.1.0" } }, "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], @@ -1768,6 +1902,16 @@ "@hey-api/shared/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "@prisma/config/c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="], + + "@prisma/dev/hono": ["hono@4.11.4", "", {}, "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA=="], + + "@prisma/engines/@prisma/get-platform": ["@prisma/get-platform@7.4.2", "", { "dependencies": { "@prisma/debug": "7.4.2" } }, "sha512-UTnChXRwiauzl/8wT4hhe7Xmixja9WE28oCnGpBtRejaHhvekx5kudr3R4Y9mLSA0kqGnAMeyTiKwDVMjaEVsw=="], + + "@prisma/fetch-engine/@prisma/get-platform": ["@prisma/get-platform@7.4.2", "", { "dependencies": { "@prisma/debug": "7.4.2" } }, "sha512-UTnChXRwiauzl/8wT4hhe7Xmixja9WE28oCnGpBtRejaHhvekx5kudr3R4Y9mLSA0kqGnAMeyTiKwDVMjaEVsw=="], + + "@prisma/get-platform/@prisma/debug": ["@prisma/debug@7.2.0", "", {}, "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw=="], + "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -1838,6 +1982,8 @@ "libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], + "mongodb-connection-string-url/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + "mssql/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "mssql/tedious": ["tedious@18.6.2", "", { "dependencies": { "@azure/core-auth": "^1.7.2", "@azure/identity": "^4.2.1", "@azure/keyvault-keys": "^4.4.0", "@js-joda/core": "^5.6.1", "@types/node": ">=18", "bl": "^6.0.11", "iconv-lite": "^0.6.3", "js-md4": "^0.3.2", "native-duplexpair": "^1.0.0", "sprintf-js": "^1.1.3" } }, "sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg=="], @@ -1850,6 +1996,8 @@ "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + "proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -1858,10 +2006,22 @@ "@azure/identity/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], + "@prisma/config/c12/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "@prisma/config/c12/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "@prisma/config/c12/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + "@tanstack/router-plugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "mongodb-connection-string-url/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], + + "mongodb-connection-string-url/whatwg-url/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + "mssql/tedious/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "@prisma/config/c12/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "@tanstack/router-plugin/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], } } diff --git a/package.json b/package.json index d7eaba32..3f57ab16 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "test:codegen": "playwright codegen localhost:4096" }, "dependencies": { - "@better-auth/sso": "^1.4.18", + "@better-auth/sso": "1.5.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -55,7 +55,7 @@ "@tanstack/react-router-ssr-query": "^1.162.9", "@tanstack/react-start": "^1.162.9", "arktype": "^2.1.28", - "better-auth": "^1.4.19", + "better-auth": "1.5.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "commander": "^14.0.2", diff --git a/playwright/dex-config.yaml b/playwright/dex-config.yaml index 15bed466..91af1066 100644 --- a/playwright/dex-config.yaml +++ b/playwright/dex-config.yaml @@ -14,15 +14,19 @@ staticPasswords: - email: "admin@example.com" hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" username: "admin" + userID: "001" - email: "user@example.com" hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" username: "user" + userID: "002" - email: "test@example.com" hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" username: "test" + userID: "003" - email: "test@test.com" hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" username: "test-local" + userID: "004" staticClients: - id: zerobyte-test