From 2eacda929b0b9d59f8953eb46277bf4fccf09d72 Mon Sep 17 00:00:00 2001 From: Justin McLellan Date: Mon, 10 Aug 2026 00:07:31 -0500 Subject: [PATCH] Add passkey authentication and responsive team navigation Enable passkeys via Better Auth's passkey plugin, alongside a few UI and tooling fixes made along the way. Passkeys: - Add @better-auth/passkey (bumps better-auth 1.6.23 -> 1.6.26, where the plugin moved into its own package) - Add the Passkey model and an additive migration - Pin rpID to the BETTER_AUTH_URL hostname so passkeys stay scoped to this app rather than a shared parent domain, and document the optional PASSKEY_RP_ID override - Add a passkey management dialog (list/add/remove) to the user menu, hidden while impersonating since the endpoints resolve the real session rather than the impersonation override - Add passkey sign-in plus browser autofill (conditional UI) on /signin - Surface auth errors without inventing a cause (src/lib/auth-errors.ts) UI: - Team pages: vertical nav on tablet/desktop, shadcn Select on mobile - Declare color-scheme per theme so browser-rendered UI (select popups, scrollbars) follows dark mode instead of always painting light - Add the Google mark to the Google sign-in/sign-up buttons Tooling: - Pass both ignore paths to Prettier. Passing only --ignore-path .gitignore replaced the default list, so .prettierignore was never read; ignore the lockfile and generated output, and reflow the files that had drifted from the pinned Prettier 3.8. Co-Authored-By: Claude Opus 5 (1M context) --- .prettierignore | 7 +- eslint.config.mjs | 49 +- example.env | 7 +- package.json | 8 +- pnpm-lock.yaml | 807 +++++++++++++++++- .../20260810041844_add_passkey/migration.sql | 25 + prisma/schema/passkey.prisma | 18 + prisma/schema/user.prisma | 1 + src/app/globals.css | 7 + src/app/signin/page.tsx | 82 +- src/app/signup/page.tsx | 2 + src/app/team/layout.tsx | 8 +- src/app/team/team-tabs.tsx | 66 +- .../layout/notification-settings.tsx | 8 +- src/components/layout/passkey-settings.tsx | 179 ++++ src/components/layout/user-menu.tsx | 22 +- src/components/ui/avatar.tsx | 2 +- src/components/ui/button.tsx | 3 +- src/components/ui/google-icon.tsx | 27 + src/components/ui/pagination-controls.tsx | 4 +- src/components/ui/select.tsx | 159 ++++ src/lib/auth-client.ts | 3 +- src/lib/auth-errors.ts | 21 + src/lib/auth.ts | 17 + 24 files changed, 1426 insertions(+), 106 deletions(-) create mode 100644 prisma/migrations/20260810041844_add_passkey/migration.sql create mode 100644 prisma/schema/passkey.prisma create mode 100644 src/components/layout/passkey-settings.tsx create mode 100644 src/components/ui/google-icon.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/lib/auth-errors.ts diff --git a/.prettierignore b/.prettierignore index 49a384e..2dce1ac 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,13 @@ .next .cache package-lock.json +pnpm-lock.yaml public node_modules next-env.d.ts next.config.ts -docker-compose.yml \ No newline at end of file +docker-compose.yml +prisma/generated + +# machine-local tool state +.claude/settings.local.json \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 7c23e8b..0ab44a7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,34 +1,45 @@ -import { FlatCompat } from "@eslint/eslintrc"; -import js from "@eslint/js"; +import { FlatCompat } from '@eslint/eslintrc'; +import js from '@eslint/js'; import vitest from '@vitest/eslint-plugin'; -import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; -import nextTypescript from "eslint-config-next/typescript"; -import { defineConfig, globalIgnores } from "eslint/config"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; +import nextCoreWebVitals from 'eslint-config-next/core-web-vitals'; +import nextTypescript from 'eslint-config-next/typescript'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, }); -export default defineConfig([globalIgnores(["**/node_modules/", "**/.next/"]), { - extends: [...nextCoreWebVitals, ...nextTypescript, ...compat.extends("prettier")], +export default defineConfig([ + globalIgnores(['**/node_modules/', '**/.next/']), + { + extends: [ + ...nextCoreWebVitals, + ...nextTypescript, + ...compat.extends('prettier'), + ], plugins: { - vitest + vitest, }, rules: { - "no-unused-vars": ["error", { - argsIgnorePattern: "^_", - ignoreRestSiblings: true, - }], + 'no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], }, -}, { + }, + { files: ['**/*.{test,spec}.{ts,tsx,js,jsx}'], ...vitest.configs.recommended, -}]); \ No newline at end of file + }, +]); diff --git a/example.env b/example.env index ba3fa02..3dfd809 100644 --- a/example.env +++ b/example.env @@ -7,8 +7,13 @@ DATABASE_HOST=localhost DATABASE_PORT=5431 # database url used for prisma (combination of the 5 lines above) DATABASE_URL=postgresql://root:euI3dxzgDt8NaLuaR4QPWIoJjVXeAKqEovTEV8ecKxw=@localhost:5431/template?schema=public -# Base url for better-auth +# Base url for better-auth. Must be set in production: passkeys derive their +# Relying Party ID from this hostname, and it falls back to "localhost" if unset. BETTER_AUTH_URL=http://localhost:3000 +# Optional passkey Relying Party ID override. Defaults to the BETTER_AUTH_URL +# hostname, which scopes passkeys to this app alone. Only set this to a parent +# domain if you intend to share passkeys with sibling subdomains. +PASSKEY_RP_ID= # Auth secret for better-auth # replace this with output from: openssl rand -base64 32 AUTH_SECRET=PxVYHb3UPLDMO/F8IWh1EmDDCWqm9Y+l2meScUjUQog= diff --git a/package.json b/package.json index d127ccf..7dfecde 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "test": "vitest --run", "start": "next start", "lint": "eslint .", - "format": "prettier --check --ignore-path .gitignore .", - "format:fix": "prettier --write --ignore-path .gitignore .", + "format": "prettier --check --ignore-path .gitignore --ignore-path .prettierignore .", + "format:fix": "prettier --write --ignore-path .gitignore --ignore-path .prettierignore .", "postinstall": "prisma generate", "prepare": "husky", "init": "tsx scripts/initialize.ts", @@ -25,12 +25,14 @@ "docker:build": "docker compose build" }, "dependencies": { + "@better-auth/passkey": "^1.6.26", "@prisma/adapter-pg": "^7.3.0", "@prisma/client": "^7.3.0", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-select": "^2.3.7", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toast": "^1.2.2", @@ -38,7 +40,7 @@ "@types/web-push": "^3.6.4", "ag-grid-community": "^35.0.1", "ag-grid-react": "^35.0.1", - "better-auth": "^1.6.23", + "better-auth": "^1.6.26", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^17.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 176a748..a551974 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@better-auth/passkey': + specifier: ^1.6.26 + version: 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-auth@1.6.26(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)))(better-call@1.3.7(zod@4.3.6))(nanostores@1.4.0) '@prisma/adapter-pg': specifier: ^7.3.0 version: 7.3.0 @@ -26,6 +29,9 @@ importers: '@radix-ui/react-label': specifier: ^2.1.0 version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-select': + specifier: ^2.3.7 + version: 2.3.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) '@radix-ui/react-slot': specifier: ^1.1.0 version: 1.2.4(@types/react@19.2.7)(react@19.2.1) @@ -48,8 +54,8 @@ importers: specifier: ^35.0.1 version: 35.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) better-auth: - specifier: ^1.6.23 - version: 1.6.23(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: ^1.6.26 + version: 1.6.26(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -286,8 +292,8 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@better-auth/core@1.6.23': - resolution: {integrity: sha512-beEhOs0uVeOxYOZKUfIEBd/nQV2Bd4/6wyLxZ0OFkn6CMTK2Vi+hXuZLnyPBeB6RdHpebEoJWiHqwHxBIxgPDQ==} + '@better-auth/core@1.6.26': + resolution: {integrity: sha512-Ud4FqnjIJDvmeo+3bN+OsuVVAiuvFjNERbW7G8/65ictSxCox62gNgTOsJ4YXmbwXzBzyyekStzrupg2qNGEkA==} peerDependencies: '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -303,46 +309,56 @@ packages: '@opentelemetry/api': optional: true - '@better-auth/drizzle-adapter@1.6.23': - resolution: {integrity: sha512-2+/PTVfIP9E7iz6af8TB3lhnowHUj9ljC66kECmHaFEdUqPgzHoWux9epotKwO7XDg2ui4ttWQ8CMeNFLvQeKQ==} + '@better-auth/drizzle-adapter@1.6.26': + resolution: {integrity: sha512-SMvAeeUqEsz0BLtWVVp+HdStAVOwcxs4vPkL/AVmFJ0e9dDKItXl881xWeB/Ze4NgBW6NYmUId9qI4LrdQu3hA==} peerDependencies: - '@better-auth/core': ^1.6.23 + '@better-auth/core': ^1.6.26 '@better-auth/utils': 0.4.2 drizzle-orm: ^0.45.2 peerDependenciesMeta: drizzle-orm: optional: true - '@better-auth/kysely-adapter@1.6.23': - resolution: {integrity: sha512-zbNJsMbG09exfkGyvFqBLLqWoMPAUWjxCuUnEK5AsjbYoZeIjj/QGZgdf4CapVWryKxjA9Q6Jlr6fbiPpC3VAg==} + '@better-auth/kysely-adapter@1.6.26': + resolution: {integrity: sha512-Y0Kdqn8JQMR8dHMtUnbBNqq7Mk8mDf18DwTves2uhjtRfcOWByVTGxsFMDVUbedMuoM+Ab+v04bwCk94sAo8NA==} peerDependencies: - '@better-auth/core': ^1.6.23 + '@better-auth/core': ^1.6.26 '@better-auth/utils': 0.4.2 kysely: ^0.28.17 || ^0.29.0 peerDependenciesMeta: kysely: optional: true - '@better-auth/memory-adapter@1.6.23': - resolution: {integrity: sha512-krIiR0pIVkaKlAzm690n5bcMW4NGbqeMg0HQSD9fz/KcQF/eWLqcq9gG/BhHTj2i/y96qH+W5JWPmaSOS5iTgQ==} + '@better-auth/memory-adapter@1.6.26': + resolution: {integrity: sha512-kb5ahphEp9jyMleXU4T8I/xgWPa8gjOLFKKsnaVqR/BU+h7xqTOsjJwTY3evY+PFYHvjNFUlrjZ1Km8A/w1p/w==} peerDependencies: - '@better-auth/core': ^1.6.23 + '@better-auth/core': ^1.6.26 '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.6.23': - resolution: {integrity: sha512-7+QdevitGlKBbP6JbiSk5SBnzPsKV/mDrQBGBn8hwByQLeJwqpqbuBPw7ZI8vzUlFfAAnyFiqwP3Eb8mxnp7pA==} + '@better-auth/mongo-adapter@1.6.26': + resolution: {integrity: sha512-jYGhuIQqj48h69ROLPYdc8jgqDaMmRRLLFiRViZFYarNyBuYUV07DywJXvRuw8l9ADx+M1TB+BJZGnLE2nnmwQ==} peerDependencies: - '@better-auth/core': ^1.6.23 + '@better-auth/core': ^1.6.26 '@better-auth/utils': 0.4.2 mongodb: ^6.0.0 || ^7.0.0 peerDependenciesMeta: mongodb: optional: true - '@better-auth/prisma-adapter@1.6.23': - resolution: {integrity: sha512-2qSdzidq4tkb1eS5TTqb4Nzg0mdZWm3Qky9SYeXeb8PpVQbC2sxqJhEM5mK7y12uU6I8hc64wO9f7AFVNL+6UQ==} + '@better-auth/passkey@1.6.26': + resolution: {integrity: sha512-qEnXvh5QPHVnqigNBJq/Qur+U9eCa2t4kgdJL+xKEZ4IOxvx55WiGkneNAzIuR7n5saTF6iWtmsbSwMAkWAl9w==} peerDependencies: - '@better-auth/core': ^1.6.23 + '@better-auth/core': ^1.6.26 + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.1 + better-auth: ^1.6.26 + better-call: 1.3.7 + nanostores: ^1.0.1 + + '@better-auth/prisma-adapter@1.6.26': + resolution: {integrity: sha512-ILQoYmnoYyghDP4iN1h/Gkd9Jt7Xs/vxoNAt4AuuJJbqs73u27LlQM4P1YiP7nJ+bM1iVTBI1dDF73myIAtjzQ==} + peerDependencies: + '@better-auth/core': ^1.6.26 '@better-auth/utils': 0.4.2 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -352,10 +368,10 @@ packages: prisma: optional: true - '@better-auth/telemetry@1.6.23': - resolution: {integrity: sha512-/R2Kb+z2BpDOOWwVHqOk+c0VNpuwfCv4Hp5Yr9003WIZPax/zyNraGLB9CFE8qF2gZW8Dsz419k4I8CPrGzpDA==} + '@better-auth/telemetry@1.6.26': + resolution: {integrity: sha512-pBs69RORUSJHRF9r5PdeY7pzLRga09YSWew8igFYrBvT2tLt7DktrbD5WjryLZpAZOuKAMZ+Xo0DlJbnvLWGpQ==} peerDependencies: - '@better-auth/core': ^1.6.23 + '@better-auth/core': ^1.6.26 '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -649,6 +665,9 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@hexagon/base64@1.1.28': + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + '@hono/node-server@1.19.9': resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} @@ -840,6 +859,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@levischuck/tiny-cbor@0.2.11': + resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} + '@mrleebo/prisma-ast@0.13.1': resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==} engines: {node: '>=16'} @@ -940,6 +962,46 @@ packages: '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + '@peculiar/asn1-android@2.8.0': + resolution: {integrity: sha512-skLbS+IOGv1lUgDqtChr8xvtvEr3HMse/JGBaL2r1J1o/n7a8wqOrovMtlRq/UXLhxvmLaONP67hwtshgzwfzA==} + + '@peculiar/asn1-cms@2.8.0': + resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} + + '@peculiar/asn1-csr@2.8.0': + resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==} + + '@peculiar/asn1-ecc@2.8.0': + resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==} + + '@peculiar/asn1-pfx@2.8.0': + resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==} + + '@peculiar/asn1-pkcs8@2.8.0': + resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==} + + '@peculiar/asn1-pkcs9@2.8.0': + resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==} + + '@peculiar/asn1-rsa@2.8.0': + resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==} + + '@peculiar/asn1-schema@2.8.0': + resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==} + + '@peculiar/asn1-x509-attr@2.8.0': + resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==} + + '@peculiar/asn1-x509@2.8.0': + resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + '@prisma/adapter-pg@7.3.0': resolution: {integrity: sha512-iuYQMbIPO6i9O45Fv8TB7vWu00BXhCaNAShenqF7gLExGDbnGp5BfFB4yz1K59zQ59jF6tQ9YHrg0P6/J3OoLg==} @@ -998,9 +1060,28 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + '@radix-ui/number@1.1.3': + resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@radix-ui/primitive@1.1.7': + resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} + + '@radix-ui/react-arrow@1.1.15': + resolution: {integrity: sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-arrow@1.1.7': resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: @@ -1027,6 +1108,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collection@1.1.15': + resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collection@1.1.7': resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: @@ -1049,6 +1143,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-compose-refs@1.1.5': + resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-context@1.1.2': resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: @@ -1067,6 +1170,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.2.2': + resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dialog@1.1.15': resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: @@ -1089,6 +1201,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-direction@1.1.4': + resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dismissable-layer@1.1.11': resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: @@ -1102,6 +1223,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dismissable-layer@1.1.19': + resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dropdown-menu@2.1.16': resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} peerDependencies: @@ -1124,6 +1258,28 @@ packages: '@types/react': optional: true + '@radix-ui/react-focus-guards@1.1.6': + resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.16': + resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-focus-scope@1.1.7': resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} peerDependencies: @@ -1146,6 +1302,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-id@1.1.4': + resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-label@2.1.8': resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==} peerDependencies: @@ -1185,6 +1350,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popper@1.3.7': + resolution: {integrity: sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.17': + resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.1.9': resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: @@ -1198,6 +1389,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.1.10': + resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.1.5': resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: @@ -1211,6 +1415,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.1.10': + resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.1.3': resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: @@ -1250,6 +1467,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-select@2.3.7': + resolution: {integrity: sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -1268,6 +1498,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.3.3': + resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-tabs@1.1.13': resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} peerDependencies: @@ -1303,6 +1542,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-callback-ref@1.1.4': + resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-controllable-state@1.2.2': resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: @@ -1312,6 +1560,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-controllable-state@1.2.6': + resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-effect-event@0.0.2': resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: @@ -1321,6 +1578,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-effect-event@0.0.5': + resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-escape-keydown@1.1.1': resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: @@ -1348,6 +1614,24 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-layout-effect@1.1.4': + resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.4': + resolution: {integrity: sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.1.1': resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: @@ -1357,6 +1641,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-rect@1.1.4': + resolution: {integrity: sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-size@1.1.1': resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: @@ -1366,6 +1659,28 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-size@1.1.4': + resolution: {integrity: sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.11': + resolution: {integrity: sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-visually-hidden@1.2.3': resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: @@ -1382,6 +1697,9 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@radix-ui/rect@1.1.3': + resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} + '@react-email/body@0.2.1': resolution: {integrity: sha512-ljDiQiJDu/Fq//vSIIP0z5Nuvt4+DX1RqGasstChDGJB/14ogd4VdNS9aacoede/ZjGy3o3Qb+cxyS+XgM6SwQ==} engines: {node: '>=20.0.0'} @@ -1688,6 +2006,13 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + '@simplewebauthn/browser@13.3.0': + resolution: {integrity: sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==} + + '@simplewebauthn/server@13.3.2': + resolution: {integrity: sha512-KEDhfcGP1PAKRVSDjA3npTQFqS2b/srm+ipoNBNHdkzrHAlaRQUTE+a5f4ywsx6thxAw1NU2rYcLEY1949RGbQ==} + engines: {node: '>=20.0.0'} + '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} @@ -2220,6 +2545,10 @@ packages: asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -2254,8 +2583,8 @@ packages: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true - better-auth@1.6.23: - resolution: {integrity: sha512-4vOaRd9UiKGKm9R+ej0jjU1es3MiJIiNc9Qq3VCnYqOZ4/nb5272QqTxWYoDxyUXl5x6A2x2we5KZKQO9teTQQ==} + better-auth@1.6.26: + resolution: {integrity: sha512-nhXWrDDj+EnZsHq1j0z1c6DowOMFZWZHe6LCaXbBfLIgHHZm6dyazBQcbRspM4spUIcUt250Mc1BFOSuP7eniQ==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -3843,6 +4172,13 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.2.0: + resolution: {integrity: sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==} + engines: {node: '>=16.0.0'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3906,6 +4242,9 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -4254,6 +4593,9 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -4262,6 +4604,10 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -4666,7 +5012,7 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0)': + '@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0)': dependencies: '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -4678,39 +5024,51 @@ snapshots: nanostores: 1.4.0 zod: 4.3.6 - '@better-auth/drizzle-adapter@1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)': + '@better-auth/drizzle-adapter@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) '@better-auth/utils': 0.4.2 - '@better-auth/kysely-adapter@1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(kysely@0.29.2)': + '@better-auth/kysely-adapter@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(kysely@0.29.2)': dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) '@better-auth/utils': 0.4.2 optionalDependencies: kysely: 0.29.2 - '@better-auth/memory-adapter@1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)': + '@better-auth/memory-adapter@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/utils': 0.4.2 + + '@better-auth/mongo-adapter@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)': + '@better-auth/passkey@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-auth@1.6.26(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)))(better-call@1.3.7(zod@4.3.6))(nanostores@1.4.0)': dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.1 + '@simplewebauthn/browser': 13.3.0 + '@simplewebauthn/server': 13.3.2 + better-auth: 1.6.26(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + better-call: 1.3.7(zod@4.3.6) + nanostores: 1.4.0 + zod: 4.3.6 - '@better-auth/prisma-adapter@1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))': + '@better-auth/prisma-adapter@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))': dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) '@better-auth/utils': 0.4.2 optionalDependencies: '@prisma/client': 7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3) prisma: 7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3) - '@better-auth/telemetry@1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + '@better-auth/telemetry@1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -4928,6 +5286,8 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@hexagon/base64@1.1.28': {} + '@hono/node-server@1.19.9(hono@4.11.4)': dependencies: hono: 4.11.4 @@ -5059,6 +5419,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@levischuck/tiny-cbor@0.2.11': {} + '@mrleebo/prisma-ast@0.13.1': dependencies: chevrotain: 10.5.0 @@ -5127,6 +5489,106 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 + '@peculiar/asn1-android@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-cms@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pfx': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.8.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-csr': 2.8.0 + '@peculiar/asn1-ecc': 2.8.0 + '@peculiar/asn1-pkcs9': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + '@prisma/adapter-pg@7.3.0': dependencies: '@prisma/driver-adapter-utils': 7.3.0 @@ -5214,8 +5676,21 @@ snapshots: react: 19.2.1 react-dom: 19.2.1(react@19.2.1) + '@radix-ui/number@1.1.3': {} + '@radix-ui/primitive@1.1.3': {} + '@radix-ui/primitive@1.1.7': {} + + '@radix-ui/react-arrow@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -5238,6 +5713,18 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-collection@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1) @@ -5256,6 +5743,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-context@1.1.2(@types/react@19.2.7)(react@19.2.1)': dependencies: react: 19.2.1 @@ -5268,6 +5761,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-context@1.2.2(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -5296,6 +5795,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-direction@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -5309,6 +5814,19 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -5330,6 +5848,23 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1) @@ -5348,6 +5883,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-id@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -5401,6 +5943,34 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-popper@1.3.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@floating-ui/react-dom': 2.1.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-rect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/rect': 1.1.3 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-portal@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -5411,6 +5981,15 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-presence@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1) @@ -5421,6 +6000,15 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-primitive@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.1) @@ -5456,6 +6044,36 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-select@2.3.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + aria-hidden: 1.2.6 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + react-remove-scroll: 2.7.2(@types/react@19.2.7)(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-slot@1.2.3(@types/react@19.2.7)(react@19.2.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1) @@ -5470,6 +6088,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-slot@1.3.3(@types/react@19.2.7)(react@19.2.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -5512,6 +6137,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.7)(react@19.2.1)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.7)(react@19.2.1) @@ -5520,6 +6151,15 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.7)(react@19.2.1)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.7)(react@19.2.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1) @@ -5527,6 +6167,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.7)(react@19.2.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.7)(react@19.2.1)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.1) @@ -5547,6 +6194,18 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-use-previous@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.7)(react@19.2.1)': dependencies: '@radix-ui/rect': 1.1.1 @@ -5554,6 +6213,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-rect@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + '@radix-ui/rect': 1.1.3 + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.7)(react@19.2.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1) @@ -5561,6 +6227,22 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-size@1.1.4(@types/react@19.2.7)(react@19.2.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.7)(react@19.2.1) + react: 19.2.1 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-visually-hidden@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -5572,6 +6254,8 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@radix-ui/rect@1.1.3': {} + '@react-email/body@0.2.1(react@19.2.1)': dependencies: react: 19.2.1 @@ -5780,6 +6464,19 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 + '@simplewebauthn/browser@13.3.0': {} + + '@simplewebauthn/server@13.3.2': + dependencies: + '@hexagon/base64': 1.1.28 + '@levischuck/tiny-cbor': 0.2.11 + '@peculiar/asn1-android': 2.8.0 + '@peculiar/asn1-ecc': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/x509': 1.14.3 + '@stablelib/base64@1.0.1': {} '@standard-schema/spec@1.1.0': {} @@ -6351,6 +7048,12 @@ snapshots: minimalistic-assert: 1.0.1 safer-buffer: 2.1.2 + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.2.0 + tslib: 2.8.1 + assertion-error@2.0.1: {} ast-types-flow@0.0.8: {} @@ -6371,15 +7074,15 @@ snapshots: baseline-browser-mapping@2.9.19: {} - better-auth@1.6.23(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): + better-auth@1.6.26(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(mysql2@3.15.3)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(pg@8.18.0)(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(vitest@4.0.18(@types/node@20.19.33)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@2.2.0))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - '@better-auth/core': 1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) - '@better-auth/drizzle-adapter': 1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2) - '@better-auth/kysely-adapter': 1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(kysely@0.29.2) - '@better-auth/memory-adapter': 1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2) - '@better-auth/prisma-adapter': 1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3)) - '@better-auth/telemetry': 1.6.23(@better-auth/core@1.6.23(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) + '@better-auth/core': 1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0) + '@better-auth/drizzle-adapter': 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2) + '@better-auth/kysely-adapter': 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(kysely@0.29.2) + '@better-auth/memory-adapter': 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2) + '@better-auth/prisma-adapter': 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@prisma/client@7.3.0(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.3.0(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3)) + '@better-auth/telemetry': 1.6.26(@better-auth/core@1.6.26(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.3.7(zod@4.3.6))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.4.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 '@noble/ciphers': 2.2.0 @@ -8027,6 +8730,12 @@ snapshots: pure-rand@6.1.0: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.2.0: {} + queue-microtask@1.2.3: {} rc9@2.1.2: @@ -8081,6 +8790,8 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + reflect-metadata@0.2.2: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -8493,6 +9204,8 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@1.14.1: {} + tslib@2.8.1: {} tsx@4.21.0: @@ -8502,6 +9215,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/prisma/migrations/20260810041844_add_passkey/migration.sql b/prisma/migrations/20260810041844_add_passkey/migration.sql new file mode 100644 index 0000000..fa123ab --- /dev/null +++ b/prisma/migrations/20260810041844_add_passkey/migration.sql @@ -0,0 +1,25 @@ +-- CreateTable +CREATE TABLE "Passkey" ( + "id" TEXT NOT NULL, + "name" TEXT, + "publicKey" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "credentialID" TEXT NOT NULL, + "counter" INTEGER NOT NULL, + "deviceType" TEXT NOT NULL, + "backedUp" BOOLEAN NOT NULL, + "transports" TEXT, + "aaguid" TEXT, + "createdAt" TIMESTAMP(3), + + CONSTRAINT "Passkey_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "Passkey_userId_idx" ON "Passkey"("userId"); + +-- CreateIndex +CREATE INDEX "Passkey_credentialID_idx" ON "Passkey"("credentialID"); + +-- AddForeignKey +ALTER TABLE "Passkey" ADD CONSTRAINT "Passkey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema/passkey.prisma b/prisma/schema/passkey.prisma new file mode 100644 index 0000000..25914c7 --- /dev/null +++ b/prisma/schema/passkey.prisma @@ -0,0 +1,18 @@ +model Passkey { + id String @id @default(cuid()) + name String? + publicKey String + userId String + credentialID String + counter Int + deviceType String + backedUp Boolean + transports String? + aaguid String? + createdAt DateTime? + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([credentialID]) +} diff --git a/prisma/schema/user.prisma b/prisma/schema/user.prisma index 358a656..0a72bd2 100644 --- a/prisma/schema/user.prisma +++ b/prisma/schema/user.prisma @@ -8,6 +8,7 @@ model User { pushSubscription String? accounts Account[] sessions Session[] + passkeys Passkey[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/app/globals.css b/src/app/globals.css index 4ca9cf7..dbac366 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -68,6 +68,11 @@ @layer base { :root { + /* Tells the browser how to paint native UI it renders itself: router.push(href)}> + + + + + {tabs.map((tab) => ( + + {tab.name} + + ))} + + + + + + + {tabs.map((tab) => ( + + {tab.name} + + ))} + + + ); } diff --git a/src/components/layout/notification-settings.tsx b/src/components/layout/notification-settings.tsx index 9dac991..697bf4e 100644 --- a/src/components/layout/notification-settings.tsx +++ b/src/components/layout/notification-settings.tsx @@ -77,10 +77,10 @@ export function NotificationSettings({ Notification Settings
- +

Notifications Not Supported

-

+

Your browser does not support push notifications.

@@ -103,9 +103,9 @@ export function NotificationSettings({
- {error &&

{error}

} + {error &&

{error}

} -

+

{isSubscribed ? 'You are subscribed to receive notifications.' : 'Subscribe to receive important updates and notifications.'} diff --git a/src/components/layout/passkey-settings.tsx b/src/components/layout/passkey-settings.tsx new file mode 100644 index 0000000..adfc65e --- /dev/null +++ b/src/components/layout/passkey-settings.tsx @@ -0,0 +1,179 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { useToast } from '@/hooks/use-toast'; +import { authClient } from '@/lib/auth-client'; +import { describeAuthError } from '@/lib/auth-errors'; +import { KeyRound, Trash2 } from 'lucide-react'; +import { useState } from 'react'; + +interface PasskeySettingsProps { + isOpen: boolean; + onOpenChange: (_open: boolean) => void; +} + +const isSupported = () => + typeof window !== 'undefined' && + typeof window.PublicKeyCredential !== 'undefined'; + +export function PasskeySettings({ + isOpen, + onOpenChange, +}: PasskeySettingsProps) { + const { toast } = useToast(); + const { data: passkeys, isPending } = authClient.useListPasskeys(); + const [name, setName] = useState(''); + const [isAdding, setIsAdding] = useState(false); + const [deletingId, setDeletingId] = useState(null); + + const handleAddPasskey = async () => { + setIsAdding(true); + + const result = await authClient.passkey.addPasskey({ + name: name.trim() || undefined, + }); + + setIsAdding(false); + + if (result?.error) { + toast({ + title: 'Could not add passkey', + description: describeAuthError(result.error), + variant: 'destructive', + }); + return; + } + + setName(''); + toast({ + title: 'Passkey added', + description: 'You can now sign in with this passkey.', + }); + }; + + const handleDeletePasskey = async (id: string) => { + setDeletingId(id); + + const { error } = await authClient.passkey.deletePasskey({ id }); + + setDeletingId(null); + + if (error) { + toast({ + title: 'Could not remove passkey', + description: describeAuthError(error), + variant: 'destructive', + }); + return; + } + + toast({ + title: 'Passkey removed', + description: 'That passkey can no longer be used to sign in.', + }); + }; + + return ( +

+ + + Passkeys + + + {!isSupported() ? ( +
+ +
+

Passkeys Not Supported

+

+ Your browser does not support passkeys. +

+
+
+ ) : ( +
+

+ Sign in with your fingerprint, face, or device PIN instead of a + password. +

+ +
+ {isPending ? ( +

+ Loading passkeys... +

+ ) : passkeys && passkeys.length > 0 ? ( +
    + {passkeys.map((passkey) => ( +
  • +
    +

    + {passkey.name || 'Unnamed passkey'} +

    + {passkey.createdAt && ( +

    + Added{' '} + {new Date(passkey.createdAt).toLocaleDateString()} +

    + )} +
    + +
  • + ))} +
+ ) : ( +

+ You have not added any passkeys yet. +

+ )} +
+ +
+ + setName(e.target.value)} + /> +
+ + +
+ )} +
+
+ ); +} diff --git a/src/components/layout/user-menu.tsx b/src/components/layout/user-menu.tsx index b825da1..1b594ea 100644 --- a/src/components/layout/user-menu.tsx +++ b/src/components/layout/user-menu.tsx @@ -11,11 +11,12 @@ import { import { useImpersonation } from '@/components/contexts/impersonation-context'; import { DropdownMenuSeparator } from '@radix-ui/react-dropdown-menu'; import { signOut, useSession } from '@/lib/auth-client'; -import { Bell, LogOut, UserCog, UserX } from 'lucide-react'; +import { Bell, KeyRound, LogOut, UserCog, UserX } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { EditProfileDialog } from './edit-profile-dialog'; import { NotificationSettings } from './notification-settings'; +import { PasskeySettings } from './passkey-settings'; import { ThemeSwitcher } from './theme-switcher'; import { useToast } from '@/hooks/use-toast'; @@ -28,6 +29,7 @@ export function UserMenu() { const [isEditProfileOpen, setIsEditProfileOpen] = useState(false); const [isNotificationSettingsOpen, setIsNotificationSettingsOpen] = useState(false); + const [isPasskeySettingsOpen, setIsPasskeySettingsOpen] = useState(false); const handleStopImpersonation = async () => { try { @@ -98,6 +100,20 @@ export function UserMenu() { Notifications + {/* + Passkey endpoints resolve the real session, not the impersonation + override, so a passkey added here would attach to the admin's own + account. Hide the entry rather than mislead. + */} + {!isImpersonating && ( + setIsPasskeySettingsOpen(true)} + > + + Passkeys + + )} @@ -128,6 +144,10 @@ export function UserMenu() { isOpen={isNotificationSettingsOpen} onOpenChange={setIsNotificationSettingsOpen} /> + ); } diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx index 1346957..bac1528 100644 --- a/src/components/ui/avatar.tsx +++ b/src/components/ui/avatar.tsx @@ -39,7 +39,7 @@ const AvatarFallback = React.forwardRef< , + extends + React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; } diff --git a/src/components/ui/google-icon.tsx b/src/components/ui/google-icon.tsx new file mode 100644 index 0000000..08d5559 --- /dev/null +++ b/src/components/ui/google-icon.tsx @@ -0,0 +1,27 @@ +/** + * Google's four-color "G" mark. Not a lucide icon: lucide does not ship brand + * logos, and this renders with fixed brand colors rather than `currentColor`. + * See https://developers.google.com/identity/branding-guidelines + */ +export function GoogleIcon({ className }: { className?: string }) { + return ( + + ); +} diff --git a/src/components/ui/pagination-controls.tsx b/src/components/ui/pagination-controls.tsx index 3606bbf..fb1c7db 100644 --- a/src/components/ui/pagination-controls.tsx +++ b/src/components/ui/pagination-controls.tsx @@ -89,7 +89,7 @@ export function PaginationControls({ return (
-

+

{totalCount === 0 ? 'No entries found' : `Showing ${startItem} to ${endItem} of ${totalCount} entries`} @@ -113,7 +113,7 @@ export function PaginationControls({ > {size} per page {pageSize === size && ( - + )} ))} diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx new file mode 100644 index 0000000..2b642f7 --- /dev/null +++ b/src/components/ui/select.tsx @@ -0,0 +1,159 @@ +'use client'; + +import * as React from 'react'; +import * as SelectPrimitive from '@radix-ui/react-select'; +import { Check, ChevronDown, ChevronUp } from 'lucide-react'; + +import { cn } from '@/lib/utils'; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1', + className + )} + {...props} + > + {children} + + + + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = 'popper', ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts index 2d3879a..3da9fb4 100644 --- a/src/lib/auth-client.ts +++ b/src/lib/auth-client.ts @@ -1,9 +1,10 @@ import type { auth } from '@/lib/auth'; +import { passkeyClient } from '@better-auth/passkey/client'; import { customSessionClient } from 'better-auth/client/plugins'; import { createAuthClient } from 'better-auth/react'; export const authClient = createAuthClient({ - plugins: [customSessionClient()], + plugins: [customSessionClient(), passkeyClient()], }); export const { useSession, signIn, signUp, signOut } = authClient; diff --git a/src/lib/auth-errors.ts b/src/lib/auth-errors.ts new file mode 100644 index 0000000..4488078 --- /dev/null +++ b/src/lib/auth-errors.ts @@ -0,0 +1,21 @@ +interface AuthErrorLike { + message?: string; + status?: number; +} + +/** + * Better Auth gives a useful message for real WebAuthn outcomes (cancelled + * ceremony, already-registered authenticator), but a failed request can arrive + * with an empty message. Don't guess a cause in that case: an earlier fallback + * here reported "cancelled" for what was actually a server 500, which pointed + * debugging in the wrong direction. + */ +export function describeAuthError(error: AuthErrorLike): string { + if (error.message) return error.message; + + if (error.status && error.status >= 500) { + return 'The server rejected the request. Check the server logs for details.'; + } + + return 'Something went wrong. Please try again.'; +} diff --git a/src/lib/auth.ts b/src/lib/auth.ts index c833457..498662a 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,4 +1,5 @@ import { prisma } from '@/lib/prisma'; +import { passkey } from '@better-auth/passkey'; import type { User } from '@prisma/client'; import { betterAuth } from 'better-auth'; import { prismaAdapter } from 'better-auth/adapters/prisma'; @@ -16,6 +17,18 @@ interface ImpersonatedUser { image: string | null; } +/** + * Passkeys are scoped to this exact hostname, so each app sharing a parent + * domain (template.c4g.dev vs. another-app.c4g.dev) keeps its own credentials. + * Pointing this at the parent domain instead would let any sibling subdomain + * assert them. Falls back to the plugin's own baseURL derivation when unset. + */ +const passkeyRpID = + process.env.PASSKEY_RP_ID || + (process.env.BETTER_AUTH_URL + ? new URL(process.env.BETTER_AUTH_URL).hostname + : undefined); + export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: 'postgresql', @@ -46,6 +59,10 @@ export const auth = betterAuth({ }, }, plugins: [ + passkey({ + rpID: passkeyRpID, + rpName: 'Template', + }), customSession(async ({ user, session }) => { const dbUser = user as typeof user & { role: UserRole }; const sessionUser = {