diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 40f454b..dec4f54 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -4,6 +4,7 @@ RUN apk add --no-cache libc6-compat WORKDIR /app COPY package.json pnpm-lock.yaml* ./ +COPY vendor/ ./vendor/ RUN corepack enable pnpm && pnpm i --frozen-lockfile || npm install # Stage 2: Builder diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index beec102..43f3645 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,12 +1,10 @@ import type { Metadata } from "next"; import { GeistSans } from "geist/font"; import { cookies } from "next/headers"; -import Link from "next/link"; import "./globals.css"; import type { SpaceConfig } from "@/lib/spaces"; import { themeToCSS } from "@/lib/spaces"; -import { AppSwitcher } from "@/components/AppSwitcher"; -import { SpaceSwitcher } from "@/components/SpaceSwitcher"; +import { HeaderBar } from "@/components/HeaderBar"; const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000/api"; @@ -80,77 +78,7 @@ export default async function RootLayout({
- {/* ── Sticky Nav ──────────────────────────────────── */} -
-
- {/* Left: App switcher + Spaces + Logo */} -
- - -
- - {logoUrl ? ( - - ) : ( -
- rSw -
- )} - - r - {name === "rSwag" ? "Swag" : name} - - -
- - {/* Center: Nav links */} - - - {/* Right: Cart */} -
- - - - - -
-
-
+ {/* ── Main Content ────────────────────────────────── */}
{children}
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 325eb00..5ab0f07 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -81,13 +81,13 @@ export default async function HomePage() { ) : ( <> Get Your Community{" "} - + Noticed
with{" "} (you) - + rMerch diff --git a/frontend/components/AuthButton.tsx b/frontend/components/AuthButton.tsx new file mode 100644 index 0000000..07b3c1b --- /dev/null +++ b/frontend/components/AuthButton.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { useState } from 'react'; +import { useAuthStore } from '@/stores/auth'; + +export function AuthButton() { + const { isAuthenticated, username, did, loading, login, register, logout } = useAuthStore(); + const [showRegister, setShowRegister] = useState(false); + const [regUsername, setRegUsername] = useState(''); + const [error, setError] = useState(''); + + if (isAuthenticated) { + return ( +
+
+ Signed in as + {username || did?.slice(0, 16) + '...'} +
+ +
+ ); + } + + if (showRegister) { + return ( +
+ setRegUsername(e.target.value)} + placeholder="Choose a username" + className="text-sm py-1 px-2 w-36 rounded-md bg-white/10 border border-white/10 text-white placeholder:text-white/30 focus:outline-none focus:ring-1 focus:ring-primary" + maxLength={20} + onKeyDown={(e) => { + if (e.key === 'Enter' && regUsername.trim()) { + setError(''); + register(regUsername.trim()).catch((err: Error) => { + setError(err.message || 'Registration failed'); + }); + } + }} + /> + + + {error && {error}} +
+ ); + } + + return ( +
+ + {error && {error}} +
+ ); +} diff --git a/frontend/components/HeaderBar.tsx b/frontend/components/HeaderBar.tsx new file mode 100644 index 0000000..5457102 --- /dev/null +++ b/frontend/components/HeaderBar.tsx @@ -0,0 +1,87 @@ +'use client'; + +import Link from 'next/link'; +import { AppSwitcher } from '@/components/AppSwitcher'; +import { AuthButton } from '@/components/AuthButton'; + +interface HeaderBarProps { + name: string; + logoUrl: string | null; +} + +export function HeaderBar({ name, logoUrl }: HeaderBarProps) { + return ( +
+
+ {/* Left: App switcher + Logo */} +
+ +
+ + {logoUrl ? ( + + ) : ( +
+ rSw +
+ )} + + r + {name === 'rSwag' ? 'Swag' : name} + + +
+ + {/* Center: Nav links */} + + + {/* Right: Auth + Cart */} +
+
+ +
+ + + + + +
+
+
+ ); +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..913199f --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3322 @@ +{ + "name": "rswag-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rswag-frontend", + "version": "0.1.0", + "dependencies": { + "@encryptid/sdk": "file:./vendor/@encryptid/sdk", + "@radix-ui/react-dialog": "^1.1.4", + "@radix-ui/react-dropdown-menu": "^2.1.4", + "@radix-ui/react-label": "^2.1.1", + "@radix-ui/react-radio-group": "^1.2.2", + "@radix-ui/react-select": "^2.1.4", + "@radix-ui/react-separator": "^1.1.1", + "@radix-ui/react-slot": "^1.1.1", + "@radix-ui/react-tabs": "^1.1.2", + "@radix-ui/react-toast": "^1.2.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "geist": "^1.3.0", + "lucide-react": "^0.469.0", + "next": "^15.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.0" + } + }, + "../../encryptid-sdk": { + "name": "@encryptid/sdk", + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", + "hono": "^4.11.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "typescript": "^5.7.0" + }, + "peerDependencies": { + "next": ">=14.0.0", + "react": ">=18.0.0" + }, + "peerDependenciesMeta": { + "next": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@encryptid/sdk": { + "resolved": "vendor/@encryptid/sdk", + "link": true + }, + "node_modules/@floating-ui/core": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", + "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz", + "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.4", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz", + "integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.5" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@next/env": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.12.tgz", + "integrity": "sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.12.tgz", + "integrity": "sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.12.tgz", + "integrity": "sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.12.tgz", + "integrity": "sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.12.tgz", + "integrity": "sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.12.tgz", + "integrity": "sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.12.tgz", + "integrity": "sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.12.tgz", + "integrity": "sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.12.tgz", + "integrity": "sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", + "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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 + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/node": { + "version": "22.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", + "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.24", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001766", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "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" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001774", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/geist": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/geist/-/geist-1.7.0.tgz", + "integrity": "sha512-ZaoiZwkSf0DwwB1ncdLKp+ggAldqxl5L1+SXaNIBGkPAqcu+xjVJLxlf3/S8vLt9UHx1xu5fz3lbzKCj5iOVdQ==", + "license": "SIL OPEN FONT LICENSE", + "peerDependencies": { + "next": ">=13.2.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.2.tgz", + "integrity": "sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/lucide-react": { + "version": "0.469.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.469.0.tgz", + "integrity": "sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.5.12", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.12.tgz", + "integrity": "sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.12", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.12", + "@next/swc-darwin-x64": "15.5.12", + "@next/swc-linux-arm64-gnu": "15.5.12", + "@next/swc-linux-arm64-musl": "15.5.12", + "@next/swc-linux-x64-gnu": "15.5.12", + "@next/swc-linux-x64-musl": "15.5.12", + "@next/swc-win32-arm64-msvc": "15.5.12", + "@next/swc-win32-x64-msvc": "15.5.12", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", + "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/zustand": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", + "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, + "vendor/@encryptid/sdk": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", + "hono": "^4.11.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "typescript": "^5.7.0" + }, + "peerDependencies": { + "next": ">=14.0.0", + "react": ">=18.0.0" + }, + "peerDependenciesMeta": { + "next": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json index 8b4380d..0ed0806 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,10 +9,7 @@ "lint": "next lint" }, "dependencies": { - "geist": "^1.3.0", - "next": "^15.1.0", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "@encryptid/sdk": "file:./vendor/@encryptid/sdk", "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-label": "^2.1.1", @@ -24,9 +21,14 @@ "@radix-ui/react-toast": "^1.2.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "geist": "^1.3.0", "lucide-react": "^0.469.0", + "next": "^15.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "tailwind-merge": "^2.6.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zustand": "^5.0.11" }, "devDependencies": { "@types/node": "^22.10.0", diff --git a/frontend/stores/auth.ts b/frontend/stores/auth.ts new file mode 100644 index 0000000..b27d3dc --- /dev/null +++ b/frontend/stores/auth.ts @@ -0,0 +1,93 @@ +/** + * EncryptID Auth Store for rSwag + * + * Optional authentication via WebAuthn passkeys. + * Zustand with localStorage persistence, delegates WebAuthn ceremony to @encryptid/sdk. + */ + +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; +import { EncryptIDClient } from '@encryptid/sdk/client'; + +const ENCRYPTID_SERVER = process.env.NEXT_PUBLIC_ENCRYPTID_SERVER_URL || 'https://auth.ridentity.online'; +const client = new EncryptIDClient(ENCRYPTID_SERVER); + +interface AuthState { + isAuthenticated: boolean; + token: string | null; + did: string | null; + username: string | null; + loading: boolean; + + login: () => Promise; + register: (username: string) => Promise; + logout: () => void; +} + +export const useAuthStore = create()( + persist( + (set) => ({ + isAuthenticated: false, + token: null, + did: null, + username: null, + loading: false, + + login: async () => { + set({ loading: true }); + try { + const result = await client.authenticate(); + document.cookie = `encryptid_token=${result.token};path=/;max-age=900;SameSite=Lax`; + set({ + isAuthenticated: true, + token: result.token, + did: result.did, + username: result.username, + loading: false, + }); + } catch (error) { + set({ loading: false }); + throw error; + } + }, + + register: async (username: string) => { + set({ loading: true }); + try { + const result = await client.register(username); + document.cookie = `encryptid_token=${result.token};path=/;max-age=900;SameSite=Lax`; + set({ + isAuthenticated: true, + token: result.token, + did: result.did, + username, + loading: false, + }); + } catch (error) { + set({ loading: false }); + throw error; + } + }, + + logout: () => { + document.cookie = 'encryptid_token=;path=/;max-age=0;SameSite=Lax'; + set({ + isAuthenticated: false, + token: null, + did: null, + username: null, + loading: false, + }); + }, + }), + { + name: 'rswag-auth', + partialize: (state) => ({ + isAuthenticated: state.isAuthenticated, + token: state.token, + did: state.did, + username: state.username, + }), + } + ) +); diff --git a/frontend/vendor/@encryptid/sdk/browser.d.ts b/frontend/vendor/@encryptid/sdk/browser.d.ts new file mode 100644 index 0000000..1892ca9 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/browser.d.ts @@ -0,0 +1,86 @@ +/** + * EncryptID Browser Bundle + * + * IIFE entry point that provides window.EncryptID for vanilla JS apps. + * Build: bun build ./src/browser.ts --outfile dist/encryptid.browser.js --target browser + * + * Usage: + * + * + */ +import { EncryptIDClient } from './client/api-client.js'; +import { detectCapabilities } from './client/webauthn.js'; +import { AuthLevel } from './client/session.js'; +import './ui/login-button.js'; +import './ui/guardian-setup.js'; +interface StoredUser { + did: string; + username: string; + token: string; +} +/** + * Authenticate with an existing passkey + */ +declare function authenticate(): Promise; +/** + * Register a new passkey + */ +declare function register(username: string, displayName?: string): Promise; +/** + * Log out — clear stored auth state + */ +declare function logout(): void; +/** + * Check if user is currently authenticated + */ +declare function isAuthenticated(): boolean; +/** + * Get stored user info + */ +declare function getUser(): StoredUser | null; +/** + * Get the stored token + */ +declare function getToken(): string | null; +/** + * Require authentication — redirects to home with login hint if not authenticated + */ +declare function requireAuth(redirectUrl?: string): boolean; +/** + * Set a recovery email for the authenticated user + */ +declare function setRecoveryEmail(email: string): Promise; +/** + * Request account recovery via email + */ +declare function requestRecovery(email: string): Promise; +/** + * Render an auth button into a container element + */ +declare function renderAuthButton(containerId: string): void; +/** + * Verify the stored token is still valid, refresh if needed + */ +declare function verifySession(): Promise; +declare const EncryptID: { + client: EncryptIDClient; + authenticate: typeof authenticate; + register: typeof register; + logout: typeof logout; + isAuthenticated: typeof isAuthenticated; + getUser: typeof getUser; + getToken: typeof getToken; + requireAuth: typeof requireAuth; + setRecoveryEmail: typeof setRecoveryEmail; + requestRecovery: typeof requestRecovery; + renderAuthButton: typeof renderAuthButton; + verifySession: typeof verifySession; + detectCapabilities: typeof detectCapabilities; + AuthLevel: typeof AuthLevel; + VERSION: string; +}; +export default EncryptID; diff --git a/frontend/vendor/@encryptid/sdk/client/api-client.d.ts b/frontend/vendor/@encryptid/sdk/client/api-client.d.ts new file mode 100644 index 0000000..e303ba8 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/api-client.d.ts @@ -0,0 +1,69 @@ +/** + * EncryptID API Client + * + * HTTP client for communicating with the EncryptID server. + * Handles registration, authentication, session management. + */ +import type { RegistrationStartResponse, RegistrationCompleteResponse, AuthStartResponse, AuthCompleteResponse, SessionVerifyResponse, EmailRecoverySetResponse, EmailRecoveryRequestResponse, EmailRecoveryVerifyResponse } from '../types/index.js'; +export declare class EncryptIDClient { + private serverUrl; + constructor(serverUrl?: string); + /** + * Start registration — get challenge and options from server + */ + registerStart(username: string, displayName?: string): Promise; + /** + * Complete registration — send credential to server + */ + registerComplete(challenge: string, credential: PublicKeyCredential, userId: string, username: string): Promise; + /** + * Start authentication — get challenge from server + */ + authStart(credentialId?: string): Promise; + /** + * Complete authentication — send assertion to server + */ + authComplete(challenge: string, credential: PublicKeyCredential): Promise; + /** + * Verify a session token + */ + verifySession(token: string): Promise; + /** + * Refresh a session token + */ + refreshToken(token: string): Promise<{ + token: string; + }>; + /** + * List user's credentials + */ + listCredentials(token: string): Promise<{ + credentials: any[]; + }>; + /** + * Set recovery email for the authenticated user + */ + setRecoveryEmail(token: string, email: string): Promise; + /** + * Request account recovery via email + */ + requestEmailRecovery(email: string): Promise; + /** + * Verify a recovery token and get a temporary session + */ + verifyRecoveryToken(recoveryToken: string): Promise; + /** + * Full registration flow: server challenge → WebAuthn create → server verify + */ + register(username: string, displayName?: string, config?: { + rpId?: string; + }): Promise; + /** + * Full authentication flow: server challenge → WebAuthn get → server verify + */ + authenticate(credentialId?: string, config?: { + rpId?: string; + }): Promise; +} diff --git a/frontend/vendor/@encryptid/sdk/client/index.d.ts b/frontend/vendor/@encryptid/sdk/client/index.d.ts new file mode 100644 index 0000000..2ca0c9f --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/index.d.ts @@ -0,0 +1,13 @@ +/** + * @encryptid/sdk/client — Browser-safe client module + */ +export { registerPasskey, authenticatePasskey, startConditionalUI, abortConditionalUI, isConditionalMediationAvailable, detectCapabilities, bufferToBase64url, base64urlToBuffer, generateChallenge, } from './webauthn.js'; +export type { EncryptIDCredential, AuthenticationResult, EncryptIDConfig, WebAuthnCapabilities } from './webauthn.js'; +export { EncryptIDKeyManager, getKeyManager, resetKeyManager, encryptData, decryptData, decryptDataAsString, signData, verifySignature, wrapKeyForRecipient, unwrapSharedKey, } from './key-derivation.js'; +export type { DerivedKeys, EncryptedData, SignedData } from './key-derivation.js'; +export { SessionManager, getSessionManager, AuthLevel, OPERATION_PERMISSIONS, } from './session.js'; +export type { EncryptIDClaims, SessionState, OperationPermission } from './session.js'; +export { RecoveryManager, getRecoveryManager, GuardianType, getGuardianTypeInfo, } from './recovery.js'; +export type { Guardian, RecoveryConfig, RecoveryRequest } from './recovery.js'; +export { EncryptIDClient } from './api-client.js'; +export { shareTokenAcrossModules, clearTokenAcrossModules, initTokenRelayListener, getStoredToken, requestTokenFromDomain, MODULE_DOMAINS, } from './token-relay.js'; diff --git a/frontend/vendor/@encryptid/sdk/client/index.js b/frontend/vendor/@encryptid/sdk/client/index.js new file mode 100644 index 0000000..53a5b6e --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/index.js @@ -0,0 +1,66 @@ +import { + EncryptIDKeyManager, + OPERATION_PERMISSIONS, + RecoveryManager, + SessionManager, + decryptData, + decryptDataAsString, + encryptData, + getGuardianTypeInfo, + getKeyManager, + getRecoveryManager, + getSessionManager, + resetKeyManager, + signData, + unwrapSharedKey, + verifySignature, + wrapKeyForRecipient +} from "../index-24r9wkfe.js"; +import { + EncryptIDClient +} from "../index-7egxprg9.js"; +import { + abortConditionalUI, + authenticatePasskey, + base64urlToBuffer, + bufferToBase64url, + detectCapabilities, + generateChallenge, + isConditionalMediationAvailable, + registerPasskey, + startConditionalUI +} from "../index-2cp5044h.js"; +import { + AuthLevel, + GuardianType +} from "../index-5c1t4ftn.js"; +export { + wrapKeyForRecipient, + verifySignature, + unwrapSharedKey, + startConditionalUI, + signData, + resetKeyManager, + registerPasskey, + isConditionalMediationAvailable, + getSessionManager, + getRecoveryManager, + getKeyManager, + getGuardianTypeInfo, + generateChallenge, + encryptData, + detectCapabilities, + decryptDataAsString, + decryptData, + bufferToBase64url, + base64urlToBuffer, + authenticatePasskey, + abortConditionalUI, + SessionManager, + RecoveryManager, + OPERATION_PERMISSIONS, + GuardianType, + EncryptIDKeyManager, + EncryptIDClient, + AuthLevel +}; diff --git a/frontend/vendor/@encryptid/sdk/client/key-derivation.d.ts b/frontend/vendor/@encryptid/sdk/client/key-derivation.d.ts new file mode 100644 index 0000000..d1feda7 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/key-derivation.d.ts @@ -0,0 +1,51 @@ +/** + * EncryptID Key Derivation Module + * + * Derives application-specific cryptographic keys from WebAuthn PRF output + * or passphrase fallback. Layer 2 of the EncryptID architecture. + */ +import type { DerivedKeys, EncryptedData, SignedData } from '../types/index.js'; +export type { DerivedKeys, EncryptedData, SignedData }; +export declare class EncryptIDKeyManager { + private masterKey; + private derivedKeys; + private fromPRF; + initFromPRF(prfOutput: ArrayBuffer): Promise; + initFromPassphrase(passphrase: string, salt: Uint8Array): Promise; + static generateSalt(): Uint8Array; + isInitialized(): boolean; + getKeys(): Promise; + private deriveEncryptionKey; + private deriveSigningKeyPair; + /** + * Derive deterministic secp256k1 keys from the master key via HKDF. + * This gives every EncryptID identity an Ethereum-compatible wallet address, + * enabling them to act as Gnosis Safe owners for multi-sig approvals. + */ + private deriveEthereumKeys; + private deriveDIDSeed; + private generateDID; + clear(): void; +} +export declare function encryptData(key: CryptoKey, data: ArrayBuffer | Uint8Array | string): Promise; +export declare function decryptData(key: CryptoKey, encrypted: EncryptedData): Promise; +export declare function decryptDataAsString(key: CryptoKey, encrypted: EncryptedData): Promise; +export declare function signData(keyPair: CryptoKeyPair, data: ArrayBuffer | Uint8Array | string): Promise; +export declare function verifySignature(signed: SignedData): Promise; +export declare function wrapKeyForRecipient(keyToWrap: CryptoKey, recipientPublicKey: CryptoKey): Promise; +export declare function unwrapSharedKey(wrappedKey: ArrayBuffer, privateKey: CryptoKey): Promise; +/** + * Sign an Ethereum-compatible message hash with a secp256k1 private key. + * Returns { r, s, v } components for Safe transaction signing. + * + * @param hash - 32-byte message hash (e.g. keccak256 of the message) + * @param privateKey - 32-byte secp256k1 private key + */ +export declare function signEthHash(hash: Uint8Array, privateKey: Uint8Array): { + r: string; + s: string; + v: number; + signature: Uint8Array; +}; +export declare function getKeyManager(): EncryptIDKeyManager; +export declare function resetKeyManager(): void; diff --git a/frontend/vendor/@encryptid/sdk/client/recovery.d.ts b/frontend/vendor/@encryptid/sdk/client/recovery.d.ts new file mode 100644 index 0000000..0a25706 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/recovery.d.ts @@ -0,0 +1,37 @@ +/** + * EncryptID Social Recovery Module + * + * Guardian-based account recovery with NO SEED PHRASES. + */ +import type { Guardian, RecoveryConfig, RecoveryRequest } from '../types/index.js'; +import { GuardianType } from '../types/index.js'; +export { GuardianType }; +export type { Guardian, RecoveryConfig, RecoveryRequest }; +export declare class RecoveryManager { + private config; + private activeRequest; + constructor(); + initializeRecovery(threshold?: number): Promise; + addGuardian(guardian: Omit): Promise; + removeGuardian(guardianId: string): Promise; + setThreshold(threshold: number): Promise; + setDelay(delaySeconds: number): Promise; + getConfig(): RecoveryConfig | null; + isConfigured(): boolean; + verifyGuardian(guardianId: string): Promise; + initiateRecovery(newCredentialId: string): Promise; + approveRecovery(guardianId: string, signature: string): Promise; + cancelRecovery(): Promise; + completeRecovery(): Promise; + getActiveRequest(): RecoveryRequest | null; + private hashGuardianList; + private saveConfig; + private loadConfig; +} +export declare function getRecoveryManager(): RecoveryManager; +export declare function getGuardianTypeInfo(type: GuardianType): { + name: string; + description: string; + icon: string; + setupInstructions: string; +}; diff --git a/frontend/vendor/@encryptid/sdk/client/session.d.ts b/frontend/vendor/@encryptid/sdk/client/session.d.ts new file mode 100644 index 0000000..2989a78 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/session.d.ts @@ -0,0 +1,35 @@ +/** + * EncryptID Session Management + * + * Handles session tokens, cross-app SSO, and authentication levels. + */ +import type { AuthenticationResult, EncryptIDClaims, SessionState, OperationPermission } from '../types/index.js'; +import { AuthLevel } from '../types/index.js'; +export { AuthLevel }; +export type { EncryptIDClaims, SessionState, OperationPermission }; +export declare const OPERATION_PERMISSIONS: Record; +export declare class SessionManager { + private session; + private refreshTimer; + constructor(); + createSession(authResult: AuthenticationResult, did: string, capabilities: EncryptIDClaims['eid']['capabilities'], walletAddress?: string, username?: string): Promise; + getSession(): SessionState | null; + getDID(): string | null; + getAccessToken(): string | null; + getAuthLevel(): AuthLevel; + canPerform(operation: string): { + allowed: boolean; + reason?: string; + }; + requiresFreshAuth(operation: string): boolean; + upgradeAuthLevel(level?: AuthLevel): void; + clearSession(): void; + isValid(): boolean; + private createUnsignedToken; + private createRefreshToken; + private persistSession; + private restoreSession; + private scheduleRefresh; + private refreshTokens; +} +export declare function getSessionManager(): SessionManager; diff --git a/frontend/vendor/@encryptid/sdk/client/token-relay.d.ts b/frontend/vendor/@encryptid/sdk/client/token-relay.d.ts new file mode 100644 index 0000000..a95703f --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/token-relay.d.ts @@ -0,0 +1,58 @@ +/** + * EncryptID Token Relay — Cross-Domain Authentication + * + * Since .online is a public suffix, cookies can't be shared across + * r*.online domains. This module uses postMessage via hidden iframes + * to relay the JWT token to sibling modules after authentication. + * + * Usage (on the authenticating domain, e.g., rspace.online): + * + * import { shareTokenAcrossModules, MODULE_DOMAINS } from '@encryptid/sdk/client/token-relay'; + * await shareTokenAcrossModules(jwt, MODULE_DOMAINS); + * + * Usage (on each module, add a relay page at /auth/relay): + * + * import { initTokenRelayListener } from '@encryptid/sdk/client/token-relay'; + * initTokenRelayListener(); // Listens for postMessage, stores token + */ +/** All r*.online module domains for token relay */ +export declare const MODULE_DOMAINS: readonly ["rvote.online", "rnotes.online", "rmaps.online", "rcal.online", "rfunds.online", "rtube.online", "rfiles.online", "rmail.online", "rtrips.online", "rnetwork.online", "rwallet.online", "rstack.online", "rspace.online"]; +interface RelayResult { + domain: string; + success: boolean; + error?: string; +} +/** + * Share an EncryptID JWT token across all r*.online module domains. + * Creates hidden iframes pointing to each module's /auth/relay page, + * then sends the token via postMessage. + * + * @param token - The JWT token to share + * @param domains - Array of domains to relay to (defaults to MODULE_DOMAINS) + * @param timeout - Timeout per domain in ms (default 5000) + * @returns Results for each domain + */ +export declare function shareTokenAcrossModules(token: string, domains?: readonly string[], timeout?: number): Promise; +/** + * Clear the token from all module domains. + * Call this on sign-out. + */ +export declare function clearTokenAcrossModules(domains?: readonly string[], timeout?: number): Promise; +/** + * Initialize the token relay listener on a module's /auth/relay page. + * Listens for postMessage from sibling r*.online domains and stores + * the token in localStorage. + * + * This should be called on a minimal page served at /auth/relay on each module. + */ +export declare function initTokenRelayListener(): void; +/** + * Get the locally stored EncryptID token, if any. + */ +export declare function getStoredToken(): string | null; +/** + * Request the token from a sibling domain if we don't have it locally. + * Creates a hidden iframe to the specified domain and asks for the token. + */ +export declare function requestTokenFromDomain(domain: string, timeout?: number): Promise; +export {}; diff --git a/frontend/vendor/@encryptid/sdk/client/webauthn.d.ts b/frontend/vendor/@encryptid/sdk/client/webauthn.d.ts new file mode 100644 index 0000000..6bf3152 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/client/webauthn.d.ts @@ -0,0 +1,21 @@ +/** + * EncryptID WebAuthn Module + * + * Handles passkey registration, authentication, and PRF extension + * for key derivation. This is the foundation layer of EncryptID. + */ +import type { EncryptIDCredential, AuthenticationResult, EncryptIDConfig, WebAuthnCapabilities } from '../types/index.js'; +export type { EncryptIDCredential, AuthenticationResult, EncryptIDConfig, WebAuthnCapabilities }; +/** + * Abort any pending conditional UI request + */ +export declare function abortConditionalUI(): void; +export declare function bufferToBase64url(buffer: ArrayBuffer): string; +export declare function base64urlToBuffer(base64url: string): ArrayBuffer; +export declare function generateChallenge(): ArrayBuffer; +export declare function generatePRFSalt(purpose: string): Promise; +export declare function registerPasskey(username: string, displayName: string, config?: Partial): Promise; +export declare function authenticatePasskey(credentialId?: string, config?: Partial): Promise; +export declare function isConditionalMediationAvailable(): Promise; +export declare function startConditionalUI(config?: Partial): Promise; +export declare function detectCapabilities(): Promise; diff --git a/frontend/vendor/@encryptid/sdk/encryptid.browser.js b/frontend/vendor/@encryptid/sdk/encryptid.browser.js new file mode 100644 index 0000000..72ee499 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/encryptid.browser.js @@ -0,0 +1,71 @@ +var b={rpId:"jeffemmett.com",rpName:"EncryptID",origin:typeof window<"u"?window.location.origin:"",userVerification:"required",timeout:60000},u=null;function R(){if(u)u.abort(),u=null}function a(i){let t=new Uint8Array(i),o="";for(let r=0;r({error:"Registration start failed"}));throw Error(r.error||`HTTP ${o.status}`)}return o.json()}async registerComplete(i,t,o,r){let n=t.response,s=n.getPublicKey(),e=await fetch(`${this.serverUrl}/api/register/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({challenge:i,userId:o,username:r,credential:{credentialId:a(t.rawId),publicKey:s?a(s):"",transports:n.getTransports?.()||[]}})});if(!e.ok){let d=await e.json().catch(()=>({error:"Registration complete failed"}));throw Error(d.error||`HTTP ${e.status}`)}return e.json()}async authStart(i){let t=await fetch(`${this.serverUrl}/api/auth/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i?{credentialId:i}:{})});if(!t.ok){let o=await t.json().catch(()=>({error:"Auth start failed"}));throw Error(o.error||`HTTP ${t.status}`)}return t.json()}async authComplete(i,t){let o=t.response,r=t.getClientExtensionResults()?.prf?.results,n=await fetch(`${this.serverUrl}/api/auth/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({challenge:i,credential:{credentialId:a(t.rawId),signature:a(o.signature),authenticatorData:a(o.authenticatorData),prfOutput:r?.first?a(r.first):null}})});if(!n.ok){let s=await n.json().catch(()=>({error:"Auth complete failed"}));throw Error(s.error||`HTTP ${n.status}`)}return n.json()}async verifySession(i){return(await fetch(`${this.serverUrl}/api/session/verify`,{headers:{Authorization:`Bearer ${i}`}})).json()}async refreshToken(i){let t=await fetch(`${this.serverUrl}/api/session/refresh`,{method:"POST",headers:{Authorization:`Bearer ${i}`}});if(!t.ok){let o=await t.json().catch(()=>({error:"Token refresh failed"}));throw Error(o.error||`HTTP ${t.status}`)}return t.json()}async listCredentials(i){let t=await fetch(`${this.serverUrl}/api/user/credentials`,{headers:{Authorization:`Bearer ${i}`}});if(!t.ok)throw Error("Failed to list credentials");return t.json()}async setRecoveryEmail(i,t){let o=await fetch(`${this.serverUrl}/api/recovery/email/set`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({email:t})});if(!o.ok){let r=await o.json().catch(()=>({error:"Failed to set recovery email"}));throw Error(r.error||`HTTP ${o.status}`)}return o.json()}async requestEmailRecovery(i){let t=await fetch(`${this.serverUrl}/api/recovery/email/request`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:i})});if(!t.ok){let o=await t.json().catch(()=>({error:"Recovery request failed"}));throw Error(o.error||`HTTP ${t.status}`)}return t.json()}async verifyRecoveryToken(i){let t=await fetch(`${this.serverUrl}/api/recovery/email/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:i})});if(!t.ok){let o=await t.json().catch(()=>({error:"Recovery verification failed"}));throw Error(o.error||`HTTP ${t.status}`)}return t.json()}async register(i,t,o){let{options:r,userId:n}=await this.registerStart(i,t),s={publicKey:{...r,challenge:l(r.challenge),user:{...r.user,id:l(r.user.id)},pubKeyCredParams:r.pubKeyCredParams,extensions:{credProps:!0,prf:{eval:{first:new Uint8Array(32)}}}}},e=await navigator.credentials.create(s);if(!e)throw Error("Failed to create credential");return this.registerComplete(r.challenge,e,n,i)}async authenticate(i,t){let{options:o}=await this.authStart(i),r={publicKey:{challenge:l(o.challenge),rpId:o.rpId,userVerification:o.userVerification,timeout:o.timeout,allowCredentials:o.allowCredentials?.map((d)=>({type:d.type,id:l(d.id),transports:d.transports})),extensions:{prf:{eval:{first:new Uint8Array(32)}}}}},n=await navigator.credentials.get(r);if(!n)throw Error("Authentication failed");let s=await this.authComplete(o.challenge,n),e=n.getClientExtensionResults()?.prf?.results;return{...s,prfOutput:e?.first}}}function l(i){let t=i.replace(/-/g,"+").replace(/_/g,"/"),o="=".repeat((4-t.length%4)%4),r=atob(t+o),n=new Uint8Array(r.length);for(let s=0;s{n[n.BASIC=1]="BASIC";n[n.STANDARD=2]="STANDARD";n[n.ELEVATED=3]="ELEVATED";n[n.CRITICAL=4]="CRITICAL"})(c||={});var Z={"rspace:view-public":{minAuthLevel:1},"rspace:view-private":{minAuthLevel:2},"rspace:edit-board":{minAuthLevel:2},"rspace:create-board":{minAuthLevel:2},"rspace:delete-board":{minAuthLevel:3,maxAgeSeconds:300},"rspace:encrypt-board":{minAuthLevel:3,requiresCapability:"encrypt"},"rwallet:view-balance":{minAuthLevel:1},"rwallet:view-history":{minAuthLevel:2},"rwallet:send-small":{minAuthLevel:2,requiresCapability:"wallet"},"rwallet:send-large":{minAuthLevel:3,requiresCapability:"wallet",maxAgeSeconds:60},"rwallet:add-guardian":{minAuthLevel:4,maxAgeSeconds:60},"rwallet:remove-guardian":{minAuthLevel:4,maxAgeSeconds:60},"rvote:view-proposals":{minAuthLevel:1},"rvote:cast-vote":{minAuthLevel:3,requiresCapability:"sign",maxAgeSeconds:300},"rvote:delegate":{minAuthLevel:3,requiresCapability:"wallet"},"rfiles:list-files":{minAuthLevel:2},"rfiles:download-own":{minAuthLevel:2,requiresCapability:"encrypt"},"rfiles:upload":{minAuthLevel:2,requiresCapability:"encrypt"},"rfiles:share":{minAuthLevel:3,requiresCapability:"encrypt"},"rfiles:delete":{minAuthLevel:3,maxAgeSeconds:300},"rfiles:export-keys":{minAuthLevel:4,maxAgeSeconds:60},"rmaps:view-public":{minAuthLevel:1},"rmaps:add-location":{minAuthLevel:2},"rmaps:edit-location":{minAuthLevel:2,requiresCapability:"sign"},"account:view-profile":{minAuthLevel:2},"account:edit-profile":{minAuthLevel:3},"account:export-data":{minAuthLevel:4,maxAgeSeconds:60},"account:delete":{minAuthLevel:4,maxAgeSeconds:60},"rspace:create-space":{minAuthLevel:2},"rspace:configure-space":{minAuthLevel:3,maxAgeSeconds:300},"rspace:delete-space":{minAuthLevel:4,maxAgeSeconds:60},"rspace:invite-member":{minAuthLevel:2},"rspace:remove-member":{minAuthLevel:3,maxAgeSeconds:300},"rspace:change-visibility":{minAuthLevel:3,maxAgeSeconds:300},"rfunds:create-space":{minAuthLevel:2},"rfunds:edit-flows":{minAuthLevel:2},"rfunds:share-space":{minAuthLevel:2}},x="encryptid_session",A=300000;class j{session=null;refreshTimer=null;constructor(){this.restoreSession()}async createSession(i,t,o){let r=Math.floor(Date.now()/1000),n={iss:"https://encryptid.jeffemmett.com",sub:t,aud:["rspace.online","rwallet.online","rvote.online","rfiles.online","rmaps.online"],iat:r,exp:r+900,jti:a(crypto.getRandomValues(new Uint8Array(16)).buffer),eid:{credentialId:i.credentialId,authLevel:3,authTime:r,capabilities:o,recoveryConfigured:!1}},s=this.createUnsignedToken(n),e=this.createRefreshToken(t);return this.session={accessToken:s,refreshToken:e,claims:n,lastAuthTime:Date.now()},this.persistSession(),this.scheduleRefresh(),this.session}getSession(){return this.session}getDID(){return this.session?.claims.sub??null}getAccessToken(){return this.session?.accessToken??null}getAuthLevel(){if(!this.session)return 1;let i=Math.floor(Date.now()/1000);if(i>=this.session.claims.exp)return 1;let t=i-this.session.claims.eid.authTime;if(t<60)return 3;if(t<900)return 2;return 1}canPerform(i){let t=Z[i];if(!t)return{allowed:!1,reason:"Unknown operation"};if(!this.session)return{allowed:!1,reason:"Not authenticated"};let o=this.getAuthLevel();if(ot.maxAgeSeconds)return{allowed:!1,reason:`Authentication too old (${r}s > ${t.maxAgeSeconds}s)`}}return{allowed:!0}}requiresFreshAuth(i){let t=Z[i];if(!t)return!0;if(t.minAuthLevel>=4)return!0;if(t.maxAgeSeconds&&t.maxAgeSeconds<=60)return!0;return!1}upgradeAuthLevel(i=3){if(!this.session)return;this.session.claims.eid.authLevel=i,this.session.claims.eid.authTime=Math.floor(Date.now()/1000),this.session.lastAuthTime=Date.now(),this.persistSession()}clearSession(){if(this.session=null,this.refreshTimer)clearTimeout(this.refreshTimer),this.refreshTimer=null;try{localStorage.removeItem(x)}catch{}}isValid(){if(!this.session)return!1;return Math.floor(Date.now()/1000)this.refreshTokens(),o)}async refreshTokens(){if(!this.session)return;let i=Math.floor(Date.now()/1000);this.session.claims.eid.authLevel=Math.min(this.session.claims.eid.authLevel,2),this.session.claims.iat=i,this.session.claims.exp=i+900,this.session.claims.jti=a(crypto.getRandomValues(new Uint8Array(16)).buffer),this.session.accessToken=this.createUnsignedToken(this.session.claims),this.persistSession(),this.scheduleRefresh()}}var $=null;function g(){if(!$)$=new j;return $}class F{masterKey=null;derivedKeys=null;fromPRF=!1;async initFromPRF(i){this.masterKey=await crypto.subtle.importKey("raw",i,{name:"HKDF"},!1,["deriveKey","deriveBits"]),this.fromPRF=!0,this.derivedKeys=null}async initFromPassphrase(i,t){let o=new TextEncoder,r=await crypto.subtle.importKey("raw",o.encode(i),{name:"PBKDF2"},!1,["deriveBits"]),n=await crypto.subtle.deriveBits({name:"PBKDF2",salt:t,iterations:600000,hash:"SHA-256"},r,256);this.masterKey=await crypto.subtle.importKey("raw",n,{name:"HKDF"},!1,["deriveKey","deriveBits"]),this.fromPRF=!1,this.derivedKeys=null}static generateSalt(){return crypto.getRandomValues(new Uint8Array(32))}isInitialized(){return this.masterKey!==null}async getKeys(){if(!this.masterKey)throw Error("Key manager not initialized");if(this.derivedKeys)return this.derivedKeys;let[i,t,o]=await Promise.all([this.deriveEncryptionKey(),this.deriveSigningKeyPair(),this.deriveDIDSeed()]),r=await this.generateDID(o);return this.derivedKeys={encryptionKey:i,signingKeyPair:t,didSeed:o,did:r,fromPRF:this.fromPRF},this.derivedKeys}async deriveEncryptionKey(){let i=new TextEncoder;return crypto.subtle.deriveKey({name:"HKDF",hash:"SHA-256",salt:i.encode("encryptid-encryption-key-v1"),info:i.encode("AES-256-GCM")},this.masterKey,{name:"AES-GCM",length:256},!1,["encrypt","decrypt","wrapKey","unwrapKey"])}async deriveSigningKeyPair(){return crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign","verify"])}async deriveDIDSeed(){let i=new TextEncoder,t=await crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:i.encode("encryptid-did-key-v1"),info:i.encode("Ed25519-seed")},this.masterKey,256);return new Uint8Array(t)}async generateDID(i){let t=await crypto.subtle.digest("SHA-256",i),o=new Uint8Array(t).slice(0,32),r=new Uint8Array([237,1]),n=new Uint8Array(34);return n.set(r),n.set(o,2),`did:key:z${a(n.buffer).replace(/-/g,"").replace(/_/g,"")}`}clear(){this.masterKey=null,this.derivedKeys=null,this.fromPRF=!1}}var E=null;function y(){if(!E)E=new F;return E}var ii='',ti=` +:host { --eid-primary: #06b6d4; --eid-primary-hover: #0891b2; --eid-bg: #0f172a; --eid-bg-hover: #1e293b; --eid-text: #f1f5f9; --eid-text-secondary: #94a3b8; --eid-radius: 8px; display: inline-block; font-family: system-ui, -apple-system, sans-serif; } +.login-btn { display: flex; align-items: center; gap: 12px; padding: 12px 24px; background: var(--eid-primary); color: white; border: none; border-radius: var(--eid-radius); font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3); } +.login-btn:hover { background: var(--eid-primary-hover); transform: translateY(-1px); } +.login-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } +.login-btn.outline { background: transparent; border: 2px solid var(--eid-primary); color: var(--eid-primary); } +.login-btn.outline:hover { background: var(--eid-primary); color: white; } +.login-btn.small { padding: 8px 16px; font-size: 0.875rem; } +.login-btn.large { padding: 16px 32px; font-size: 1.125rem; } +.passkey-icon { width: 24px; height: 24px; } +.user-info { display: flex; align-items: center; gap: 12px; padding: 8px 16px; background: var(--eid-bg); border-radius: var(--eid-radius); color: var(--eid-text); cursor: pointer; } +.user-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--eid-primary); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 0.875rem; } +.user-did { font-size: 0.75rem; color: var(--eid-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; } +.auth-level { font-size: 0.625rem; padding: 2px 6px; border-radius: 4px; } +.auth-level.elevated { background: rgba(34, 197, 94, 0.2); color: #22c55e; } +.auth-level.standard { background: rgba(234, 179, 8, 0.2); color: #eab308; } +.dropdown { position: absolute; top: 100%; right: 0; margin-top: 8px; background: var(--eid-bg); border-radius: var(--eid-radius); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3); min-width: 200px; z-index: 100; overflow: hidden; } +.dropdown-item { display: flex; align-items: center; gap: 12px; padding: 12px 16px; color: var(--eid-text); cursor: pointer; transition: background 0.2s; } +.dropdown-item:hover { background: var(--eid-bg-hover); } +.dropdown-item.danger { color: #ef4444; } +.dropdown-divider { height: 1px; background: #334155; margin: 4px 0; } +.loading-spinner { width: 20px; height: 20px; border: 2px solid transparent; border-top-color: currentColor; border-radius: 50%; animation: spin 0.8s linear infinite; } +@keyframes spin { to { transform: rotate(360deg); } } +`;class Y extends HTMLElement{shadow;loading=!1;showDropdown=!1;capabilities=null;static get observedAttributes(){return["size","variant","label","show-user"]}constructor(){super();this.shadow=this.attachShadow({mode:"open"})}async connectedCallback(){if(this.capabilities=await m(),this.capabilities.conditionalUI)this.startConditionalAuth();this.render(),document.addEventListener("click",(i)=>{if(!this.contains(i.target))this.showDropdown=!1,this.render()})}attributeChangedCallback(){this.render()}get size(){return this.getAttribute("size")||"medium"}get variant(){return this.getAttribute("variant")||"primary"}get label(){return this.getAttribute("label")||"Sign in with Passkey"}get showUser(){return this.hasAttribute("show-user")}render(){let i=g(),t=i.isValid(),o=i.getDID(),r=i.getAuthLevel();this.shadow.innerHTML=``,this.attachEventListeners()}renderLoginButton(){let i=this.size==="medium"?"":this.size,t=this.variant==="primary"?"":this.variant;return``}renderUserInfo(i,t){let o=i.slice(0,20)+"..."+i.slice(-8),r=i.slice(8,10).toUpperCase(),n=c[t].toLowerCase();return``}renderDropdown(){return``}attachEventListeners(){if(g().isValid()&&this.showUser)this.shadow.querySelector(".user-info")?.addEventListener("click",()=>{this.showDropdown=!this.showDropdown,this.render()}),this.shadow.querySelectorAll(".dropdown-item").forEach((t)=>{t.addEventListener("click",(o)=>{o.stopPropagation(),this.handleDropdownAction(t.dataset.action)})});else this.shadow.querySelector(".login-btn")?.addEventListener("click",()=>this.handleLogin())}async handleLogin(){if(this.loading)return;this.loading=!0,this.render();try{let i=await D(),t=y();if(i.prfOutput)await t.initFromPRF(i.prfOutput);let o=await t.getKeys();await g().createSession(i,o.did,{encrypt:!0,sign:!0,wallet:!1}),this.dispatchEvent(new CustomEvent("login-success",{detail:{did:o.did,credentialId:i.credentialId,prfAvailable:!!i.prfOutput},bubbles:!0}))}catch(i){if(i.name==="NotAllowedError"||i.message?.includes("No credential"))this.dispatchEvent(new CustomEvent("login-register-needed",{bubbles:!0}));else this.dispatchEvent(new CustomEvent("login-error",{detail:{error:i.message},bubbles:!0}))}finally{this.loading=!1,this.render()}}async handleDropdownAction(i){if(this.showDropdown=!1,i==="logout")g().clearSession(),y().clear(),this.dispatchEvent(new CustomEvent("logout",{bubbles:!0}));else if(i==="upgrade")try{await D(),g().upgradeAuthLevel(3),this.dispatchEvent(new CustomEvent("auth-upgraded",{detail:{level:3},bubbles:!0}))}catch{}else this.dispatchEvent(new CustomEvent("navigate",{detail:{path:`/${i}`},bubbles:!0}));this.render()}async startConditionalAuth(){try{let i=await X();if(i){let t=y();if(i.prfOutput)await t.initFromPRF(i.prfOutput);let o=await t.getKeys();await g().createSession(i,o.did,{encrypt:!0,sign:!0,wallet:!1}),this.dispatchEvent(new CustomEvent("login-success",{detail:{did:o.did,credentialId:i.credentialId,viaConditionalUI:!0},bubbles:!0})),this.render()}}catch{}}async register(i,t){this.loading=!0,this.render();try{let o=await Q(i,t);this.dispatchEvent(new CustomEvent("register-success",{detail:{credentialId:o.credentialId,prfSupported:o.prfSupported},bubbles:!0})),await this.handleLogin()}catch(o){this.dispatchEvent(new CustomEvent("register-error",{detail:{error:o.message},bubbles:!0}))}finally{this.loading=!1,this.render()}}}customElements.define("encryptid-login",Y);class S{config=null;activeRequest=null;constructor(){this.loadConfig()}async initializeRecovery(i=3){return this.config={threshold:i,delaySeconds:172800,guardians:[],guardianListHash:"",updatedAt:Date.now()},await this.saveConfig(),this.config}async addGuardian(i){if(!this.config)throw Error("Recovery not initialized");if(this.config.guardians.length>=7)throw Error("Maximum of 7 guardians allowed");let t={...i,id:a(crypto.getRandomValues(new Uint8Array(16)).buffer),addedAt:Date.now()};return this.config.guardians.push(t),this.config.guardianListHash=await this.hashGuardianList(),this.config.updatedAt=Date.now(),await this.saveConfig(),t}async removeGuardian(i){if(!this.config)throw Error("Recovery not initialized");let t=this.config.guardians.findIndex((r)=>r.id===i);if(t===-1)throw Error("Guardian not found");if(this.config.guardians.filter((r)=>r.id!==i).reduce((r,n)=>r+n.weight,0)o+r.weight,0);if(i>t)throw Error("Threshold cannot exceed total guardian weight");if(i<1)throw Error("Threshold must be at least 1");this.config.threshold=i,this.config.updatedAt=Date.now(),await this.saveConfig()}async setDelay(i){if(!this.config)throw Error("Recovery not initialized");if(i<3600||i>604800)throw Error("Delay must be between 1 hour and 7 days");this.config.delaySeconds=i,this.config.updatedAt=Date.now(),await this.saveConfig()}getConfig(){return this.config}isConfigured(){if(!this.config)return!1;return this.config.guardians.reduce((i,t)=>i+t.weight,0)>=this.config.threshold}async verifyGuardian(i){if(!this.config)throw Error("Recovery not initialized");let t=this.config.guardians.find((o)=>o.id===i);if(!t)throw Error("Guardian not found");return t.lastVerified=Date.now(),await this.saveConfig(),!0}async initiateRecovery(i){if(!this.config)throw Error("Recovery not configured");if(this.activeRequest?.status==="pending")throw Error("Recovery already in progress");let t=Date.now();return this.activeRequest={id:a(crypto.getRandomValues(new Uint8Array(16)).buffer),accountDID:"",newCredentialId:i,initiatedAt:t,completesAt:t+this.config.delaySeconds*1000,status:"pending",approvals:[],approvalWeight:0},this.activeRequest}async approveRecovery(i,t){if(!this.activeRequest||this.activeRequest.status!=="pending")throw Error("No pending recovery request");if(!this.config)throw Error("Recovery not configured");let o=this.config.guardians.find((r)=>r.id===i);if(!o)throw Error("Guardian not found");if(this.activeRequest.approvals.some((r)=>r.guardianId===i))throw Error("Guardian already approved");if(this.activeRequest.approvals.push({guardianId:i,approvedAt:Date.now(),signature:t}),this.activeRequest.approvalWeight+=o.weight,this.activeRequest.approvalWeight>=this.config.threshold)this.activeRequest.status="approved";return this.activeRequest}async cancelRecovery(){if(!this.activeRequest||this.activeRequest.status!=="pending")throw Error("No pending recovery request to cancel");this.activeRequest.status="cancelled",this.activeRequest=null}async completeRecovery(){if(!this.activeRequest)throw Error("No recovery request");if(this.activeRequest.status!=="approved")throw Error("Recovery not approved");if(Date.now()o.id).sort().join(","),t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(i));return a(t)}async saveConfig(){if(!this.config)return;try{localStorage.setItem("encryptid_recovery",JSON.stringify(this.config))}catch{}}loadConfig(){try{let i=localStorage.getItem("encryptid_recovery");if(i)this.config=JSON.parse(i)}catch{}}}var k=null;function G(){if(!k)k=new S;return k}function N(i){switch(i){case"secondary_passkey":return{name:"Backup Passkey",description:"Another device you own (phone, YubiKey, etc.)",icon:"key",setupInstructions:"Register a passkey on a second device you control."};case"trusted_contact":return{name:"Trusted Contact",description:"A friend or family member with their own EncryptID",icon:"user",setupInstructions:"Ask a trusted person to create an EncryptID account."};case"hardware_key":return{name:"Hardware Security Key",description:"A YubiKey or similar device stored offline",icon:"shield",setupInstructions:"Register a hardware security key and store it safely."};case"institutional":return{name:"Recovery Service",description:"A professional recovery service provider",icon:"building",setupInstructions:"Connect with a trusted recovery service."};case"time_delayed_self":return{name:"Time-Delayed Self",description:"Recover yourself after a waiting period",icon:"clock",setupInstructions:"Set up a recovery option that requires waiting before completing."};default:return{name:"Unknown",description:"Unknown guardian type",icon:"question",setupInstructions:""}}}class I extends HTMLElement{shadow;constructor(){super();this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){let i=G();if(!i.getConfig())i.initializeRecovery(3).then(()=>this.render());else this.render()}render(){let t=G().getConfig(),o=t?.guardians??[],r=t?.threshold??3,n=o.reduce((e,d)=>e+d.weight,0),s=n>=r;this.shadow.innerHTML=` + +
+

Social Recovery

+

Set up guardians to recover your account without seed phrases

+
+
+
+
${s?"Recovery Configured":"Setup Incomplete"}
+
${n}/${r} guardians
+
+
+ ${o.map((e)=>{let d=N(e.type);return`
${d.icon==="key"?"\uD83D\uDD11":d.icon==="user"?"\uD83D\uDC64":d.icon==="shield"?"\uD83D\uDEE1️":d.icon==="building"?"\uD83C\uDFE2":"⏰"}
${e.name}
${d.name}
`}).join("")} +
+ `}}customElements.define("encryptid-guardian-setup",I);var w="encryptid_token",f="encryptid_user",oi="https://encryptid.jeffemmett.com",h=new z(oi);async function U(){let i=await h.authenticate(),t={did:i.did,username:i.username,token:i.token};return localStorage.setItem(w,i.token),localStorage.setItem(f,JSON.stringify(t)),t}async function B(i,t){let o=await h.register(i,t),r={did:o.did,username:i,token:o.token};return localStorage.setItem(w,o.token),localStorage.setItem(f,JSON.stringify(r)),r}function P(){localStorage.removeItem(w),localStorage.removeItem(f),localStorage.removeItem("encryptid_session")}function O(){return!!localStorage.getItem(w)}function W(){let i=localStorage.getItem(f);if(!i)return null;try{return JSON.parse(i)}catch{return null}}function H(){return localStorage.getItem(w)}function ri(i){if(O())return!0;let t=i||window.location.href;return window.location.href=`/?login=required&return=${encodeURIComponent(t)}`,!1}async function ni(i){let t=H();if(!t)throw Error("Not authenticated");await h.setRecoveryEmail(t,i)}async function si(i){await h.requestEmailRecovery(i)}function ei(i){let t=document.getElementById(i);if(!t){console.warn(`EncryptID: Container #${i} not found`);return}function o(){let r=W();if(r)t.innerHTML=` +
+ ${r.username||r.did.slice(0,16)+"..."} + +
`,document.getElementById("eid-signout")?.addEventListener("click",()=>{P(),o(),t.dispatchEvent(new CustomEvent("encryptid-logout",{bubbles:!0}))});else t.innerHTML=` + `,document.getElementById("eid-signin")?.addEventListener("click",async()=>{try{let n=await U();o(),t.dispatchEvent(new CustomEvent("encryptid-login",{bubbles:!0,detail:n}))}catch(n){if(n.name==="NotAllowedError"||n.message?.includes("No credential")){let s=prompt("No passkey found. Register with a username:");if(s)try{let e=await B(s);o(),t.dispatchEvent(new CustomEvent("encryptid-login",{bubbles:!0,detail:e}))}catch(e){alert(`Registration failed: ${e.message}`)}}else console.error("EncryptID auth error:",n)}})}o()}async function ai(){let i=H();if(!i)return!1;try{if((await h.verifySession(i)).valid)return!0;try{let o=await h.refreshToken(i);localStorage.setItem(w,o.token);let r=W();if(r)r.token=o.token,localStorage.setItem(f,JSON.stringify(r));return!0}catch{return P(),!1}}catch{return!1}}var _={client:h,authenticate:U,register:B,logout:P,isAuthenticated:O,getUser:W,getToken:H,requireAuth:ri,setRecoveryEmail:ni,requestRecovery:si,renderAuthButton:ei,verifySession:ai,detectCapabilities:m,AuthLevel:c,VERSION:"0.1.0"};if(typeof window<"u")window.EncryptID=_;var Xi=_;export{Xi as default}; diff --git a/frontend/vendor/@encryptid/sdk/index-24r9wkfe.js b/frontend/vendor/@encryptid/sdk/index-24r9wkfe.js new file mode 100644 index 0000000..203b289 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-24r9wkfe.js @@ -0,0 +1,529 @@ +import { + bufferToBase64url +} from "./index-2cp5044h.js"; +import { + AuthLevel +} from "./index-5c1t4ftn.js"; + +// src/client/key-derivation.ts +class EncryptIDKeyManager { + masterKey = null; + derivedKeys = null; + fromPRF = false; + async initFromPRF(prfOutput) { + this.masterKey = await crypto.subtle.importKey("raw", prfOutput, { name: "HKDF" }, false, ["deriveKey", "deriveBits"]); + this.fromPRF = true; + this.derivedKeys = null; + } + async initFromPassphrase(passphrase, salt) { + const encoder = new TextEncoder; + const passphraseKey = await crypto.subtle.importKey("raw", encoder.encode(passphrase), { name: "PBKDF2" }, false, ["deriveBits"]); + const masterKeyMaterial = await crypto.subtle.deriveBits({ name: "PBKDF2", salt, iterations: 600000, hash: "SHA-256" }, passphraseKey, 256); + this.masterKey = await crypto.subtle.importKey("raw", masterKeyMaterial, { name: "HKDF" }, false, ["deriveKey", "deriveBits"]); + this.fromPRF = false; + this.derivedKeys = null; + } + static generateSalt() { + return crypto.getRandomValues(new Uint8Array(32)); + } + isInitialized() { + return this.masterKey !== null; + } + async getKeys() { + if (!this.masterKey) + throw new Error("Key manager not initialized"); + if (this.derivedKeys) + return this.derivedKeys; + const [encryptionKey, signingKeyPair, didSeed] = await Promise.all([ + this.deriveEncryptionKey(), + this.deriveSigningKeyPair(), + this.deriveDIDSeed() + ]); + const did = await this.generateDID(didSeed); + this.derivedKeys = { encryptionKey, signingKeyPair, didSeed, did, fromPRF: this.fromPRF }; + return this.derivedKeys; + } + async deriveEncryptionKey() { + const encoder = new TextEncoder; + return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: encoder.encode("encryptid-encryption-key-v1"), info: encoder.encode("AES-256-GCM") }, this.masterKey, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt", "wrapKey", "unwrapKey"]); + } + async deriveSigningKeyPair() { + return crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign", "verify"]); + } + async deriveDIDSeed() { + const encoder = new TextEncoder; + const seed = await crypto.subtle.deriveBits({ name: "HKDF", hash: "SHA-256", salt: encoder.encode("encryptid-did-key-v1"), info: encoder.encode("Ed25519-seed") }, this.masterKey, 256); + return new Uint8Array(seed); + } + async generateDID(seed) { + const publicKeyHash = await crypto.subtle.digest("SHA-256", seed); + const publicKeyBytes = new Uint8Array(publicKeyHash).slice(0, 32); + const multicodecPrefix = new Uint8Array([237, 1]); + const multicodecKey = new Uint8Array(34); + multicodecKey.set(multicodecPrefix); + multicodecKey.set(publicKeyBytes, 2); + const base58Encoded = bufferToBase64url(multicodecKey.buffer).replace(/-/g, "").replace(/_/g, ""); + return `did:key:z${base58Encoded}`; + } + clear() { + this.masterKey = null; + this.derivedKeys = null; + this.fromPRF = false; + } +} +async function encryptData(key, data) { + let plaintext; + if (typeof data === "string") + plaintext = new TextEncoder().encode(data).buffer; + else if (data instanceof Uint8Array) + plaintext = data.buffer; + else + plaintext = data; + const iv = crypto.getRandomValues(new Uint8Array(12)); + const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext); + return { ciphertext, iv }; +} +async function decryptData(key, encrypted) { + return crypto.subtle.decrypt({ name: "AES-GCM", iv: encrypted.iv }, key, encrypted.ciphertext); +} +async function decryptDataAsString(key, encrypted) { + return new TextDecoder().decode(await decryptData(key, encrypted)); +} +async function signData(keyPair, data) { + let dataBuffer; + if (typeof data === "string") + dataBuffer = new TextEncoder().encode(data).buffer; + else if (data instanceof Uint8Array) + dataBuffer = data.buffer; + else + dataBuffer = data; + const signature = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, keyPair.privateKey, dataBuffer); + const publicKey = await crypto.subtle.exportKey("raw", keyPair.publicKey); + return { data: dataBuffer, signature, publicKey }; +} +async function verifySignature(signed) { + const publicKey = await crypto.subtle.importKey("raw", signed.publicKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]); + return crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, publicKey, signed.signature, signed.data); +} +async function wrapKeyForRecipient(keyToWrap, recipientPublicKey) { + return crypto.subtle.wrapKey("raw", keyToWrap, recipientPublicKey, { name: "RSA-OAEP" }); +} +async function unwrapSharedKey(wrappedKey, privateKey) { + return crypto.subtle.unwrapKey("raw", wrappedKey, privateKey, { name: "RSA-OAEP" }, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]); +} +var keyManagerInstance = null; +function getKeyManager() { + if (!keyManagerInstance) + keyManagerInstance = new EncryptIDKeyManager; + return keyManagerInstance; +} +function resetKeyManager() { + if (keyManagerInstance) { + keyManagerInstance.clear(); + keyManagerInstance = null; + } +} + +// src/client/session.ts +var OPERATION_PERMISSIONS = { + "rspace:view-public": { minAuthLevel: 1 /* BASIC */ }, + "rspace:view-private": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:edit-board": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:create-board": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:delete-board": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rspace:encrypt-board": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "encrypt" }, + "rwallet:view-balance": { minAuthLevel: 1 /* BASIC */ }, + "rwallet:view-history": { minAuthLevel: 2 /* STANDARD */ }, + "rwallet:send-small": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "wallet" }, + "rwallet:send-large": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "wallet", maxAgeSeconds: 60 }, + "rwallet:add-guardian": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rwallet:remove-guardian": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rvote:view-proposals": { minAuthLevel: 1 /* BASIC */ }, + "rvote:cast-vote": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "sign", maxAgeSeconds: 300 }, + "rvote:delegate": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "wallet" }, + "rfiles:list-files": { minAuthLevel: 2 /* STANDARD */ }, + "rfiles:download-own": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "encrypt" }, + "rfiles:upload": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "encrypt" }, + "rfiles:share": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "encrypt" }, + "rfiles:delete": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rfiles:export-keys": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rmaps:view-public": { minAuthLevel: 1 /* BASIC */ }, + "rmaps:add-location": { minAuthLevel: 2 /* STANDARD */ }, + "rmaps:edit-location": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "sign" }, + "account:view-profile": { minAuthLevel: 2 /* STANDARD */ }, + "account:edit-profile": { minAuthLevel: 3 /* ELEVATED */ }, + "account:export-data": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "account:delete": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rspace:create-space": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:configure-space": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rspace:delete-space": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rspace:invite-member": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:remove-member": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rspace:change-visibility": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rfunds:create-space": { minAuthLevel: 2 /* STANDARD */ }, + "rfunds:edit-flows": { minAuthLevel: 2 /* STANDARD */ }, + "rfunds:share-space": { minAuthLevel: 2 /* STANDARD */ } +}; +var SESSION_STORAGE_KEY = "encryptid_session"; +var TOKEN_REFRESH_THRESHOLD = 5 * 60 * 1000; + +class SessionManager { + session = null; + refreshTimer = null; + constructor() { + this.restoreSession(); + } + async createSession(authResult, did, capabilities) { + const now = Math.floor(Date.now() / 1000); + const claims = { + iss: "https://encryptid.jeffemmett.com", + sub: did, + aud: ["rspace.online", "rwallet.online", "rvote.online", "rfiles.online", "rmaps.online"], + iat: now, + exp: now + 15 * 60, + jti: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer), + eid: { + credentialId: authResult.credentialId, + authLevel: 3 /* ELEVATED */, + authTime: now, + capabilities, + recoveryConfigured: false + } + }; + const accessToken = this.createUnsignedToken(claims); + const refreshToken = this.createRefreshToken(did); + this.session = { accessToken, refreshToken, claims, lastAuthTime: Date.now() }; + this.persistSession(); + this.scheduleRefresh(); + return this.session; + } + getSession() { + return this.session; + } + getDID() { + return this.session?.claims.sub ?? null; + } + getAccessToken() { + return this.session?.accessToken ?? null; + } + getAuthLevel() { + if (!this.session) + return 1 /* BASIC */; + const now = Math.floor(Date.now() / 1000); + if (now >= this.session.claims.exp) + return 1 /* BASIC */; + const authAge = now - this.session.claims.eid.authTime; + if (authAge < 60) + return 3 /* ELEVATED */; + if (authAge < 15 * 60) + return 2 /* STANDARD */; + return 1 /* BASIC */; + } + canPerform(operation) { + const permission = OPERATION_PERMISSIONS[operation]; + if (!permission) + return { allowed: false, reason: "Unknown operation" }; + if (!this.session) + return { allowed: false, reason: "Not authenticated" }; + const currentLevel = this.getAuthLevel(); + if (currentLevel < permission.minAuthLevel) { + return { allowed: false, reason: `Requires ${AuthLevel[permission.minAuthLevel]} auth level (current: ${AuthLevel[currentLevel]})` }; + } + if (permission.requiresCapability) { + if (!this.session.claims.eid.capabilities[permission.requiresCapability]) { + return { allowed: false, reason: `Requires ${permission.requiresCapability} capability` }; + } + } + if (permission.maxAgeSeconds) { + const authAge = Math.floor(Date.now() / 1000) - this.session.claims.eid.authTime; + if (authAge > permission.maxAgeSeconds) { + return { allowed: false, reason: `Authentication too old (${authAge}s > ${permission.maxAgeSeconds}s)` }; + } + } + return { allowed: true }; + } + requiresFreshAuth(operation) { + const permission = OPERATION_PERMISSIONS[operation]; + if (!permission) + return true; + if (permission.minAuthLevel >= 4 /* CRITICAL */) + return true; + if (permission.maxAgeSeconds && permission.maxAgeSeconds <= 60) + return true; + return false; + } + upgradeAuthLevel(level = 3 /* ELEVATED */) { + if (!this.session) + return; + this.session.claims.eid.authLevel = level; + this.session.claims.eid.authTime = Math.floor(Date.now() / 1000); + this.session.lastAuthTime = Date.now(); + this.persistSession(); + } + clearSession() { + this.session = null; + if (this.refreshTimer) { + clearTimeout(this.refreshTimer); + this.refreshTimer = null; + } + try { + localStorage.removeItem(SESSION_STORAGE_KEY); + } catch {} + } + isValid() { + if (!this.session) + return false; + return Math.floor(Date.now() / 1000) < this.session.claims.exp; + } + createUnsignedToken(claims) { + const header = { alg: "none", typ: "JWT" }; + return `${btoa(JSON.stringify(header))}.${btoa(JSON.stringify(claims))}.`; + } + createRefreshToken(did) { + return btoa(JSON.stringify({ + sub: did, + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, + jti: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer) + })); + } + persistSession() { + if (!this.session) + return; + try { + localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(this.session)); + } catch {} + } + restoreSession() { + try { + const stored = localStorage.getItem(SESSION_STORAGE_KEY); + if (stored) { + const session = JSON.parse(stored); + if (Math.floor(Date.now() / 1000) < session.claims.exp) { + this.session = session; + this.scheduleRefresh(); + } else { + localStorage.removeItem(SESSION_STORAGE_KEY); + } + } + } catch {} + } + scheduleRefresh() { + if (!this.session) + return; + if (this.refreshTimer) + clearTimeout(this.refreshTimer); + const expiresAt = this.session.claims.exp * 1000; + const refreshAt = expiresAt - TOKEN_REFRESH_THRESHOLD; + const delay = Math.max(refreshAt - Date.now(), 0); + this.refreshTimer = setTimeout(() => this.refreshTokens(), delay); + } + async refreshTokens() { + if (!this.session) + return; + const now = Math.floor(Date.now() / 1000); + this.session.claims.eid.authLevel = Math.min(this.session.claims.eid.authLevel, 2 /* STANDARD */); + this.session.claims.iat = now; + this.session.claims.exp = now + 15 * 60; + this.session.claims.jti = bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer); + this.session.accessToken = this.createUnsignedToken(this.session.claims); + this.persistSession(); + this.scheduleRefresh(); + } +} +var sessionManagerInstance = null; +function getSessionManager() { + if (!sessionManagerInstance) + sessionManagerInstance = new SessionManager; + return sessionManagerInstance; +} + +// src/client/recovery.ts +class RecoveryManager { + config = null; + activeRequest = null; + constructor() { + this.loadConfig(); + } + async initializeRecovery(threshold = 3) { + this.config = { + threshold, + delaySeconds: 48 * 60 * 60, + guardians: [], + guardianListHash: "", + updatedAt: Date.now() + }; + await this.saveConfig(); + return this.config; + } + async addGuardian(guardian) { + if (!this.config) + throw new Error("Recovery not initialized"); + if (this.config.guardians.length >= 7) + throw new Error("Maximum of 7 guardians allowed"); + const newGuardian = { + ...guardian, + id: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer), + addedAt: Date.now() + }; + this.config.guardians.push(newGuardian); + this.config.guardianListHash = await this.hashGuardianList(); + this.config.updatedAt = Date.now(); + await this.saveConfig(); + return newGuardian; + } + async removeGuardian(guardianId) { + if (!this.config) + throw new Error("Recovery not initialized"); + const index = this.config.guardians.findIndex((g) => g.id === guardianId); + if (index === -1) + throw new Error("Guardian not found"); + const remainingWeight = this.config.guardians.filter((g) => g.id !== guardianId).reduce((sum, g) => sum + g.weight, 0); + if (remainingWeight < this.config.threshold) + throw new Error("Cannot remove guardian: would make recovery impossible"); + this.config.guardians.splice(index, 1); + this.config.guardianListHash = await this.hashGuardianList(); + this.config.updatedAt = Date.now(); + await this.saveConfig(); + } + async setThreshold(threshold) { + if (!this.config) + throw new Error("Recovery not initialized"); + const totalWeight = this.config.guardians.reduce((sum, g) => sum + g.weight, 0); + if (threshold > totalWeight) + throw new Error("Threshold cannot exceed total guardian weight"); + if (threshold < 1) + throw new Error("Threshold must be at least 1"); + this.config.threshold = threshold; + this.config.updatedAt = Date.now(); + await this.saveConfig(); + } + async setDelay(delaySeconds) { + if (!this.config) + throw new Error("Recovery not initialized"); + if (delaySeconds < 3600 || delaySeconds > 7 * 24 * 3600) + throw new Error("Delay must be between 1 hour and 7 days"); + this.config.delaySeconds = delaySeconds; + this.config.updatedAt = Date.now(); + await this.saveConfig(); + } + getConfig() { + return this.config; + } + isConfigured() { + if (!this.config) + return false; + return this.config.guardians.reduce((sum, g) => sum + g.weight, 0) >= this.config.threshold; + } + async verifyGuardian(guardianId) { + if (!this.config) + throw new Error("Recovery not initialized"); + const guardian = this.config.guardians.find((g) => g.id === guardianId); + if (!guardian) + throw new Error("Guardian not found"); + guardian.lastVerified = Date.now(); + await this.saveConfig(); + return true; + } + async initiateRecovery(newCredentialId) { + if (!this.config) + throw new Error("Recovery not configured"); + if (this.activeRequest?.status === "pending") + throw new Error("Recovery already in progress"); + const now = Date.now(); + this.activeRequest = { + id: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer), + accountDID: "", + newCredentialId, + initiatedAt: now, + completesAt: now + this.config.delaySeconds * 1000, + status: "pending", + approvals: [], + approvalWeight: 0 + }; + return this.activeRequest; + } + async approveRecovery(guardianId, signature) { + if (!this.activeRequest || this.activeRequest.status !== "pending") + throw new Error("No pending recovery request"); + if (!this.config) + throw new Error("Recovery not configured"); + const guardian = this.config.guardians.find((g) => g.id === guardianId); + if (!guardian) + throw new Error("Guardian not found"); + if (this.activeRequest.approvals.some((a) => a.guardianId === guardianId)) + throw new Error("Guardian already approved"); + this.activeRequest.approvals.push({ guardianId, approvedAt: Date.now(), signature }); + this.activeRequest.approvalWeight += guardian.weight; + if (this.activeRequest.approvalWeight >= this.config.threshold) { + this.activeRequest.status = "approved"; + } + return this.activeRequest; + } + async cancelRecovery() { + if (!this.activeRequest || this.activeRequest.status !== "pending") + throw new Error("No pending recovery request to cancel"); + this.activeRequest.status = "cancelled"; + this.activeRequest = null; + } + async completeRecovery() { + if (!this.activeRequest) + throw new Error("No recovery request"); + if (this.activeRequest.status !== "approved") + throw new Error("Recovery not approved"); + if (Date.now() < this.activeRequest.completesAt) { + const remaining = this.activeRequest.completesAt - Date.now(); + throw new Error(`Time-lock not expired. ${Math.ceil(remaining / 1000 / 60)} minutes remaining.`); + } + this.activeRequest.status = "completed"; + this.activeRequest = null; + } + getActiveRequest() { + return this.activeRequest; + } + async hashGuardianList() { + if (!this.config) + return ""; + const sortedIds = this.config.guardians.map((g) => g.id).sort().join(","); + const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(sortedIds)); + return bufferToBase64url(hash); + } + async saveConfig() { + if (!this.config) + return; + try { + localStorage.setItem("encryptid_recovery", JSON.stringify(this.config)); + } catch {} + } + loadConfig() { + try { + const stored = localStorage.getItem("encryptid_recovery"); + if (stored) + this.config = JSON.parse(stored); + } catch {} + } +} +var recoveryManagerInstance = null; +function getRecoveryManager() { + if (!recoveryManagerInstance) + recoveryManagerInstance = new RecoveryManager; + return recoveryManagerInstance; +} +function getGuardianTypeInfo(type) { + switch (type) { + case "secondary_passkey" /* SECONDARY_PASSKEY */: + return { name: "Backup Passkey", description: "Another device you own (phone, YubiKey, etc.)", icon: "key", setupInstructions: "Register a passkey on a second device you control." }; + case "trusted_contact" /* TRUSTED_CONTACT */: + return { name: "Trusted Contact", description: "A friend or family member with their own EncryptID", icon: "user", setupInstructions: "Ask a trusted person to create an EncryptID account." }; + case "hardware_key" /* HARDWARE_KEY */: + return { name: "Hardware Security Key", description: "A YubiKey or similar device stored offline", icon: "shield", setupInstructions: "Register a hardware security key and store it safely." }; + case "institutional" /* INSTITUTIONAL */: + return { name: "Recovery Service", description: "A professional recovery service provider", icon: "building", setupInstructions: "Connect with a trusted recovery service." }; + case "time_delayed_self" /* TIME_DELAYED_SELF */: + return { name: "Time-Delayed Self", description: "Recover yourself after a waiting period", icon: "clock", setupInstructions: "Set up a recovery option that requires waiting before completing." }; + default: + return { name: "Unknown", description: "Unknown guardian type", icon: "question", setupInstructions: "" }; + } +} + +export { EncryptIDKeyManager, encryptData, decryptData, decryptDataAsString, signData, verifySignature, wrapKeyForRecipient, unwrapSharedKey, getKeyManager, resetKeyManager, OPERATION_PERMISSIONS, SessionManager, getSessionManager, RecoveryManager, getRecoveryManager, getGuardianTypeInfo }; diff --git a/frontend/vendor/@encryptid/sdk/index-2cp5044h.js b/frontend/vendor/@encryptid/sdk/index-2cp5044h.js new file mode 100644 index 0000000..a119d8c --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-2cp5044h.js @@ -0,0 +1,194 @@ +// src/client/webauthn.ts +var DEFAULT_CONFIG = { + rpId: "jeffemmett.com", + rpName: "EncryptID", + origin: typeof window !== "undefined" ? window.location.origin : "", + userVerification: "required", + timeout: 60000 +}; +var conditionalUIAbortController = null; +function abortConditionalUI() { + if (conditionalUIAbortController) { + conditionalUIAbortController.abort(); + conditionalUIAbortController = null; + } +} +function bufferToBase64url(buffer) { + const bytes = new Uint8Array(buffer); + let binary = ""; + for (let i = 0;i < bytes.byteLength; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +} +function base64urlToBuffer(base64url) { + const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/"); + const padding = "=".repeat((4 - base64.length % 4) % 4); + const binary = atob(base64 + padding); + const bytes = new Uint8Array(binary.length); + for (let i = 0;i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes.buffer; +} +function generateChallenge() { + return crypto.getRandomValues(new Uint8Array(32)).buffer; +} +async function generatePRFSalt(purpose) { + const encoder = new TextEncoder; + const data = encoder.encode(`encryptid-prf-salt-${purpose}-v1`); + return crypto.subtle.digest("SHA-256", data); +} +async function registerPasskey(username, displayName, config = {}) { + abortConditionalUI(); + const cfg = { ...DEFAULT_CONFIG, ...config }; + if (!window.PublicKeyCredential) { + throw new Error("WebAuthn is not supported in this browser"); + } + const platformAvailable = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); + const userId = crypto.getRandomValues(new Uint8Array(32)); + const challenge = generateChallenge(); + const prfSalt = await generatePRFSalt("master-key"); + const createOptions = { + publicKey: { + challenge: new Uint8Array(challenge), + rp: { id: cfg.rpId, name: cfg.rpName }, + user: { id: userId, name: username, displayName }, + pubKeyCredParams: [ + { alg: -7, type: "public-key" }, + { alg: -257, type: "public-key" } + ], + authenticatorSelection: { + residentKey: "required", + requireResidentKey: true, + userVerification: cfg.userVerification, + authenticatorAttachment: platformAvailable ? "platform" : undefined + }, + attestation: "none", + timeout: cfg.timeout, + extensions: { + prf: { eval: { first: new Uint8Array(prfSalt) } }, + credProps: true + } + } + }; + const credential = await navigator.credentials.create(createOptions); + if (!credential) + throw new Error("Failed to create credential"); + const response = credential.response; + const prfSupported = credential.getClientExtensionResults()?.prf?.enabled === true; + const publicKey = response.getPublicKey(); + if (!publicKey) + throw new Error("Failed to get public key from credential"); + return { + credentialId: bufferToBase64url(credential.rawId), + publicKey, + userId: bufferToBase64url(userId.buffer), + username, + createdAt: Date.now(), + prfSupported, + transports: response.getTransports?.() + }; +} +async function authenticatePasskey(credentialId, config = {}) { + abortConditionalUI(); + const cfg = { ...DEFAULT_CONFIG, ...config }; + if (!window.PublicKeyCredential) { + throw new Error("WebAuthn is not supported in this browser"); + } + const challenge = generateChallenge(); + const prfSalt = await generatePRFSalt("master-key"); + const allowCredentials = credentialId ? [{ type: "public-key", id: new Uint8Array(base64urlToBuffer(credentialId)) }] : undefined; + const getOptions = { + publicKey: { + challenge: new Uint8Array(challenge), + rpId: cfg.rpId, + allowCredentials, + userVerification: cfg.userVerification, + timeout: cfg.timeout, + extensions: { + prf: { eval: { first: new Uint8Array(prfSalt) } } + } + } + }; + const credential = await navigator.credentials.get(getOptions); + if (!credential) + throw new Error("Authentication failed"); + const response = credential.response; + const prfResults = credential.getClientExtensionResults()?.prf?.results; + return { + credentialId: bufferToBase64url(credential.rawId), + userId: response.userHandle ? bufferToBase64url(response.userHandle) : "", + prfOutput: prfResults?.first, + signature: response.signature, + authenticatorData: response.authenticatorData + }; +} +async function isConditionalMediationAvailable() { + if (!window.PublicKeyCredential) + return false; + if (typeof PublicKeyCredential.isConditionalMediationAvailable === "function") { + return PublicKeyCredential.isConditionalMediationAvailable(); + } + return false; +} +async function startConditionalUI(config = {}) { + const available = await isConditionalMediationAvailable(); + if (!available) + return null; + abortConditionalUI(); + conditionalUIAbortController = new AbortController; + const cfg = { ...DEFAULT_CONFIG, ...config }; + const challenge = generateChallenge(); + const prfSalt = await generatePRFSalt("master-key"); + try { + const credential = await navigator.credentials.get({ + publicKey: { + challenge: new Uint8Array(challenge), + rpId: cfg.rpId, + userVerification: cfg.userVerification, + timeout: cfg.timeout, + extensions: { + prf: { eval: { first: new Uint8Array(prfSalt) } } + } + }, + mediation: "conditional", + signal: conditionalUIAbortController.signal + }); + conditionalUIAbortController = null; + if (!credential) + return null; + const response = credential.response; + const prfResults = credential.getClientExtensionResults()?.prf?.results; + return { + credentialId: bufferToBase64url(credential.rawId), + userId: response.userHandle ? bufferToBase64url(response.userHandle) : "", + prfOutput: prfResults?.first, + signature: response.signature, + authenticatorData: response.authenticatorData + }; + } catch { + return null; + } +} +async function detectCapabilities() { + const capabilities = { + webauthn: false, + platformAuthenticator: false, + conditionalUI: false, + prfExtension: false + }; + if (!window.PublicKeyCredential) + return capabilities; + capabilities.webauthn = true; + try { + capabilities.platformAuthenticator = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); + } catch { + capabilities.platformAuthenticator = false; + } + capabilities.conditionalUI = await isConditionalMediationAvailable(); + capabilities.prfExtension = true; + return capabilities; +} + +export { abortConditionalUI, bufferToBase64url, base64urlToBuffer, generateChallenge, registerPasskey, authenticatePasskey, isConditionalMediationAvailable, startConditionalUI, detectCapabilities }; diff --git a/frontend/vendor/@encryptid/sdk/index-2yszamrn.js b/frontend/vendor/@encryptid/sdk/index-2yszamrn.js new file mode 100644 index 0000000..4e11d3f --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-2yszamrn.js @@ -0,0 +1,38 @@ +import { + verifyEncryptIDToken +} from "./index-stg63j73.js"; + +// src/server/ws-auth.ts +async function authenticateWSUpgrade(request, options = {}) { + const url = new URL(request.url); + const queryToken = url.searchParams.get("token"); + if (queryToken) { + try { + return await verifyEncryptIDToken(queryToken, options); + } catch { + return null; + } + } + const protocols = request.headers.get("Sec-WebSocket-Protocol") || ""; + const tokenProtocol = protocols.split(",").map((p) => p.trim()).find((p) => p.startsWith("encryptid.")); + if (tokenProtocol) { + const token = tokenProtocol.slice("encryptid.".length); + try { + return await verifyEncryptIDToken(token, options); + } catch { + return null; + } + } + const cookie = request.headers.get("Cookie") || ""; + const match = cookie.match(/encryptid_token=([^;]+)/); + if (match) { + try { + return await verifyEncryptIDToken(match[1], options); + } catch { + return null; + } + } + return null; +} + +export { authenticateWSUpgrade }; diff --git a/frontend/vendor/@encryptid/sdk/index-5c1t4ftn.js b/frontend/vendor/@encryptid/sdk/index-5c1t4ftn.js new file mode 100644 index 0000000..6a64368 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-5c1t4ftn.js @@ -0,0 +1,25 @@ +// src/types/index.ts +var AuthLevel; +((AuthLevel2) => { + AuthLevel2[AuthLevel2["BASIC"] = 1] = "BASIC"; + AuthLevel2[AuthLevel2["STANDARD"] = 2] = "STANDARD"; + AuthLevel2[AuthLevel2["ELEVATED"] = 3] = "ELEVATED"; + AuthLevel2[AuthLevel2["CRITICAL"] = 4] = "CRITICAL"; +})(AuthLevel ||= {}); +var GuardianType; +((GuardianType2) => { + GuardianType2["SECONDARY_PASSKEY"] = "secondary_passkey"; + GuardianType2["TRUSTED_CONTACT"] = "trusted_contact"; + GuardianType2["HARDWARE_KEY"] = "hardware_key"; + GuardianType2["INSTITUTIONAL"] = "institutional"; + GuardianType2["TIME_DELAYED_SELF"] = "time_delayed_self"; +})(GuardianType ||= {}); +var SpaceVisibility; +((SpaceVisibility2) => { + SpaceVisibility2["PUBLIC"] = "public"; + SpaceVisibility2["PUBLIC_READ"] = "public_read"; + SpaceVisibility2["AUTHENTICATED"] = "authenticated"; + SpaceVisibility2["MEMBERS_ONLY"] = "members_only"; +})(SpaceVisibility ||= {}); + +export { AuthLevel, GuardianType, SpaceVisibility }; diff --git a/frontend/vendor/@encryptid/sdk/index-7egxprg9.js b/frontend/vendor/@encryptid/sdk/index-7egxprg9.js new file mode 100644 index 0000000..4230e47 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-7egxprg9.js @@ -0,0 +1,170 @@ +import { + bufferToBase64url +} from "./index-2cp5044h.js"; + +// src/client/api-client.ts +var DEFAULT_SERVER_URL = "https://encryptid.jeffemmett.com"; + +class EncryptIDClient { + serverUrl; + constructor(serverUrl = DEFAULT_SERVER_URL) { + this.serverUrl = serverUrl.replace(/\/$/, ""); + } + async registerStart(username, displayName) { + const res = await fetch(`${this.serverUrl}/api/register/start`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, displayName: displayName || username }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Registration start failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async registerComplete(challenge, credential, userId, username) { + const response = credential.response; + const publicKey = response.getPublicKey(); + const res = await fetch(`${this.serverUrl}/api/register/complete`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + challenge, + userId, + username, + credential: { + credentialId: bufferToBase64url(credential.rawId), + publicKey: publicKey ? bufferToBase64url(publicKey) : "", + transports: response.getTransports?.() || [] + } + }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Registration complete failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async authStart(credentialId) { + const res = await fetch(`${this.serverUrl}/api/auth/start`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(credentialId ? { credentialId } : {}) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Auth start failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async authComplete(challenge, credential) { + const response = credential.response; + const prfResults = credential.getClientExtensionResults()?.prf?.results; + const res = await fetch(`${this.serverUrl}/api/auth/complete`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + challenge, + credential: { + credentialId: bufferToBase64url(credential.rawId), + signature: bufferToBase64url(response.signature), + authenticatorData: bufferToBase64url(response.authenticatorData), + prfOutput: prfResults?.first ? bufferToBase64url(prfResults.first) : null + } + }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Auth complete failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async verifySession(token) { + const res = await fetch(`${this.serverUrl}/api/session/verify`, { + headers: { Authorization: `Bearer ${token}` } + }); + return res.json(); + } + async refreshToken(token) { + const res = await fetch(`${this.serverUrl}/api/session/refresh`, { + method: "POST", + headers: { Authorization: `Bearer ${token}` } + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Token refresh failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async listCredentials(token) { + const res = await fetch(`${this.serverUrl}/api/user/credentials`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (!res.ok) { + throw new Error("Failed to list credentials"); + } + return res.json(); + } + async register(username, displayName, config) { + const { options, userId } = await this.registerStart(username, displayName); + const createOptions = { + publicKey: { + ...options, + challenge: base64urlToUint8Array(options.challenge), + user: { + ...options.user, + id: base64urlToUint8Array(options.user.id) + }, + pubKeyCredParams: options.pubKeyCredParams, + extensions: { + credProps: true, + prf: { eval: { first: new Uint8Array(32) } } + } + } + }; + const credential = await navigator.credentials.create(createOptions); + if (!credential) + throw new Error("Failed to create credential"); + return this.registerComplete(options.challenge, credential, userId, username); + } + async authenticate(credentialId, config) { + const { options } = await this.authStart(credentialId); + const getOptions = { + publicKey: { + challenge: base64urlToUint8Array(options.challenge), + rpId: options.rpId, + userVerification: options.userVerification, + timeout: options.timeout, + allowCredentials: options.allowCredentials?.map((c) => ({ + type: c.type, + id: base64urlToUint8Array(c.id), + transports: c.transports + })), + extensions: { + prf: { eval: { first: new Uint8Array(32) } } + } + } + }; + const credential = await navigator.credentials.get(getOptions); + if (!credential) + throw new Error("Authentication failed"); + const result = await this.authComplete(options.challenge, credential); + const prfResults = credential.getClientExtensionResults()?.prf?.results; + return { + ...result, + prfOutput: prfResults?.first + }; + } +} +function base64urlToUint8Array(base64url) { + const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/"); + const padding = "=".repeat((4 - base64.length % 4) % 4); + const binary = atob(base64 + padding); + const bytes = new Uint8Array(binary.length); + for (let i = 0;i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +} + +export { EncryptIDClient }; diff --git a/frontend/vendor/@encryptid/sdk/index-j6kh1974.js b/frontend/vendor/@encryptid/sdk/index-j6kh1974.js new file mode 100644 index 0000000..8e5266c --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-j6kh1974.js @@ -0,0 +1,72 @@ +import { + verifyEncryptIDToken +} from "./index-stg63j73.js"; + +// src/server/space-auth.ts +async function evaluateSpaceAccess(spaceSlug, token, method, options) { + const config = await options.getSpaceConfig(spaceSlug); + if (!config) { + return { allowed: false, claims: null, reason: "Space not found", isOwner: false, readOnly: false }; + } + let claims = null; + if (token) { + try { + claims = await verifyEncryptIDToken(token, options); + } catch {} + } + const isRead = method === "GET" || method === "HEAD" || method === "OPTIONS"; + const isOwner = !!(claims && config.ownerDID && claims.sub === config.ownerDID); + switch (config.visibility) { + case "public" /* PUBLIC */: + return { allowed: true, claims, isOwner, readOnly: false }; + case "public_read" /* PUBLIC_READ */: + if (isRead) { + return { allowed: true, claims, isOwner, readOnly: !claims }; + } + if (!claims) { + return { + allowed: false, + claims: null, + reason: "Authentication required to modify this space", + isOwner: false, + readOnly: true + }; + } + return { allowed: true, claims, isOwner, readOnly: false }; + case "authenticated" /* AUTHENTICATED */: + if (!claims) { + return { allowed: false, claims: null, reason: "Authentication required", isOwner: false, readOnly: false }; + } + return { allowed: true, claims, isOwner, readOnly: false }; + case "members_only" /* MEMBERS_ONLY */: + if (!claims) { + return { allowed: false, claims: null, reason: "Authentication required", isOwner: false, readOnly: false }; + } + return { allowed: true, claims, isOwner, readOnly: false }; + default: + return { allowed: false, claims: null, reason: "Unknown visibility setting", isOwner: false, readOnly: false }; + } +} +function extractToken(headers) { + if (typeof headers.get === "function") { + const auth = headers.get("Authorization") || headers.get("authorization"); + if (auth?.startsWith("Bearer ")) + return auth.slice(7); + const cookie = headers.get("Cookie") || headers.get("cookie") || ""; + const match = cookie.match(/encryptid_token=([^;]+)/); + if (match) + return match[1]; + } + if (typeof headers.authorization === "string") { + if (headers.authorization.startsWith("Bearer ")) + return headers.authorization.slice(7); + } + if (typeof headers.cookie === "string") { + const match = headers.cookie.match(/encryptid_token=([^;]+)/); + if (match) + return match[1]; + } + return null; +} + +export { evaluateSpaceAccess, extractToken }; diff --git a/frontend/vendor/@encryptid/sdk/index-stg63j73.js b/frontend/vendor/@encryptid/sdk/index-stg63j73.js new file mode 100644 index 0000000..fc9aa73 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index-stg63j73.js @@ -0,0 +1,118 @@ +// src/server/jwt-verify.ts +var ENCRYPTID_SERVER = "https://encryptid.jeffemmett.com"; +async function verifyEncryptIDToken(token, options = {}) { + const { secret, serverUrl = ENCRYPTID_SERVER, audience, clockTolerance = 30 } = options; + if (secret) { + return verifyLocally(token, secret, audience, clockTolerance); + } + return verifyRemotely(token, serverUrl); +} +async function verifyLocally(token, secret, audience, clockTolerance = 30) { + const parts = token.split("."); + if (parts.length !== 3) { + throw new Error("Invalid JWT format"); + } + const [headerB64, payloadB64, signatureB64] = parts; + const encoder = new TextEncoder; + const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]); + const data = encoder.encode(`${headerB64}.${payloadB64}`); + const signature = base64urlDecode(signatureB64); + const valid = await crypto.subtle.verify("HMAC", key, signature, data); + if (!valid) { + throw new Error("Invalid JWT signature"); + } + const payload = JSON.parse(new TextDecoder().decode(base64urlDecode(payloadB64))); + const now = Math.floor(Date.now() / 1000); + if (payload.exp && now > payload.exp + clockTolerance) { + throw new Error("Token expired"); + } + if (audience && payload.aud) { + const auds = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; + if (!auds.some((a) => a.includes(audience))) { + throw new Error(`Token audience mismatch: expected ${audience}`); + } + } + return payload; +} +async function verifyRemotely(token, serverUrl) { + const res = await fetch(`${serverUrl}/api/session/verify`, { + headers: { Authorization: `Bearer ${token}` } + }); + const data = await res.json(); + if (!data.valid) { + throw new Error(data.error || "Invalid token"); + } + const parts = token.split("."); + if (parts.length >= 2) { + try { + const payload = JSON.parse(new TextDecoder().decode(base64urlDecode(parts[1]))); + return payload; + } catch {} + } + return { + iss: serverUrl, + sub: data.userId, + aud: [], + iat: 0, + exp: data.exp || 0, + jti: "", + username: data.username, + did: data.did, + eid: { + authLevel: 2, + authTime: 0, + capabilities: { encrypt: true, sign: true, wallet: false }, + recoveryConfigured: false + } + }; +} +function getAuthLevel(claims) { + if (!claims.eid) + return 1; + const authAge = Math.floor(Date.now() / 1000) - claims.eid.authTime; + if (authAge < 60) + return 3; + if (authAge < 15 * 60) + return 2; + return 1; +} +function checkPermission(claims, permission) { + const currentLevel = getAuthLevel(claims); + if (currentLevel < permission.minAuthLevel) { + return { + allowed: false, + reason: `Requires auth level ${permission.minAuthLevel} (current: ${currentLevel})` + }; + } + if (permission.requiresCapability) { + const has = claims.eid?.capabilities?.[permission.requiresCapability]; + if (!has) { + return { + allowed: false, + reason: `Requires ${permission.requiresCapability} capability` + }; + } + } + if (permission.maxAgeSeconds) { + const authAge = Math.floor(Date.now() / 1000) - (claims.eid?.authTime || 0); + if (authAge > permission.maxAgeSeconds) { + return { + allowed: false, + reason: `Authentication too old (${authAge}s > ${permission.maxAgeSeconds}s)` + }; + } + } + return { allowed: true }; +} +function base64urlDecode(str) { + const base64 = str.replace(/-/g, "+").replace(/_/g, "/"); + const padding = "=".repeat((4 - base64.length % 4) % 4); + const binary = atob(base64 + padding); + const bytes = new Uint8Array(binary.length); + for (let i = 0;i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes.buffer; +} + +export { verifyEncryptIDToken, getAuthLevel, checkPermission }; diff --git a/frontend/vendor/@encryptid/sdk/index.d.ts b/frontend/vendor/@encryptid/sdk/index.d.ts new file mode 100644 index 0000000..81b19bc --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index.d.ts @@ -0,0 +1,15 @@ +/** + * @encryptid/sdk — Self-Sovereign Identity SDK + * + * WebAuthn passkey authentication with derived keys, social recovery, + * and cross-app SSO for the r-ecosystem. + */ +export type { EncryptIDCredential, AuthenticationResult, EncryptIDConfig, WebAuthnCapabilities, DerivedKeys, EncryptedData, SignedData, EncryptIDClaims, SessionState, OperationPermission, Guardian, RecoveryConfig, RecoveryRequest, RegistrationStartResponse, RegistrationCompleteResponse, AuthStartResponse, AuthCompleteResponse, SessionVerifyResponse, EmailRecoverySetResponse, EmailRecoveryRequestResponse, EmailRecoveryVerifyResponse, } from './types/index.js'; +export { AuthLevel, GuardianType } from './types/index.js'; +export { EncryptIDClient } from './client/api-client.js'; +export { registerPasskey, authenticatePasskey, startConditionalUI, detectCapabilities, bufferToBase64url, base64urlToBuffer, } from './client/webauthn.js'; +export { EncryptIDKeyManager, getKeyManager, signEthHash } from './client/key-derivation.js'; +export { SessionManager, getSessionManager, OPERATION_PERMISSIONS } from './client/session.js'; +export { RecoveryManager, getRecoveryManager, getGuardianTypeInfo } from './client/recovery.js'; +export declare const VERSION = "0.1.0"; +export declare const SPEC_VERSION = "2026-02"; diff --git a/frontend/vendor/@encryptid/sdk/index.js b/frontend/vendor/@encryptid/sdk/index.js new file mode 100644 index 0000000..f1d678a --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/index.js @@ -0,0 +1,3618 @@ +// src/types/roles.ts +var SPACE_ROLE_LEVEL = { + ["viewer" /* VIEWER */]: 0, + ["participant" /* PARTICIPANT */]: 1, + ["moderator" /* MODERATOR */]: 2, + ["admin" /* ADMIN */]: 3 +}; + +// src/types/index.ts +var AuthLevel; +((AuthLevel2) => { + AuthLevel2[AuthLevel2["BASIC"] = 1] = "BASIC"; + AuthLevel2[AuthLevel2["STANDARD"] = 2] = "STANDARD"; + AuthLevel2[AuthLevel2["ELEVATED"] = 3] = "ELEVATED"; + AuthLevel2[AuthLevel2["CRITICAL"] = 4] = "CRITICAL"; +})(AuthLevel ||= {}); +var GuardianType; +((GuardianType2) => { + GuardianType2["SECONDARY_PASSKEY"] = "secondary_passkey"; + GuardianType2["TRUSTED_CONTACT"] = "trusted_contact"; + GuardianType2["HARDWARE_KEY"] = "hardware_key"; + GuardianType2["INSTITUTIONAL"] = "institutional"; + GuardianType2["TIME_DELAYED_SELF"] = "time_delayed_self"; +})(GuardianType ||= {}); +// src/client/webauthn.ts +var DEFAULT_CONFIG = { + rpId: "jeffemmett.com", + rpName: "EncryptID", + origin: typeof window !== "undefined" ? window.location.origin : "", + userVerification: "required", + timeout: 60000 +}; +var conditionalUIAbortController = null; +function abortConditionalUI() { + if (conditionalUIAbortController) { + conditionalUIAbortController.abort(); + conditionalUIAbortController = null; + } +} +function bufferToBase64url(buffer) { + const bytes = new Uint8Array(buffer); + let binary = ""; + for (let i = 0;i < bytes.byteLength; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +} +function base64urlToBuffer(base64url) { + const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/"); + const padding = "=".repeat((4 - base64.length % 4) % 4); + const binary = atob(base64 + padding); + const bytes = new Uint8Array(binary.length); + for (let i = 0;i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes.buffer; +} +function generateChallenge() { + return crypto.getRandomValues(new Uint8Array(32)).buffer; +} +async function generatePRFSalt(purpose) { + const encoder = new TextEncoder; + const data = encoder.encode(`encryptid-prf-salt-${purpose}-v1`); + return crypto.subtle.digest("SHA-256", data); +} +async function registerPasskey(username, displayName, config = {}) { + abortConditionalUI(); + const cfg = { ...DEFAULT_CONFIG, ...config }; + if (!window.PublicKeyCredential) { + throw new Error("WebAuthn is not supported in this browser"); + } + const platformAvailable = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); + const userId = crypto.getRandomValues(new Uint8Array(32)); + const challenge = generateChallenge(); + const prfSalt = await generatePRFSalt("master-key"); + const createOptions = { + publicKey: { + challenge: new Uint8Array(challenge), + rp: { id: cfg.rpId, name: cfg.rpName }, + user: { id: userId, name: username, displayName }, + pubKeyCredParams: [ + { alg: -7, type: "public-key" }, + { alg: -257, type: "public-key" } + ], + authenticatorSelection: { + residentKey: "required", + requireResidentKey: true, + userVerification: cfg.userVerification, + authenticatorAttachment: platformAvailable ? "platform" : undefined + }, + attestation: "none", + timeout: cfg.timeout, + extensions: { + prf: { eval: { first: new Uint8Array(prfSalt) } }, + credProps: true + } + } + }; + const credential = await navigator.credentials.create(createOptions); + if (!credential) + throw new Error("Failed to create credential"); + const response = credential.response; + const prfSupported = credential.getClientExtensionResults()?.prf?.enabled === true; + const publicKey = response.getPublicKey(); + if (!publicKey) + throw new Error("Failed to get public key from credential"); + return { + credentialId: bufferToBase64url(credential.rawId), + publicKey, + userId: bufferToBase64url(userId.buffer), + username, + createdAt: Date.now(), + prfSupported, + transports: response.getTransports?.() + }; +} +async function authenticatePasskey(credentialId, config = {}) { + abortConditionalUI(); + const cfg = { ...DEFAULT_CONFIG, ...config }; + if (!window.PublicKeyCredential) { + throw new Error("WebAuthn is not supported in this browser"); + } + const challenge = generateChallenge(); + const prfSalt = await generatePRFSalt("master-key"); + const allowCredentials = credentialId ? [{ type: "public-key", id: new Uint8Array(base64urlToBuffer(credentialId)) }] : undefined; + const getOptions = { + publicKey: { + challenge: new Uint8Array(challenge), + rpId: cfg.rpId, + allowCredentials, + userVerification: cfg.userVerification, + timeout: cfg.timeout, + extensions: { + prf: { eval: { first: new Uint8Array(prfSalt) } } + } + } + }; + const credential = await navigator.credentials.get(getOptions); + if (!credential) + throw new Error("Authentication failed"); + const response = credential.response; + const prfResults = credential.getClientExtensionResults()?.prf?.results; + return { + credentialId: bufferToBase64url(credential.rawId), + userId: response.userHandle ? bufferToBase64url(response.userHandle) : "", + prfOutput: prfResults?.first, + signature: response.signature, + authenticatorData: response.authenticatorData + }; +} +async function isConditionalMediationAvailable() { + if (!window.PublicKeyCredential) + return false; + if (typeof PublicKeyCredential.isConditionalMediationAvailable === "function") { + return PublicKeyCredential.isConditionalMediationAvailable(); + } + return false; +} +async function startConditionalUI(config = {}) { + const available = await isConditionalMediationAvailable(); + if (!available) + return null; + abortConditionalUI(); + conditionalUIAbortController = new AbortController; + const cfg = { ...DEFAULT_CONFIG, ...config }; + const challenge = generateChallenge(); + const prfSalt = await generatePRFSalt("master-key"); + try { + const credential = await navigator.credentials.get({ + publicKey: { + challenge: new Uint8Array(challenge), + rpId: cfg.rpId, + userVerification: cfg.userVerification, + timeout: cfg.timeout, + extensions: { + prf: { eval: { first: new Uint8Array(prfSalt) } } + } + }, + mediation: "conditional", + signal: conditionalUIAbortController.signal + }); + conditionalUIAbortController = null; + if (!credential) + return null; + const response = credential.response; + const prfResults = credential.getClientExtensionResults()?.prf?.results; + return { + credentialId: bufferToBase64url(credential.rawId), + userId: response.userHandle ? bufferToBase64url(response.userHandle) : "", + prfOutput: prfResults?.first, + signature: response.signature, + authenticatorData: response.authenticatorData + }; + } catch { + return null; + } +} +async function detectCapabilities() { + const capabilities = { + webauthn: false, + platformAuthenticator: false, + conditionalUI: false, + prfExtension: false + }; + if (!window.PublicKeyCredential) + return capabilities; + capabilities.webauthn = true; + try { + capabilities.platformAuthenticator = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); + } catch { + capabilities.platformAuthenticator = false; + } + capabilities.conditionalUI = await isConditionalMediationAvailable(); + capabilities.prfExtension = true; + return capabilities; +} + +// src/client/api-client.ts +var DEFAULT_SERVER_URL = "https://encryptid.jeffemmett.com"; + +class EncryptIDClient { + serverUrl; + constructor(serverUrl = DEFAULT_SERVER_URL) { + this.serverUrl = serverUrl.replace(/\/$/, ""); + } + async registerStart(username, displayName) { + const res = await fetch(`${this.serverUrl}/api/register/start`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, displayName: displayName || username }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Registration start failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async registerComplete(challenge, credential, userId, username) { + const response = credential.response; + const publicKey = response.getPublicKey(); + const res = await fetch(`${this.serverUrl}/api/register/complete`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + challenge, + userId, + username, + credential: { + credentialId: bufferToBase64url(credential.rawId), + publicKey: publicKey ? bufferToBase64url(publicKey) : "", + transports: response.getTransports?.() || [] + } + }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Registration complete failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async authStart(credentialId) { + const res = await fetch(`${this.serverUrl}/api/auth/start`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(credentialId ? { credentialId } : {}) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Auth start failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async authComplete(challenge, credential) { + const response = credential.response; + const prfResults = credential.getClientExtensionResults()?.prf?.results; + const res = await fetch(`${this.serverUrl}/api/auth/complete`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + challenge, + credential: { + credentialId: bufferToBase64url(credential.rawId), + signature: bufferToBase64url(response.signature), + authenticatorData: bufferToBase64url(response.authenticatorData), + prfOutput: prfResults?.first ? bufferToBase64url(prfResults.first) : null + } + }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Auth complete failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async verifySession(token) { + const res = await fetch(`${this.serverUrl}/api/session/verify`, { + headers: { Authorization: `Bearer ${token}` } + }); + return res.json(); + } + async refreshToken(token) { + const res = await fetch(`${this.serverUrl}/api/session/refresh`, { + method: "POST", + headers: { Authorization: `Bearer ${token}` } + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Token refresh failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async listCredentials(token) { + const res = await fetch(`${this.serverUrl}/api/user/credentials`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (!res.ok) { + throw new Error("Failed to list credentials"); + } + return res.json(); + } + async setRecoveryEmail(token, email) { + const res = await fetch(`${this.serverUrl}/api/recovery/email/set`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ email }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Failed to set recovery email" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async requestEmailRecovery(email) { + const res = await fetch(`${this.serverUrl}/api/recovery/email/request`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Recovery request failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async verifyRecoveryToken(recoveryToken) { + const res = await fetch(`${this.serverUrl}/api/recovery/email/verify`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ token: recoveryToken }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: "Recovery verification failed" })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); + } + async register(username, displayName, config) { + const { options, userId } = await this.registerStart(username, displayName); + const createOptions = { + publicKey: { + ...options, + challenge: base64urlToUint8Array(options.challenge), + user: { + ...options.user, + id: base64urlToUint8Array(options.user.id) + }, + pubKeyCredParams: options.pubKeyCredParams, + extensions: { + credProps: true, + prf: { eval: { first: new Uint8Array(32) } } + } + } + }; + const credential = await navigator.credentials.create(createOptions); + if (!credential) + throw new Error("Failed to create credential"); + return this.registerComplete(options.challenge, credential, userId, username); + } + async authenticate(credentialId, config) { + const { options } = await this.authStart(credentialId); + const getOptions = { + publicKey: { + challenge: base64urlToUint8Array(options.challenge), + rpId: options.rpId, + userVerification: options.userVerification, + timeout: options.timeout, + allowCredentials: options.allowCredentials?.map((c) => ({ + type: c.type, + id: base64urlToUint8Array(c.id), + transports: c.transports + })), + extensions: { + prf: { eval: { first: new Uint8Array(32) } } + } + } + }; + const credential = await navigator.credentials.get(getOptions); + if (!credential) + throw new Error("Authentication failed"); + const result = await this.authComplete(options.challenge, credential); + const prfResults = credential.getClientExtensionResults()?.prf?.results; + return { + ...result, + prfOutput: prfResults?.first + }; + } +} +function base64urlToUint8Array(base64url) { + const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/"); + const padding = "=".repeat((4 - base64.length % 4) % 4); + const binary = atob(base64 + padding); + const bytes = new Uint8Array(binary.length); + for (let i = 0;i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +} +// node_modules/@noble/hashes/utils.js +/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ +function isBytes(a) { + return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array"; +} +function anumber(n, title = "") { + if (!Number.isSafeInteger(n) || n < 0) { + const prefix = title && `"${title}" `; + throw new Error(`${prefix}expected integer >= 0, got ${n}`); + } +} +function abytes(value, length, title = "") { + const bytes = isBytes(value); + const len = value?.length; + const needsLen = length !== undefined; + if (!bytes || needsLen && len !== length) { + const prefix = title && `"${title}" `; + const ofLen = needsLen ? ` of length ${length}` : ""; + const got = bytes ? `length=${len}` : `type=${typeof value}`; + throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got); + } + return value; +} +function ahash(h) { + if (typeof h !== "function" || typeof h.create !== "function") + throw new Error("Hash must wrapped by utils.createHasher"); + anumber(h.outputLen); + anumber(h.blockLen); +} +function aexists(instance, checkFinished = true) { + if (instance.destroyed) + throw new Error("Hash instance has been destroyed"); + if (checkFinished && instance.finished) + throw new Error("Hash#digest() has already been called"); +} +function aoutput(out, instance) { + abytes(out, undefined, "digestInto() output"); + const min = instance.outputLen; + if (out.length < min) { + throw new Error('"digestInto() output" expected to be of length >=' + min); + } +} +function u32(arr) { + return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); +} +function clean(...arrays) { + for (let i = 0;i < arrays.length; i++) { + arrays[i].fill(0); + } +} +function createView(arr) { + return new DataView(arr.buffer, arr.byteOffset, arr.byteLength); +} +function rotr(word, shift) { + return word << 32 - shift | word >>> shift; +} +var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)(); +function byteSwap(word) { + return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255; +} +function byteSwap32(arr) { + for (let i = 0;i < arr.length; i++) { + arr[i] = byteSwap(arr[i]); + } + return arr; +} +var swap32IfBE = isLE ? (u) => u : byteSwap32; +var hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")(); +var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); +function bytesToHex(bytes) { + abytes(bytes); + if (hasHexBuiltin) + return bytes.toHex(); + let hex = ""; + for (let i = 0;i < bytes.length; i++) { + hex += hexes[bytes[i]]; + } + return hex; +} +var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; +function asciiToBase16(ch) { + if (ch >= asciis._0 && ch <= asciis._9) + return ch - asciis._0; + if (ch >= asciis.A && ch <= asciis.F) + return ch - (asciis.A - 10); + if (ch >= asciis.a && ch <= asciis.f) + return ch - (asciis.a - 10); + return; +} +function hexToBytes(hex) { + if (typeof hex !== "string") + throw new Error("hex string expected, got " + typeof hex); + if (hasHexBuiltin) + return Uint8Array.fromHex(hex); + const hl = hex.length; + const al = hl / 2; + if (hl % 2) + throw new Error("hex string expected, got unpadded hex of length " + hl); + const array = new Uint8Array(al); + for (let ai = 0, hi = 0;ai < al; ai++, hi += 2) { + const n1 = asciiToBase16(hex.charCodeAt(hi)); + const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); + if (n1 === undefined || n2 === undefined) { + const char = hex[hi] + hex[hi + 1]; + throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); + } + array[ai] = n1 * 16 + n2; + } + return array; +} +function concatBytes(...arrays) { + let sum = 0; + for (let i = 0;i < arrays.length; i++) { + const a = arrays[i]; + abytes(a); + sum += a.length; + } + const res = new Uint8Array(sum); + for (let i = 0, pad = 0;i < arrays.length; i++) { + const a = arrays[i]; + res.set(a, pad); + pad += a.length; + } + return res; +} +function createHasher(hashCons, info = {}) { + const hashC = (msg, opts) => hashCons(opts).update(msg).digest(); + const tmp = hashCons(undefined); + hashC.outputLen = tmp.outputLen; + hashC.blockLen = tmp.blockLen; + hashC.create = (opts) => hashCons(opts); + Object.assign(hashC, info); + return Object.freeze(hashC); +} +function randomBytes(bytesLength = 32) { + const cr = typeof globalThis === "object" ? globalThis.crypto : null; + if (typeof cr?.getRandomValues !== "function") + throw new Error("crypto.getRandomValues must be defined"); + return cr.getRandomValues(new Uint8Array(bytesLength)); +} +var oidNist = (suffix) => ({ + oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix]) +}); + +// node_modules/@noble/hashes/_md.js +function Chi(a, b, c) { + return a & b ^ ~a & c; +} +function Maj(a, b, c) { + return a & b ^ a & c ^ b & c; +} + +class HashMD { + blockLen; + outputLen; + padOffset; + isLE; + buffer; + view; + finished = false; + length = 0; + pos = 0; + destroyed = false; + constructor(blockLen, outputLen, padOffset, isLE2) { + this.blockLen = blockLen; + this.outputLen = outputLen; + this.padOffset = padOffset; + this.isLE = isLE2; + this.buffer = new Uint8Array(blockLen); + this.view = createView(this.buffer); + } + update(data) { + aexists(this); + abytes(data); + const { view, buffer, blockLen } = this; + const len = data.length; + for (let pos = 0;pos < len; ) { + const take = Math.min(blockLen - this.pos, len - pos); + if (take === blockLen) { + const dataView = createView(data); + for (;blockLen <= len - pos; pos += blockLen) + this.process(dataView, pos); + continue; + } + buffer.set(data.subarray(pos, pos + take), this.pos); + this.pos += take; + pos += take; + if (this.pos === blockLen) { + this.process(view, 0); + this.pos = 0; + } + } + this.length += data.length; + this.roundClean(); + return this; + } + digestInto(out) { + aexists(this); + aoutput(out, this); + this.finished = true; + const { buffer, view, blockLen, isLE: isLE2 } = this; + let { pos } = this; + buffer[pos++] = 128; + clean(this.buffer.subarray(pos)); + if (this.padOffset > blockLen - pos) { + this.process(view, 0); + pos = 0; + } + for (let i = pos;i < blockLen; i++) + buffer[i] = 0; + view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE2); + this.process(view, 0); + const oview = createView(out); + const len = this.outputLen; + if (len % 4) + throw new Error("_sha2: outputLen must be aligned to 32bit"); + const outLen = len / 4; + const state = this.get(); + if (outLen > state.length) + throw new Error("_sha2: outputLen bigger than state"); + for (let i = 0;i < outLen; i++) + oview.setUint32(4 * i, state[i], isLE2); + } + digest() { + const { buffer, outputLen } = this; + this.digestInto(buffer); + const res = buffer.slice(0, outputLen); + this.destroy(); + return res; + } + _cloneInto(to) { + to ||= new this.constructor; + to.set(...this.get()); + const { blockLen, buffer, length, finished, destroyed, pos } = this; + to.destroyed = destroyed; + to.finished = finished; + to.length = length; + to.pos = pos; + if (length % blockLen) + to.buffer.set(buffer); + return to; + } + clone() { + return this._cloneInto(); + } +} +var SHA256_IV = /* @__PURE__ */ Uint32Array.from([ + 1779033703, + 3144134277, + 1013904242, + 2773480762, + 1359893119, + 2600822924, + 528734635, + 1541459225 +]); +var SHA224_IV = /* @__PURE__ */ Uint32Array.from([ + 3238371032, + 914150663, + 812702999, + 4144912697, + 4290775857, + 1750603025, + 1694076839, + 3204075428 +]); +var SHA384_IV = /* @__PURE__ */ Uint32Array.from([ + 3418070365, + 3238371032, + 1654270250, + 914150663, + 2438529370, + 812702999, + 355462360, + 4144912697, + 1731405415, + 4290775857, + 2394180231, + 1750603025, + 3675008525, + 1694076839, + 1203062813, + 3204075428 +]); +var SHA512_IV = /* @__PURE__ */ Uint32Array.from([ + 1779033703, + 4089235720, + 3144134277, + 2227873595, + 1013904242, + 4271175723, + 2773480762, + 1595750129, + 1359893119, + 2917565137, + 2600822924, + 725511199, + 528734635, + 4215389547, + 1541459225, + 327033209 +]); + +// node_modules/@noble/hashes/_u64.js +var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1); +var _32n = /* @__PURE__ */ BigInt(32); +function fromBig(n, le = false) { + if (le) + return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) }; + return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; +} +function split(lst, le = false) { + const len = lst.length; + let Ah = new Uint32Array(len); + let Al = new Uint32Array(len); + for (let i = 0;i < len; i++) { + const { h, l } = fromBig(lst[i], le); + [Ah[i], Al[i]] = [h, l]; + } + return [Ah, Al]; +} +var shrSH = (h, _l, s) => h >>> s; +var shrSL = (h, l, s) => h << 32 - s | l >>> s; +var rotrSH = (h, l, s) => h >>> s | l << 32 - s; +var rotrSL = (h, l, s) => h << 32 - s | l >>> s; +var rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32; +var rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s; +var rotlSH = (h, l, s) => h << s | l >>> 32 - s; +var rotlSL = (h, l, s) => l << s | h >>> 32 - s; +var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s; +var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s; +function add(Ah, Al, Bh, Bl) { + const l = (Al >>> 0) + (Bl >>> 0); + return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 }; +} +var add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0); +var add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0; +var add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0); +var add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0; +var add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0); +var add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0; + +// node_modules/@noble/hashes/sha2.js +var SHA256_K = /* @__PURE__ */ Uint32Array.from([ + 1116352408, + 1899447441, + 3049323471, + 3921009573, + 961987163, + 1508970993, + 2453635748, + 2870763221, + 3624381080, + 310598401, + 607225278, + 1426881987, + 1925078388, + 2162078206, + 2614888103, + 3248222580, + 3835390401, + 4022224774, + 264347078, + 604807628, + 770255983, + 1249150122, + 1555081692, + 1996064986, + 2554220882, + 2821834349, + 2952996808, + 3210313671, + 3336571891, + 3584528711, + 113926993, + 338241895, + 666307205, + 773529912, + 1294757372, + 1396182291, + 1695183700, + 1986661051, + 2177026350, + 2456956037, + 2730485921, + 2820302411, + 3259730800, + 3345764771, + 3516065817, + 3600352804, + 4094571909, + 275423344, + 430227734, + 506948616, + 659060556, + 883997877, + 958139571, + 1322822218, + 1537002063, + 1747873779, + 1955562222, + 2024104815, + 2227730452, + 2361852424, + 2428436474, + 2756734187, + 3204031479, + 3329325298 +]); +var SHA256_W = /* @__PURE__ */ new Uint32Array(64); + +class SHA2_32B extends HashMD { + constructor(outputLen) { + super(64, outputLen, 8, false); + } + get() { + const { A, B, C, D, E, F, G, H } = this; + return [A, B, C, D, E, F, G, H]; + } + set(A, B, C, D, E, F, G, H) { + this.A = A | 0; + this.B = B | 0; + this.C = C | 0; + this.D = D | 0; + this.E = E | 0; + this.F = F | 0; + this.G = G | 0; + this.H = H | 0; + } + process(view, offset) { + for (let i = 0;i < 16; i++, offset += 4) + SHA256_W[i] = view.getUint32(offset, false); + for (let i = 16;i < 64; i++) { + const W15 = SHA256_W[i - 15]; + const W2 = SHA256_W[i - 2]; + const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3; + const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10; + SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0; + } + let { A, B, C, D, E, F, G, H } = this; + for (let i = 0;i < 64; i++) { + const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25); + const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0; + const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22); + const T2 = sigma0 + Maj(A, B, C) | 0; + H = G; + G = F; + F = E; + E = D + T1 | 0; + D = C; + C = B; + B = A; + A = T1 + T2 | 0; + } + A = A + this.A | 0; + B = B + this.B | 0; + C = C + this.C | 0; + D = D + this.D | 0; + E = E + this.E | 0; + F = F + this.F | 0; + G = G + this.G | 0; + H = H + this.H | 0; + this.set(A, B, C, D, E, F, G, H); + } + roundClean() { + clean(SHA256_W); + } + destroy() { + this.set(0, 0, 0, 0, 0, 0, 0, 0); + clean(this.buffer); + } +} + +class _SHA256 extends SHA2_32B { + A = SHA256_IV[0] | 0; + B = SHA256_IV[1] | 0; + C = SHA256_IV[2] | 0; + D = SHA256_IV[3] | 0; + E = SHA256_IV[4] | 0; + F = SHA256_IV[5] | 0; + G = SHA256_IV[6] | 0; + H = SHA256_IV[7] | 0; + constructor() { + super(32); + } +} + +class _SHA224 extends SHA2_32B { + A = SHA224_IV[0] | 0; + B = SHA224_IV[1] | 0; + C = SHA224_IV[2] | 0; + D = SHA224_IV[3] | 0; + E = SHA224_IV[4] | 0; + F = SHA224_IV[5] | 0; + G = SHA224_IV[6] | 0; + H = SHA224_IV[7] | 0; + constructor() { + super(28); + } +} +var K512 = /* @__PURE__ */ (() => split([ + "0x428a2f98d728ae22", + "0x7137449123ef65cd", + "0xb5c0fbcfec4d3b2f", + "0xe9b5dba58189dbbc", + "0x3956c25bf348b538", + "0x59f111f1b605d019", + "0x923f82a4af194f9b", + "0xab1c5ed5da6d8118", + "0xd807aa98a3030242", + "0x12835b0145706fbe", + "0x243185be4ee4b28c", + "0x550c7dc3d5ffb4e2", + "0x72be5d74f27b896f", + "0x80deb1fe3b1696b1", + "0x9bdc06a725c71235", + "0xc19bf174cf692694", + "0xe49b69c19ef14ad2", + "0xefbe4786384f25e3", + "0x0fc19dc68b8cd5b5", + "0x240ca1cc77ac9c65", + "0x2de92c6f592b0275", + "0x4a7484aa6ea6e483", + "0x5cb0a9dcbd41fbd4", + "0x76f988da831153b5", + "0x983e5152ee66dfab", + "0xa831c66d2db43210", + "0xb00327c898fb213f", + "0xbf597fc7beef0ee4", + "0xc6e00bf33da88fc2", + "0xd5a79147930aa725", + "0x06ca6351e003826f", + "0x142929670a0e6e70", + "0x27b70a8546d22ffc", + "0x2e1b21385c26c926", + "0x4d2c6dfc5ac42aed", + "0x53380d139d95b3df", + "0x650a73548baf63de", + "0x766a0abb3c77b2a8", + "0x81c2c92e47edaee6", + "0x92722c851482353b", + "0xa2bfe8a14cf10364", + "0xa81a664bbc423001", + "0xc24b8b70d0f89791", + "0xc76c51a30654be30", + "0xd192e819d6ef5218", + "0xd69906245565a910", + "0xf40e35855771202a", + "0x106aa07032bbd1b8", + "0x19a4c116b8d2d0c8", + "0x1e376c085141ab53", + "0x2748774cdf8eeb99", + "0x34b0bcb5e19b48a8", + "0x391c0cb3c5c95a63", + "0x4ed8aa4ae3418acb", + "0x5b9cca4f7763e373", + "0x682e6ff3d6b2b8a3", + "0x748f82ee5defb2fc", + "0x78a5636f43172f60", + "0x84c87814a1f0ab72", + "0x8cc702081a6439ec", + "0x90befffa23631e28", + "0xa4506cebde82bde9", + "0xbef9a3f7b2c67915", + "0xc67178f2e372532b", + "0xca273eceea26619c", + "0xd186b8c721c0c207", + "0xeada7dd6cde0eb1e", + "0xf57d4f7fee6ed178", + "0x06f067aa72176fba", + "0x0a637dc5a2c898a6", + "0x113f9804bef90dae", + "0x1b710b35131c471b", + "0x28db77f523047d84", + "0x32caab7b40c72493", + "0x3c9ebe0a15c9bebc", + "0x431d67c49c100d4c", + "0x4cc5d4becb3e42b6", + "0x597f299cfc657e2a", + "0x5fcb6fab3ad6faec", + "0x6c44198c4a475817" +].map((n) => BigInt(n))))(); +var SHA512_Kh = /* @__PURE__ */ (() => K512[0])(); +var SHA512_Kl = /* @__PURE__ */ (() => K512[1])(); +var SHA512_W_H = /* @__PURE__ */ new Uint32Array(80); +var SHA512_W_L = /* @__PURE__ */ new Uint32Array(80); + +class SHA2_64B extends HashMD { + constructor(outputLen) { + super(128, outputLen, 16, false); + } + get() { + const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; + return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl]; + } + set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) { + this.Ah = Ah | 0; + this.Al = Al | 0; + this.Bh = Bh | 0; + this.Bl = Bl | 0; + this.Ch = Ch | 0; + this.Cl = Cl | 0; + this.Dh = Dh | 0; + this.Dl = Dl | 0; + this.Eh = Eh | 0; + this.El = El | 0; + this.Fh = Fh | 0; + this.Fl = Fl | 0; + this.Gh = Gh | 0; + this.Gl = Gl | 0; + this.Hh = Hh | 0; + this.Hl = Hl | 0; + } + process(view, offset) { + for (let i = 0;i < 16; i++, offset += 4) { + SHA512_W_H[i] = view.getUint32(offset); + SHA512_W_L[i] = view.getUint32(offset += 4); + } + for (let i = 16;i < 80; i++) { + const W15h = SHA512_W_H[i - 15] | 0; + const W15l = SHA512_W_L[i - 15] | 0; + const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7); + const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7); + const W2h = SHA512_W_H[i - 2] | 0; + const W2l = SHA512_W_L[i - 2] | 0; + const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6); + const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6); + const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]); + const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]); + SHA512_W_H[i] = SUMh | 0; + SHA512_W_L[i] = SUMl | 0; + } + let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; + for (let i = 0;i < 80; i++) { + const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41); + const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41); + const CHIh = Eh & Fh ^ ~Eh & Gh; + const CHIl = El & Fl ^ ~El & Gl; + const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]); + const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]); + const T1l = T1ll | 0; + const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39); + const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39); + const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch; + const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl; + Hh = Gh | 0; + Hl = Gl | 0; + Gh = Fh | 0; + Gl = Fl | 0; + Fh = Eh | 0; + Fl = El | 0; + ({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)); + Dh = Ch | 0; + Dl = Cl | 0; + Ch = Bh | 0; + Cl = Bl | 0; + Bh = Ah | 0; + Bl = Al | 0; + const All = add3L(T1l, sigma0l, MAJl); + Ah = add3H(All, T1h, sigma0h, MAJh); + Al = All | 0; + } + ({ h: Ah, l: Al } = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0)); + ({ h: Bh, l: Bl } = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0)); + ({ h: Ch, l: Cl } = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0)); + ({ h: Dh, l: Dl } = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0)); + ({ h: Eh, l: El } = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0)); + ({ h: Fh, l: Fl } = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0)); + ({ h: Gh, l: Gl } = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0)); + ({ h: Hh, l: Hl } = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0)); + this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl); + } + roundClean() { + clean(SHA512_W_H, SHA512_W_L); + } + destroy() { + clean(this.buffer); + this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } +} + +class _SHA512 extends SHA2_64B { + Ah = SHA512_IV[0] | 0; + Al = SHA512_IV[1] | 0; + Bh = SHA512_IV[2] | 0; + Bl = SHA512_IV[3] | 0; + Ch = SHA512_IV[4] | 0; + Cl = SHA512_IV[5] | 0; + Dh = SHA512_IV[6] | 0; + Dl = SHA512_IV[7] | 0; + Eh = SHA512_IV[8] | 0; + El = SHA512_IV[9] | 0; + Fh = SHA512_IV[10] | 0; + Fl = SHA512_IV[11] | 0; + Gh = SHA512_IV[12] | 0; + Gl = SHA512_IV[13] | 0; + Hh = SHA512_IV[14] | 0; + Hl = SHA512_IV[15] | 0; + constructor() { + super(64); + } +} + +class _SHA384 extends SHA2_64B { + Ah = SHA384_IV[0] | 0; + Al = SHA384_IV[1] | 0; + Bh = SHA384_IV[2] | 0; + Bl = SHA384_IV[3] | 0; + Ch = SHA384_IV[4] | 0; + Cl = SHA384_IV[5] | 0; + Dh = SHA384_IV[6] | 0; + Dl = SHA384_IV[7] | 0; + Eh = SHA384_IV[8] | 0; + El = SHA384_IV[9] | 0; + Fh = SHA384_IV[10] | 0; + Fl = SHA384_IV[11] | 0; + Gh = SHA384_IV[12] | 0; + Gl = SHA384_IV[13] | 0; + Hh = SHA384_IV[14] | 0; + Hl = SHA384_IV[15] | 0; + constructor() { + super(48); + } +} +var T224_IV = /* @__PURE__ */ Uint32Array.from([ + 2352822216, + 424955298, + 1944164710, + 2312950998, + 502970286, + 855612546, + 1738396948, + 1479516111, + 258812777, + 2077511080, + 2011393907, + 79989058, + 1067287976, + 1780299464, + 286451373, + 2446758561 +]); +var T256_IV = /* @__PURE__ */ Uint32Array.from([ + 573645204, + 4230739756, + 2673172387, + 3360449730, + 596883563, + 1867755857, + 2520282905, + 1497426621, + 2519219938, + 2827943907, + 3193839141, + 1401305490, + 721525244, + 746961066, + 246885852, + 2177182882 +]); + +class _SHA512_224 extends SHA2_64B { + Ah = T224_IV[0] | 0; + Al = T224_IV[1] | 0; + Bh = T224_IV[2] | 0; + Bl = T224_IV[3] | 0; + Ch = T224_IV[4] | 0; + Cl = T224_IV[5] | 0; + Dh = T224_IV[6] | 0; + Dl = T224_IV[7] | 0; + Eh = T224_IV[8] | 0; + El = T224_IV[9] | 0; + Fh = T224_IV[10] | 0; + Fl = T224_IV[11] | 0; + Gh = T224_IV[12] | 0; + Gl = T224_IV[13] | 0; + Hh = T224_IV[14] | 0; + Hl = T224_IV[15] | 0; + constructor() { + super(28); + } +} + +class _SHA512_256 extends SHA2_64B { + Ah = T256_IV[0] | 0; + Al = T256_IV[1] | 0; + Bh = T256_IV[2] | 0; + Bl = T256_IV[3] | 0; + Ch = T256_IV[4] | 0; + Cl = T256_IV[5] | 0; + Dh = T256_IV[6] | 0; + Dl = T256_IV[7] | 0; + Eh = T256_IV[8] | 0; + El = T256_IV[9] | 0; + Fh = T256_IV[10] | 0; + Fl = T256_IV[11] | 0; + Gh = T256_IV[12] | 0; + Gl = T256_IV[13] | 0; + Hh = T256_IV[14] | 0; + Hl = T256_IV[15] | 0; + constructor() { + super(32); + } +} +var sha256 = /* @__PURE__ */ createHasher(() => new _SHA256, /* @__PURE__ */ oidNist(1)); + +// node_modules/@noble/curves/utils.js +/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ +var _0n = /* @__PURE__ */ BigInt(0); +var _1n = /* @__PURE__ */ BigInt(1); +function abool(value, title = "") { + if (typeof value !== "boolean") { + const prefix = title && `"${title}" `; + throw new Error(prefix + "expected boolean, got type=" + typeof value); + } + return value; +} +function abignumber(n) { + if (typeof n === "bigint") { + if (!isPosBig(n)) + throw new Error("positive bigint expected, got " + n); + } else + anumber(n); + return n; +} +function numberToHexUnpadded(num) { + const hex = abignumber(num).toString(16); + return hex.length & 1 ? "0" + hex : hex; +} +function hexToNumber(hex) { + if (typeof hex !== "string") + throw new Error("hex string expected, got " + typeof hex); + return hex === "" ? _0n : BigInt("0x" + hex); +} +function bytesToNumberBE(bytes) { + return hexToNumber(bytesToHex(bytes)); +} +function bytesToNumberLE(bytes) { + return hexToNumber(bytesToHex(copyBytes(abytes(bytes)).reverse())); +} +function numberToBytesBE(n, len) { + anumber(len); + n = abignumber(n); + const res = hexToBytes(n.toString(16).padStart(len * 2, "0")); + if (res.length !== len) + throw new Error("number too large"); + return res; +} +function numberToBytesLE(n, len) { + return numberToBytesBE(n, len).reverse(); +} +function copyBytes(bytes) { + return Uint8Array.from(bytes); +} +var isPosBig = (n) => typeof n === "bigint" && _0n <= n; +function inRange(n, min, max) { + return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max; +} +function aInRange(title, n, min, max) { + if (!inRange(n, min, max)) + throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n); +} +function bitLen(n) { + let len; + for (len = 0;n > _0n; n >>= _1n, len += 1) + ; + return len; +} +var bitMask = (n) => (_1n << BigInt(n)) - _1n; +function createHmacDrbg(hashLen, qByteLen, hmacFn) { + anumber(hashLen, "hashLen"); + anumber(qByteLen, "qByteLen"); + if (typeof hmacFn !== "function") + throw new Error("hmacFn must be a function"); + const u8n = (len) => new Uint8Array(len); + const NULL = Uint8Array.of(); + const byte0 = Uint8Array.of(0); + const byte1 = Uint8Array.of(1); + const _maxDrbgIters = 1000; + let v = u8n(hashLen); + let k = u8n(hashLen); + let i = 0; + const reset = () => { + v.fill(1); + k.fill(0); + i = 0; + }; + const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs)); + const reseed = (seed = NULL) => { + k = h(byte0, seed); + v = h(); + if (seed.length === 0) + return; + k = h(byte1, seed); + v = h(); + }; + const gen = () => { + if (i++ >= _maxDrbgIters) + throw new Error("drbg: tried max amount of iterations"); + let len = 0; + const out = []; + while (len < qByteLen) { + v = h(); + const sl = v.slice(); + out.push(sl); + len += v.length; + } + return concatBytes(...out); + }; + const genUntil = (seed, pred) => { + reset(); + reseed(seed); + let res = undefined; + while (!(res = pred(gen()))) + reseed(); + reset(); + return res; + }; + return genUntil; +} +function validateObject(object, fields = {}, optFields = {}) { + if (!object || typeof object !== "object") + throw new Error("expected valid options object"); + function checkField(fieldName, expectedType, isOpt) { + const val = object[fieldName]; + if (isOpt && val === undefined) + return; + const current = typeof val; + if (current !== expectedType || val === null) + throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`); + } + const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt)); + iter(fields, false); + iter(optFields, true); +} +function memoized(fn) { + const map = new WeakMap; + return (arg, ...args) => { + const val = map.get(arg); + if (val !== undefined) + return val; + const computed = fn(arg, ...args); + map.set(arg, computed); + return computed; + }; +} + +// node_modules/@noble/curves/abstract/modular.js +/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ +var _0n2 = /* @__PURE__ */ BigInt(0); +var _1n2 = /* @__PURE__ */ BigInt(1); +var _2n = /* @__PURE__ */ BigInt(2); +var _3n = /* @__PURE__ */ BigInt(3); +var _4n = /* @__PURE__ */ BigInt(4); +var _5n = /* @__PURE__ */ BigInt(5); +var _7n = /* @__PURE__ */ BigInt(7); +var _8n = /* @__PURE__ */ BigInt(8); +var _9n = /* @__PURE__ */ BigInt(9); +var _16n = /* @__PURE__ */ BigInt(16); +function mod(a, b) { + const result = a % b; + return result >= _0n2 ? result : b + result; +} +function pow2(x, power, modulo) { + let res = x; + while (power-- > _0n2) { + res *= res; + res %= modulo; + } + return res; +} +function invert(number, modulo) { + if (number === _0n2) + throw new Error("invert: expected non-zero number"); + if (modulo <= _0n2) + throw new Error("invert: expected positive modulus, got " + modulo); + let a = mod(number, modulo); + let b = modulo; + let x = _0n2, y = _1n2, u = _1n2, v = _0n2; + while (a !== _0n2) { + const q = b / a; + const r = b % a; + const m = x - u * q; + const n = y - v * q; + b = a, a = r, x = u, y = v, u = m, v = n; + } + const gcd = b; + if (gcd !== _1n2) + throw new Error("invert: does not exist"); + return mod(x, modulo); +} +function assertIsSquare(Fp, root, n) { + if (!Fp.eql(Fp.sqr(root), n)) + throw new Error("Cannot find square root"); +} +function sqrt3mod4(Fp, n) { + const p1div4 = (Fp.ORDER + _1n2) / _4n; + const root = Fp.pow(n, p1div4); + assertIsSquare(Fp, root, n); + return root; +} +function sqrt5mod8(Fp, n) { + const p5div8 = (Fp.ORDER - _5n) / _8n; + const n2 = Fp.mul(n, _2n); + const v = Fp.pow(n2, p5div8); + const nv = Fp.mul(n, v); + const i = Fp.mul(Fp.mul(nv, _2n), v); + const root = Fp.mul(nv, Fp.sub(i, Fp.ONE)); + assertIsSquare(Fp, root, n); + return root; +} +function sqrt9mod16(P) { + const Fp_ = Field(P); + const tn = tonelliShanks(P); + const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); + const c2 = tn(Fp_, c1); + const c3 = tn(Fp_, Fp_.neg(c1)); + const c4 = (P + _7n) / _16n; + return (Fp, n) => { + let tv1 = Fp.pow(n, c4); + let tv2 = Fp.mul(tv1, c1); + const tv3 = Fp.mul(tv1, c2); + const tv4 = Fp.mul(tv1, c3); + const e1 = Fp.eql(Fp.sqr(tv2), n); + const e2 = Fp.eql(Fp.sqr(tv3), n); + tv1 = Fp.cmov(tv1, tv2, e1); + tv2 = Fp.cmov(tv4, tv3, e2); + const e3 = Fp.eql(Fp.sqr(tv2), n); + const root = Fp.cmov(tv1, tv2, e3); + assertIsSquare(Fp, root, n); + return root; + }; +} +function tonelliShanks(P) { + if (P < _3n) + throw new Error("sqrt is not defined for small field"); + let Q = P - _1n2; + let S = 0; + while (Q % _2n === _0n2) { + Q /= _2n; + S++; + } + let Z = _2n; + const _Fp = Field(P); + while (FpLegendre(_Fp, Z) === 1) { + if (Z++ > 1000) + throw new Error("Cannot find square root: probably non-prime P"); + } + if (S === 1) + return sqrt3mod4; + let cc = _Fp.pow(Z, Q); + const Q1div2 = (Q + _1n2) / _2n; + return function tonelliSlow(Fp, n) { + if (Fp.is0(n)) + return n; + if (FpLegendre(Fp, n) !== 1) + throw new Error("Cannot find square root"); + let M = S; + let c = Fp.mul(Fp.ONE, cc); + let t = Fp.pow(n, Q); + let R = Fp.pow(n, Q1div2); + while (!Fp.eql(t, Fp.ONE)) { + if (Fp.is0(t)) + return Fp.ZERO; + let i = 1; + let t_tmp = Fp.sqr(t); + while (!Fp.eql(t_tmp, Fp.ONE)) { + i++; + t_tmp = Fp.sqr(t_tmp); + if (i === M) + throw new Error("Cannot find square root"); + } + const exponent = _1n2 << BigInt(M - i - 1); + const b = Fp.pow(c, exponent); + M = i; + c = Fp.sqr(b); + t = Fp.mul(t, c); + R = Fp.mul(R, b); + } + return R; + }; +} +function FpSqrt(P) { + if (P % _4n === _3n) + return sqrt3mod4; + if (P % _8n === _5n) + return sqrt5mod8; + if (P % _16n === _9n) + return sqrt9mod16(P); + return tonelliShanks(P); +} +var FIELD_FIELDS = [ + "create", + "isValid", + "is0", + "neg", + "inv", + "sqrt", + "sqr", + "eql", + "add", + "sub", + "mul", + "pow", + "div", + "addN", + "subN", + "mulN", + "sqrN" +]; +function validateField(field) { + const initial = { + ORDER: "bigint", + BYTES: "number", + BITS: "number" + }; + const opts = FIELD_FIELDS.reduce((map, val) => { + map[val] = "function"; + return map; + }, initial); + validateObject(field, opts); + return field; +} +function FpPow(Fp, num, power) { + if (power < _0n2) + throw new Error("invalid exponent, negatives unsupported"); + if (power === _0n2) + return Fp.ONE; + if (power === _1n2) + return num; + let p = Fp.ONE; + let d = num; + while (power > _0n2) { + if (power & _1n2) + p = Fp.mul(p, d); + d = Fp.sqr(d); + power >>= _1n2; + } + return p; +} +function FpInvertBatch(Fp, nums, passZero = false) { + const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined); + const multipliedAcc = nums.reduce((acc, num, i) => { + if (Fp.is0(num)) + return acc; + inverted[i] = acc; + return Fp.mul(acc, num); + }, Fp.ONE); + const invertedAcc = Fp.inv(multipliedAcc); + nums.reduceRight((acc, num, i) => { + if (Fp.is0(num)) + return acc; + inverted[i] = Fp.mul(acc, inverted[i]); + return Fp.mul(acc, num); + }, invertedAcc); + return inverted; +} +function FpLegendre(Fp, n) { + const p1mod2 = (Fp.ORDER - _1n2) / _2n; + const powered = Fp.pow(n, p1mod2); + const yes = Fp.eql(powered, Fp.ONE); + const zero = Fp.eql(powered, Fp.ZERO); + const no = Fp.eql(powered, Fp.neg(Fp.ONE)); + if (!yes && !zero && !no) + throw new Error("invalid Legendre symbol result"); + return yes ? 1 : zero ? 0 : -1; +} +function nLength(n, nBitLength) { + if (nBitLength !== undefined) + anumber(nBitLength); + const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length; + const nByteLength = Math.ceil(_nBitLength / 8); + return { nBitLength: _nBitLength, nByteLength }; +} + +class _Field { + ORDER; + BITS; + BYTES; + isLE; + ZERO = _0n2; + ONE = _1n2; + _lengths; + _sqrt; + _mod; + constructor(ORDER, opts = {}) { + if (ORDER <= _0n2) + throw new Error("invalid field: expected ORDER > 0, got " + ORDER); + let _nbitLength = undefined; + this.isLE = false; + if (opts != null && typeof opts === "object") { + if (typeof opts.BITS === "number") + _nbitLength = opts.BITS; + if (typeof opts.sqrt === "function") + this.sqrt = opts.sqrt; + if (typeof opts.isLE === "boolean") + this.isLE = opts.isLE; + if (opts.allowedLengths) + this._lengths = opts.allowedLengths?.slice(); + if (typeof opts.modFromBytes === "boolean") + this._mod = opts.modFromBytes; + } + const { nBitLength, nByteLength } = nLength(ORDER, _nbitLength); + if (nByteLength > 2048) + throw new Error("invalid field: expected ORDER of <= 2048 bytes"); + this.ORDER = ORDER; + this.BITS = nBitLength; + this.BYTES = nByteLength; + this._sqrt = undefined; + Object.preventExtensions(this); + } + create(num) { + return mod(num, this.ORDER); + } + isValid(num) { + if (typeof num !== "bigint") + throw new Error("invalid field element: expected bigint, got " + typeof num); + return _0n2 <= num && num < this.ORDER; + } + is0(num) { + return num === _0n2; + } + isValidNot0(num) { + return !this.is0(num) && this.isValid(num); + } + isOdd(num) { + return (num & _1n2) === _1n2; + } + neg(num) { + return mod(-num, this.ORDER); + } + eql(lhs, rhs) { + return lhs === rhs; + } + sqr(num) { + return mod(num * num, this.ORDER); + } + add(lhs, rhs) { + return mod(lhs + rhs, this.ORDER); + } + sub(lhs, rhs) { + return mod(lhs - rhs, this.ORDER); + } + mul(lhs, rhs) { + return mod(lhs * rhs, this.ORDER); + } + pow(num, power) { + return FpPow(this, num, power); + } + div(lhs, rhs) { + return mod(lhs * invert(rhs, this.ORDER), this.ORDER); + } + sqrN(num) { + return num * num; + } + addN(lhs, rhs) { + return lhs + rhs; + } + subN(lhs, rhs) { + return lhs - rhs; + } + mulN(lhs, rhs) { + return lhs * rhs; + } + inv(num) { + return invert(num, this.ORDER); + } + sqrt(num) { + if (!this._sqrt) + this._sqrt = FpSqrt(this.ORDER); + return this._sqrt(this, num); + } + toBytes(num) { + return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES); + } + fromBytes(bytes, skipValidation = false) { + abytes(bytes); + const { _lengths: allowedLengths, BYTES, isLE: isLE2, ORDER, _mod: modFromBytes } = this; + if (allowedLengths) { + if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) { + throw new Error("Field.fromBytes: expected " + allowedLengths + " bytes, got " + bytes.length); + } + const padded = new Uint8Array(BYTES); + padded.set(bytes, isLE2 ? 0 : padded.length - bytes.length); + bytes = padded; + } + if (bytes.length !== BYTES) + throw new Error("Field.fromBytes: expected " + BYTES + " bytes, got " + bytes.length); + let scalar = isLE2 ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes); + if (modFromBytes) + scalar = mod(scalar, ORDER); + if (!skipValidation) { + if (!this.isValid(scalar)) + throw new Error("invalid field element: outside of range 0..ORDER"); + } + return scalar; + } + invertBatch(lst) { + return FpInvertBatch(this, lst); + } + cmov(a, b, condition) { + return condition ? b : a; + } +} +function Field(ORDER, opts = {}) { + return new _Field(ORDER, opts); +} +function getFieldBytesLength(fieldOrder) { + if (typeof fieldOrder !== "bigint") + throw new Error("field order must be bigint"); + const bitLength = fieldOrder.toString(2).length; + return Math.ceil(bitLength / 8); +} +function getMinHashLength(fieldOrder) { + const length = getFieldBytesLength(fieldOrder); + return length + Math.ceil(length / 2); +} +function mapHashToField(key, fieldOrder, isLE2 = false) { + abytes(key); + const len = key.length; + const fieldLen = getFieldBytesLength(fieldOrder); + const minLen = getMinHashLength(fieldOrder); + if (len < 16 || len < minLen || len > 1024) + throw new Error("expected " + minLen + "-1024 bytes of input, got " + len); + const num = isLE2 ? bytesToNumberLE(key) : bytesToNumberBE(key); + const reduced = mod(num, fieldOrder - _1n2) + _1n2; + return isLE2 ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen); +} + +// node_modules/@noble/curves/abstract/curve.js +/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ +var _0n3 = /* @__PURE__ */ BigInt(0); +var _1n3 = /* @__PURE__ */ BigInt(1); +function negateCt(condition, item) { + const neg = item.negate(); + return condition ? neg : item; +} +function normalizeZ(c, points) { + const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z)); + return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i]))); +} +function validateW(W, bits) { + if (!Number.isSafeInteger(W) || W <= 0 || W > bits) + throw new Error("invalid window size, expected [1.." + bits + "], got W=" + W); +} +function calcWOpts(W, scalarBits) { + validateW(W, scalarBits); + const windows = Math.ceil(scalarBits / W) + 1; + const windowSize = 2 ** (W - 1); + const maxNumber = 2 ** W; + const mask = bitMask(W); + const shiftBy = BigInt(W); + return { windows, windowSize, mask, maxNumber, shiftBy }; +} +function calcOffsets(n, window2, wOpts) { + const { windowSize, mask, maxNumber, shiftBy } = wOpts; + let wbits = Number(n & mask); + let nextN = n >> shiftBy; + if (wbits > windowSize) { + wbits -= maxNumber; + nextN += _1n3; + } + const offsetStart = window2 * windowSize; + const offset = offsetStart + Math.abs(wbits) - 1; + const isZero = wbits === 0; + const isNeg = wbits < 0; + const isNegF = window2 % 2 !== 0; + const offsetF = offsetStart; + return { nextN, offset, isZero, isNeg, isNegF, offsetF }; +} +var pointPrecomputes = new WeakMap; +var pointWindowSizes = new WeakMap; +function getW(P) { + return pointWindowSizes.get(P) || 1; +} +function assert0(n) { + if (n !== _0n3) + throw new Error("invalid wNAF"); +} + +class wNAF { + BASE; + ZERO; + Fn; + bits; + constructor(Point, bits) { + this.BASE = Point.BASE; + this.ZERO = Point.ZERO; + this.Fn = Point.Fn; + this.bits = bits; + } + _unsafeLadder(elm, n, p = this.ZERO) { + let d = elm; + while (n > _0n3) { + if (n & _1n3) + p = p.add(d); + d = d.double(); + n >>= _1n3; + } + return p; + } + precomputeWindow(point, W) { + const { windows, windowSize } = calcWOpts(W, this.bits); + const points = []; + let p = point; + let base = p; + for (let window2 = 0;window2 < windows; window2++) { + base = p; + points.push(base); + for (let i = 1;i < windowSize; i++) { + base = base.add(p); + points.push(base); + } + p = base.double(); + } + return points; + } + wNAF(W, precomputes, n) { + if (!this.Fn.isValid(n)) + throw new Error("invalid scalar"); + let p = this.ZERO; + let f = this.BASE; + const wo = calcWOpts(W, this.bits); + for (let window2 = 0;window2 < wo.windows; window2++) { + const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window2, wo); + n = nextN; + if (isZero) { + f = f.add(negateCt(isNegF, precomputes[offsetF])); + } else { + p = p.add(negateCt(isNeg, precomputes[offset])); + } + } + assert0(n); + return { p, f }; + } + wNAFUnsafe(W, precomputes, n, acc = this.ZERO) { + const wo = calcWOpts(W, this.bits); + for (let window2 = 0;window2 < wo.windows; window2++) { + if (n === _0n3) + break; + const { nextN, offset, isZero, isNeg } = calcOffsets(n, window2, wo); + n = nextN; + if (isZero) { + continue; + } else { + const item = precomputes[offset]; + acc = acc.add(isNeg ? item.negate() : item); + } + } + assert0(n); + return acc; + } + getPrecomputes(W, point, transform) { + let comp = pointPrecomputes.get(point); + if (!comp) { + comp = this.precomputeWindow(point, W); + if (W !== 1) { + if (typeof transform === "function") + comp = transform(comp); + pointPrecomputes.set(point, comp); + } + } + return comp; + } + cached(point, scalar, transform) { + const W = getW(point); + return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar); + } + unsafe(point, scalar, transform, prev) { + const W = getW(point); + if (W === 1) + return this._unsafeLadder(point, scalar, prev); + return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev); + } + createCache(P, W) { + validateW(W, this.bits); + pointWindowSizes.set(P, W); + pointPrecomputes.delete(P); + } + hasCache(elm) { + return getW(elm) !== 1; + } +} +function mulEndoUnsafe(Point, point, k1, k2) { + let acc = point; + let p1 = Point.ZERO; + let p2 = Point.ZERO; + while (k1 > _0n3 || k2 > _0n3) { + if (k1 & _1n3) + p1 = p1.add(acc); + if (k2 & _1n3) + p2 = p2.add(acc); + acc = acc.double(); + k1 >>= _1n3; + k2 >>= _1n3; + } + return { p1, p2 }; +} +function createField(order, field, isLE2) { + if (field) { + if (field.ORDER !== order) + throw new Error("Field.ORDER must match order: Fp == p, Fn == n"); + validateField(field); + return field; + } else { + return Field(order, { isLE: isLE2 }); + } +} +function createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) { + if (FpFnLE === undefined) + FpFnLE = type === "edwards"; + if (!CURVE || typeof CURVE !== "object") + throw new Error(`expected valid ${type} CURVE object`); + for (const p of ["p", "n", "h"]) { + const val = CURVE[p]; + if (!(typeof val === "bigint" && val > _0n3)) + throw new Error(`CURVE.${p} must be positive bigint`); + } + const Fp = createField(CURVE.p, curveOpts.Fp, FpFnLE); + const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE); + const _b = type === "weierstrass" ? "b" : "d"; + const params = ["Gx", "Gy", "a", _b]; + for (const p of params) { + if (!Fp.isValid(CURVE[p])) + throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`); + } + CURVE = Object.freeze(Object.assign({}, CURVE)); + return { CURVE, Fp, Fn }; +} +function createKeygen(randomSecretKey, getPublicKey) { + return function keygen(seed) { + const secretKey = randomSecretKey(seed); + return { secretKey, publicKey: getPublicKey(secretKey) }; + }; +} + +// node_modules/@noble/hashes/hmac.js +class _HMAC { + oHash; + iHash; + blockLen; + outputLen; + finished = false; + destroyed = false; + constructor(hash, key) { + ahash(hash); + abytes(key, undefined, "key"); + this.iHash = hash.create(); + if (typeof this.iHash.update !== "function") + throw new Error("Expected instance of class which extends utils.Hash"); + this.blockLen = this.iHash.blockLen; + this.outputLen = this.iHash.outputLen; + const blockLen = this.blockLen; + const pad = new Uint8Array(blockLen); + pad.set(key.length > blockLen ? hash.create().update(key).digest() : key); + for (let i = 0;i < pad.length; i++) + pad[i] ^= 54; + this.iHash.update(pad); + this.oHash = hash.create(); + for (let i = 0;i < pad.length; i++) + pad[i] ^= 54 ^ 92; + this.oHash.update(pad); + clean(pad); + } + update(buf) { + aexists(this); + this.iHash.update(buf); + return this; + } + digestInto(out) { + aexists(this); + abytes(out, this.outputLen, "output"); + this.finished = true; + this.iHash.digestInto(out); + this.oHash.update(out); + this.oHash.digestInto(out); + this.destroy(); + } + digest() { + const out = new Uint8Array(this.oHash.outputLen); + this.digestInto(out); + return out; + } + _cloneInto(to) { + to ||= Object.create(Object.getPrototypeOf(this), {}); + const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; + to = to; + to.finished = finished; + to.destroyed = destroyed; + to.blockLen = blockLen; + to.outputLen = outputLen; + to.oHash = oHash._cloneInto(to.oHash); + to.iHash = iHash._cloneInto(to.iHash); + return to; + } + clone() { + return this._cloneInto(); + } + destroy() { + this.destroyed = true; + this.oHash.destroy(); + this.iHash.destroy(); + } +} +var hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest(); +hmac.create = (hash, key) => new _HMAC(hash, key); + +// node_modules/@noble/curves/abstract/weierstrass.js +/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ +var divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n2) / den; +function _splitEndoScalar(k, basis, n) { + const [[a1, b1], [a2, b2]] = basis; + const c1 = divNearest(b2 * k, n); + const c2 = divNearest(-b1 * k, n); + let k1 = k - c1 * a1 - c2 * a2; + let k2 = -c1 * b1 - c2 * b2; + const k1neg = k1 < _0n4; + const k2neg = k2 < _0n4; + if (k1neg) + k1 = -k1; + if (k2neg) + k2 = -k2; + const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n4; + if (k1 < _0n4 || k1 >= MAX_NUM || k2 < _0n4 || k2 >= MAX_NUM) { + throw new Error("splitScalar (endomorphism): failed, k=" + k); + } + return { k1neg, k1, k2neg, k2 }; +} +function validateSigFormat(format) { + if (!["compact", "recovered", "der"].includes(format)) + throw new Error('Signature format must be "compact", "recovered", or "der"'); + return format; +} +function validateSigOpts(opts, def) { + const optsn = {}; + for (let optName of Object.keys(def)) { + optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName]; + } + abool(optsn.lowS, "lowS"); + abool(optsn.prehash, "prehash"); + if (optsn.format !== undefined) + validateSigFormat(optsn.format); + return optsn; +} + +class DERErr extends Error { + constructor(m = "") { + super(m); + } +} +var DER = { + Err: DERErr, + _tlv: { + encode: (tag, data) => { + const { Err: E } = DER; + if (tag < 0 || tag > 256) + throw new E("tlv.encode: wrong tag"); + if (data.length & 1) + throw new E("tlv.encode: unpadded data"); + const dataLen = data.length / 2; + const len = numberToHexUnpadded(dataLen); + if (len.length / 2 & 128) + throw new E("tlv.encode: long form length too big"); + const lenLen = dataLen > 127 ? numberToHexUnpadded(len.length / 2 | 128) : ""; + const t = numberToHexUnpadded(tag); + return t + lenLen + len + data; + }, + decode(tag, data) { + const { Err: E } = DER; + let pos = 0; + if (tag < 0 || tag > 256) + throw new E("tlv.encode: wrong tag"); + if (data.length < 2 || data[pos++] !== tag) + throw new E("tlv.decode: wrong tlv"); + const first = data[pos++]; + const isLong = !!(first & 128); + let length = 0; + if (!isLong) + length = first; + else { + const lenLen = first & 127; + if (!lenLen) + throw new E("tlv.decode(long): indefinite length not supported"); + if (lenLen > 4) + throw new E("tlv.decode(long): byte length is too big"); + const lengthBytes = data.subarray(pos, pos + lenLen); + if (lengthBytes.length !== lenLen) + throw new E("tlv.decode: length bytes not complete"); + if (lengthBytes[0] === 0) + throw new E("tlv.decode(long): zero leftmost byte"); + for (const b of lengthBytes) + length = length << 8 | b; + pos += lenLen; + if (length < 128) + throw new E("tlv.decode(long): not minimal encoding"); + } + const v = data.subarray(pos, pos + length); + if (v.length !== length) + throw new E("tlv.decode: wrong value length"); + return { v, l: data.subarray(pos + length) }; + } + }, + _int: { + encode(num) { + const { Err: E } = DER; + if (num < _0n4) + throw new E("integer: negative integers are not allowed"); + let hex = numberToHexUnpadded(num); + if (Number.parseInt(hex[0], 16) & 8) + hex = "00" + hex; + if (hex.length & 1) + throw new E("unexpected DER parsing assertion: unpadded hex"); + return hex; + }, + decode(data) { + const { Err: E } = DER; + if (data[0] & 128) + throw new E("invalid signature integer: negative"); + if (data[0] === 0 && !(data[1] & 128)) + throw new E("invalid signature integer: unnecessary leading zero"); + return bytesToNumberBE(data); + } + }, + toSig(bytes) { + const { Err: E, _int: int, _tlv: tlv } = DER; + const data = abytes(bytes, undefined, "signature"); + const { v: seqBytes, l: seqLeftBytes } = tlv.decode(48, data); + if (seqLeftBytes.length) + throw new E("invalid signature: left bytes after parsing"); + const { v: rBytes, l: rLeftBytes } = tlv.decode(2, seqBytes); + const { v: sBytes, l: sLeftBytes } = tlv.decode(2, rLeftBytes); + if (sLeftBytes.length) + throw new E("invalid signature: left bytes after parsing"); + return { r: int.decode(rBytes), s: int.decode(sBytes) }; + }, + hexFromSig(sig) { + const { _tlv: tlv, _int: int } = DER; + const rs = tlv.encode(2, int.encode(sig.r)); + const ss = tlv.encode(2, int.encode(sig.s)); + const seq = rs + ss; + return tlv.encode(48, seq); + } +}; +var _0n4 = BigInt(0); +var _1n4 = BigInt(1); +var _2n2 = BigInt(2); +var _3n2 = BigInt(3); +var _4n2 = BigInt(4); +function weierstrass(params, extraOpts = {}) { + const validated = createCurveFields("weierstrass", params, extraOpts); + const { Fp, Fn } = validated; + let CURVE = validated.CURVE; + const { h: cofactor, n: CURVE_ORDER } = CURVE; + validateObject(extraOpts, {}, { + allowInfinityPoint: "boolean", + clearCofactor: "function", + isTorsionFree: "function", + fromBytes: "function", + toBytes: "function", + endo: "object" + }); + const { endo } = extraOpts; + if (endo) { + if (!Fp.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) { + throw new Error('invalid endo: expected "beta": bigint and "basises": array'); + } + } + const lengths = getWLengths(Fp, Fn); + function assertCompressionIsSupported() { + if (!Fp.isOdd) + throw new Error("compression is not supported: Field does not have .isOdd()"); + } + function pointToBytes(_c, point, isCompressed) { + const { x, y } = point.toAffine(); + const bx = Fp.toBytes(x); + abool(isCompressed, "isCompressed"); + if (isCompressed) { + assertCompressionIsSupported(); + const hasEvenY = !Fp.isOdd(y); + return concatBytes(pprefix(hasEvenY), bx); + } else { + return concatBytes(Uint8Array.of(4), bx, Fp.toBytes(y)); + } + } + function pointFromBytes(bytes) { + abytes(bytes, undefined, "Point"); + const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; + const length = bytes.length; + const head = bytes[0]; + const tail = bytes.subarray(1); + if (length === comp && (head === 2 || head === 3)) { + const x = Fp.fromBytes(tail); + if (!Fp.isValid(x)) + throw new Error("bad point: is not on curve, wrong x"); + const y2 = weierstrassEquation(x); + let y; + try { + y = Fp.sqrt(y2); + } catch (sqrtError) { + const err = sqrtError instanceof Error ? ": " + sqrtError.message : ""; + throw new Error("bad point: is not on curve, sqrt error" + err); + } + assertCompressionIsSupported(); + const evenY = Fp.isOdd(y); + const evenH = (head & 1) === 1; + if (evenH !== evenY) + y = Fp.neg(y); + return { x, y }; + } else if (length === uncomp && head === 4) { + const L = Fp.BYTES; + const x = Fp.fromBytes(tail.subarray(0, L)); + const y = Fp.fromBytes(tail.subarray(L, L * 2)); + if (!isValidXY(x, y)) + throw new Error("bad point: is not on curve"); + return { x, y }; + } else { + throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`); + } + } + const encodePoint = extraOpts.toBytes || pointToBytes; + const decodePoint = extraOpts.fromBytes || pointFromBytes; + function weierstrassEquation(x) { + const x2 = Fp.sqr(x); + const x3 = Fp.mul(x2, x); + return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); + } + function isValidXY(x, y) { + const left = Fp.sqr(y); + const right = weierstrassEquation(x); + return Fp.eql(left, right); + } + if (!isValidXY(CURVE.Gx, CURVE.Gy)) + throw new Error("bad curve params: generator point"); + const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n2), _4n2); + const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27)); + if (Fp.is0(Fp.add(_4a3, _27b2))) + throw new Error("bad curve params: a or b"); + function acoord(title, n, banZero = false) { + if (!Fp.isValid(n) || banZero && Fp.is0(n)) + throw new Error(`bad point coordinate ${title}`); + return n; + } + function aprjpoint(other) { + if (!(other instanceof Point)) + throw new Error("Weierstrass Point expected"); + } + function splitEndoScalarN(k) { + if (!endo || !endo.basises) + throw new Error("no endo"); + return _splitEndoScalar(k, endo.basises, Fn.ORDER); + } + const toAffineMemo = memoized((p, iz) => { + const { X, Y, Z } = p; + if (Fp.eql(Z, Fp.ONE)) + return { x: X, y: Y }; + const is0 = p.is0(); + if (iz == null) + iz = is0 ? Fp.ONE : Fp.inv(Z); + const x = Fp.mul(X, iz); + const y = Fp.mul(Y, iz); + const zz = Fp.mul(Z, iz); + if (is0) + return { x: Fp.ZERO, y: Fp.ZERO }; + if (!Fp.eql(zz, Fp.ONE)) + throw new Error("invZ was invalid"); + return { x, y }; + }); + const assertValidMemo = memoized((p) => { + if (p.is0()) { + if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y)) + return; + throw new Error("bad point: ZERO"); + } + const { x, y } = p.toAffine(); + if (!Fp.isValid(x) || !Fp.isValid(y)) + throw new Error("bad point: x or y not field elements"); + if (!isValidXY(x, y)) + throw new Error("bad point: equation left != right"); + if (!p.isTorsionFree()) + throw new Error("bad point: not in prime-order subgroup"); + return true; + }); + function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) { + k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z); + k1p = negateCt(k1neg, k1p); + k2p = negateCt(k2neg, k2p); + return k1p.add(k2p); + } + + class Point { + static BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE); + static ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); + static Fp = Fp; + static Fn = Fn; + X; + Y; + Z; + constructor(X, Y, Z) { + this.X = acoord("x", X); + this.Y = acoord("y", Y, true); + this.Z = acoord("z", Z); + Object.freeze(this); + } + static CURVE() { + return CURVE; + } + static fromAffine(p) { + const { x, y } = p || {}; + if (!p || !Fp.isValid(x) || !Fp.isValid(y)) + throw new Error("invalid affine point"); + if (p instanceof Point) + throw new Error("projective point not allowed"); + if (Fp.is0(x) && Fp.is0(y)) + return Point.ZERO; + return new Point(x, y, Fp.ONE); + } + static fromBytes(bytes) { + const P = Point.fromAffine(decodePoint(abytes(bytes, undefined, "point"))); + P.assertValidity(); + return P; + } + static fromHex(hex) { + return Point.fromBytes(hexToBytes(hex)); + } + get x() { + return this.toAffine().x; + } + get y() { + return this.toAffine().y; + } + precompute(windowSize = 8, isLazy = true) { + wnaf.createCache(this, windowSize); + if (!isLazy) + this.multiply(_3n2); + return this; + } + assertValidity() { + assertValidMemo(this); + } + hasEvenY() { + const { y } = this.toAffine(); + if (!Fp.isOdd) + throw new Error("Field doesn't support isOdd"); + return !Fp.isOdd(y); + } + equals(other) { + aprjpoint(other); + const { X: X1, Y: Y1, Z: Z1 } = this; + const { X: X2, Y: Y2, Z: Z2 } = other; + const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1)); + const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1)); + return U1 && U2; + } + negate() { + return new Point(this.X, Fp.neg(this.Y), this.Z); + } + double() { + const { a, b } = CURVE; + const b3 = Fp.mul(b, _3n2); + const { X: X1, Y: Y1, Z: Z1 } = this; + let { ZERO: X3, ZERO: Y3, ZERO: Z3 } = Fp; + let t0 = Fp.mul(X1, X1); + let t1 = Fp.mul(Y1, Y1); + let t2 = Fp.mul(Z1, Z1); + let t3 = Fp.mul(X1, Y1); + t3 = Fp.add(t3, t3); + Z3 = Fp.mul(X1, Z1); + Z3 = Fp.add(Z3, Z3); + X3 = Fp.mul(a, Z3); + Y3 = Fp.mul(b3, t2); + Y3 = Fp.add(X3, Y3); + X3 = Fp.sub(t1, Y3); + Y3 = Fp.add(t1, Y3); + Y3 = Fp.mul(X3, Y3); + X3 = Fp.mul(t3, X3); + Z3 = Fp.mul(b3, Z3); + t2 = Fp.mul(a, t2); + t3 = Fp.sub(t0, t2); + t3 = Fp.mul(a, t3); + t3 = Fp.add(t3, Z3); + Z3 = Fp.add(t0, t0); + t0 = Fp.add(Z3, t0); + t0 = Fp.add(t0, t2); + t0 = Fp.mul(t0, t3); + Y3 = Fp.add(Y3, t0); + t2 = Fp.mul(Y1, Z1); + t2 = Fp.add(t2, t2); + t0 = Fp.mul(t2, t3); + X3 = Fp.sub(X3, t0); + Z3 = Fp.mul(t2, t1); + Z3 = Fp.add(Z3, Z3); + Z3 = Fp.add(Z3, Z3); + return new Point(X3, Y3, Z3); + } + add(other) { + aprjpoint(other); + const { X: X1, Y: Y1, Z: Z1 } = this; + const { X: X2, Y: Y2, Z: Z2 } = other; + let { ZERO: X3, ZERO: Y3, ZERO: Z3 } = Fp; + const a = CURVE.a; + const b3 = Fp.mul(CURVE.b, _3n2); + let t0 = Fp.mul(X1, X2); + let t1 = Fp.mul(Y1, Y2); + let t2 = Fp.mul(Z1, Z2); + let t3 = Fp.add(X1, Y1); + let t4 = Fp.add(X2, Y2); + t3 = Fp.mul(t3, t4); + t4 = Fp.add(t0, t1); + t3 = Fp.sub(t3, t4); + t4 = Fp.add(X1, Z1); + let t5 = Fp.add(X2, Z2); + t4 = Fp.mul(t4, t5); + t5 = Fp.add(t0, t2); + t4 = Fp.sub(t4, t5); + t5 = Fp.add(Y1, Z1); + X3 = Fp.add(Y2, Z2); + t5 = Fp.mul(t5, X3); + X3 = Fp.add(t1, t2); + t5 = Fp.sub(t5, X3); + Z3 = Fp.mul(a, t4); + X3 = Fp.mul(b3, t2); + Z3 = Fp.add(X3, Z3); + X3 = Fp.sub(t1, Z3); + Z3 = Fp.add(t1, Z3); + Y3 = Fp.mul(X3, Z3); + t1 = Fp.add(t0, t0); + t1 = Fp.add(t1, t0); + t2 = Fp.mul(a, t2); + t4 = Fp.mul(b3, t4); + t1 = Fp.add(t1, t2); + t2 = Fp.sub(t0, t2); + t2 = Fp.mul(a, t2); + t4 = Fp.add(t4, t2); + t0 = Fp.mul(t1, t4); + Y3 = Fp.add(Y3, t0); + t0 = Fp.mul(t5, t4); + X3 = Fp.mul(t3, X3); + X3 = Fp.sub(X3, t0); + t0 = Fp.mul(t3, t1); + Z3 = Fp.mul(t5, Z3); + Z3 = Fp.add(Z3, t0); + return new Point(X3, Y3, Z3); + } + subtract(other) { + return this.add(other.negate()); + } + is0() { + return this.equals(Point.ZERO); + } + multiply(scalar) { + const { endo: endo2 } = extraOpts; + if (!Fn.isValidNot0(scalar)) + throw new Error("invalid scalar: out of range"); + let point, fake; + const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p)); + if (endo2) { + const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar); + const { p: k1p, f: k1f } = mul(k1); + const { p: k2p, f: k2f } = mul(k2); + fake = k1f.add(k2f); + point = finishEndo(endo2.beta, k1p, k2p, k1neg, k2neg); + } else { + const { p, f } = mul(scalar); + point = p; + fake = f; + } + return normalizeZ(Point, [point, fake])[0]; + } + multiplyUnsafe(sc) { + const { endo: endo2 } = extraOpts; + const p = this; + if (!Fn.isValid(sc)) + throw new Error("invalid scalar: out of range"); + if (sc === _0n4 || p.is0()) + return Point.ZERO; + if (sc === _1n4) + return p; + if (wnaf.hasCache(this)) + return this.multiply(sc); + if (endo2) { + const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc); + const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); + return finishEndo(endo2.beta, p1, p2, k1neg, k2neg); + } else { + return wnaf.unsafe(p, sc); + } + } + toAffine(invertedZ) { + return toAffineMemo(this, invertedZ); + } + isTorsionFree() { + const { isTorsionFree } = extraOpts; + if (cofactor === _1n4) + return true; + if (isTorsionFree) + return isTorsionFree(Point, this); + return wnaf.unsafe(this, CURVE_ORDER).is0(); + } + clearCofactor() { + const { clearCofactor } = extraOpts; + if (cofactor === _1n4) + return this; + if (clearCofactor) + return clearCofactor(Point, this); + return this.multiplyUnsafe(cofactor); + } + isSmallOrder() { + return this.multiplyUnsafe(cofactor).is0(); + } + toBytes(isCompressed = true) { + abool(isCompressed, "isCompressed"); + this.assertValidity(); + return encodePoint(Point, this, isCompressed); + } + toHex(isCompressed = true) { + return bytesToHex(this.toBytes(isCompressed)); + } + toString() { + return ``; + } + } + const bits = Fn.BITS; + const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits); + Point.BASE.precompute(8); + return Point; +} +function pprefix(hasEvenY) { + return Uint8Array.of(hasEvenY ? 2 : 3); +} +function getWLengths(Fp, Fn) { + return { + secretKey: Fn.BYTES, + publicKey: 1 + Fp.BYTES, + publicKeyUncompressed: 1 + 2 * Fp.BYTES, + publicKeyHasPrefix: true, + signature: 2 * Fn.BYTES + }; +} +function ecdh(Point, ecdhOpts = {}) { + const { Fn } = Point; + const randomBytes_ = ecdhOpts.randomBytes || randomBytes; + const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) }); + function isValidSecretKey(secretKey) { + try { + const num = Fn.fromBytes(secretKey); + return Fn.isValidNot0(num); + } catch (error) { + return false; + } + } + function isValidPublicKey(publicKey, isCompressed) { + const { publicKey: comp, publicKeyUncompressed } = lengths; + try { + const l = publicKey.length; + if (isCompressed === true && l !== comp) + return false; + if (isCompressed === false && l !== publicKeyUncompressed) + return false; + return !!Point.fromBytes(publicKey); + } catch (error) { + return false; + } + } + function randomSecretKey(seed = randomBytes_(lengths.seed)) { + return mapHashToField(abytes(seed, lengths.seed, "seed"), Fn.ORDER); + } + function getPublicKey(secretKey, isCompressed = true) { + return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed); + } + function isProbPub(item) { + const { secretKey, publicKey, publicKeyUncompressed } = lengths; + if (!isBytes(item)) + return; + if ("_lengths" in Fn && Fn._lengths || secretKey === publicKey) + return; + const l = abytes(item, undefined, "key").length; + return l === publicKey || l === publicKeyUncompressed; + } + function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) { + if (isProbPub(secretKeyA) === true) + throw new Error("first arg must be private key"); + if (isProbPub(publicKeyB) === false) + throw new Error("second arg must be public key"); + const s = Fn.fromBytes(secretKeyA); + const b = Point.fromBytes(publicKeyB); + return b.multiply(s).toBytes(isCompressed); + } + const utils = { + isValidSecretKey, + isValidPublicKey, + randomSecretKey + }; + const keygen = createKeygen(randomSecretKey, getPublicKey); + return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths }); +} +function ecdsa(Point, hash, ecdsaOpts = {}) { + ahash(hash); + validateObject(ecdsaOpts, {}, { + hmac: "function", + lowS: "boolean", + randomBytes: "function", + bits2int: "function", + bits2int_modN: "function" + }); + ecdsaOpts = Object.assign({}, ecdsaOpts); + const randomBytes2 = ecdsaOpts.randomBytes || randomBytes; + const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg)); + const { Fp, Fn } = Point; + const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn; + const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts); + const defaultSigOpts = { + prehash: true, + lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : true, + format: "compact", + extraEntropy: false + }; + const hasLargeCofactor = CURVE_ORDER * _2n2 < Fp.ORDER; + function isBiggerThanHalfOrder(number) { + const HALF = CURVE_ORDER >> _1n4; + return number > HALF; + } + function validateRS(title, num) { + if (!Fn.isValidNot0(num)) + throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`); + return num; + } + function assertSmallCofactor() { + if (hasLargeCofactor) + throw new Error('"recovered" sig type is not supported for cofactor >2 curves'); + } + function validateSigLength(bytes, format) { + validateSigFormat(format); + const size = lengths.signature; + const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : undefined; + return abytes(bytes, sizer); + } + + class Signature { + r; + s; + recovery; + constructor(r, s, recovery) { + this.r = validateRS("r", r); + this.s = validateRS("s", s); + if (recovery != null) { + assertSmallCofactor(); + if (![0, 1, 2, 3].includes(recovery)) + throw new Error("invalid recovery id"); + this.recovery = recovery; + } + Object.freeze(this); + } + static fromBytes(bytes, format = defaultSigOpts.format) { + validateSigLength(bytes, format); + let recid; + if (format === "der") { + const { r: r2, s: s2 } = DER.toSig(abytes(bytes)); + return new Signature(r2, s2); + } + if (format === "recovered") { + recid = bytes[0]; + format = "compact"; + bytes = bytes.subarray(1); + } + const L = lengths.signature / 2; + const r = bytes.subarray(0, L); + const s = bytes.subarray(L, L * 2); + return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid); + } + static fromHex(hex, format) { + return this.fromBytes(hexToBytes(hex), format); + } + assertRecovery() { + const { recovery } = this; + if (recovery == null) + throw new Error("invalid recovery id: must be present"); + return recovery; + } + addRecoveryBit(recovery) { + return new Signature(this.r, this.s, recovery); + } + recoverPublicKey(messageHash) { + const { r, s } = this; + const recovery = this.assertRecovery(); + const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r; + if (!Fp.isValid(radj)) + throw new Error("invalid recovery id: sig.r+curve.n != R.x"); + const x = Fp.toBytes(radj); + const R = Point.fromBytes(concatBytes(pprefix((recovery & 1) === 0), x)); + const ir = Fn.inv(radj); + const h = bits2int_modN(abytes(messageHash, undefined, "msgHash")); + const u1 = Fn.create(-h * ir); + const u2 = Fn.create(s * ir); + const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2)); + if (Q.is0()) + throw new Error("invalid recovery: point at infinify"); + Q.assertValidity(); + return Q; + } + hasHighS() { + return isBiggerThanHalfOrder(this.s); + } + toBytes(format = defaultSigOpts.format) { + validateSigFormat(format); + if (format === "der") + return hexToBytes(DER.hexFromSig(this)); + const { r, s } = this; + const rb = Fn.toBytes(r); + const sb = Fn.toBytes(s); + if (format === "recovered") { + assertSmallCofactor(); + return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb); + } + return concatBytes(rb, sb); + } + toHex(format) { + return bytesToHex(this.toBytes(format)); + } + } + const bits2int = ecdsaOpts.bits2int || function bits2int_def(bytes) { + if (bytes.length > 8192) + throw new Error("input is too large"); + const num = bytesToNumberBE(bytes); + const delta = bytes.length * 8 - fnBits; + return delta > 0 ? num >> BigInt(delta) : num; + }; + const bits2int_modN = ecdsaOpts.bits2int_modN || function bits2int_modN_def(bytes) { + return Fn.create(bits2int(bytes)); + }; + const ORDER_MASK = bitMask(fnBits); + function int2octets(num) { + aInRange("num < 2^" + fnBits, num, _0n4, ORDER_MASK); + return Fn.toBytes(num); + } + function validateMsgAndHash(message, prehash) { + abytes(message, undefined, "message"); + return prehash ? abytes(hash(message), undefined, "prehashed message") : message; + } + function prepSig(message, secretKey, opts) { + const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts); + message = validateMsgAndHash(message, prehash); + const h1int = bits2int_modN(message); + const d = Fn.fromBytes(secretKey); + if (!Fn.isValidNot0(d)) + throw new Error("invalid private key"); + const seedArgs = [int2octets(d), int2octets(h1int)]; + if (extraEntropy != null && extraEntropy !== false) { + const e = extraEntropy === true ? randomBytes2(lengths.secretKey) : extraEntropy; + seedArgs.push(abytes(e, undefined, "extraEntropy")); + } + const seed = concatBytes(...seedArgs); + const m = h1int; + function k2sig(kBytes) { + const k = bits2int(kBytes); + if (!Fn.isValidNot0(k)) + return; + const ik = Fn.inv(k); + const q = Point.BASE.multiply(k).toAffine(); + const r = Fn.create(q.x); + if (r === _0n4) + return; + const s = Fn.create(ik * Fn.create(m + r * d)); + if (s === _0n4) + return; + let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n4); + let normS = s; + if (lowS && isBiggerThanHalfOrder(s)) { + normS = Fn.neg(s); + recovery ^= 1; + } + return new Signature(r, normS, hasLargeCofactor ? undefined : recovery); + } + return { seed, k2sig }; + } + function sign(message, secretKey, opts = {}) { + const { seed, k2sig } = prepSig(message, secretKey, opts); + const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac2); + const sig = drbg(seed, k2sig); + return sig.toBytes(opts.format); + } + function verify(signature, message, publicKey, opts = {}) { + const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts); + publicKey = abytes(publicKey, undefined, "publicKey"); + message = validateMsgAndHash(message, prehash); + if (!isBytes(signature)) { + const end = signature instanceof Signature ? ", use sig.toBytes()" : ""; + throw new Error("verify expects Uint8Array signature" + end); + } + validateSigLength(signature, format); + try { + const sig = Signature.fromBytes(signature, format); + const P = Point.fromBytes(publicKey); + if (lowS && sig.hasHighS()) + return false; + const { r, s } = sig; + const h = bits2int_modN(message); + const is = Fn.inv(s); + const u1 = Fn.create(h * is); + const u2 = Fn.create(r * is); + const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); + if (R.is0()) + return false; + const v = Fn.create(R.x); + return v === r; + } catch (e) { + return false; + } + } + function recoverPublicKey(signature, message, opts = {}) { + const { prehash } = validateSigOpts(opts, defaultSigOpts); + message = validateMsgAndHash(message, prehash); + return Signature.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes(); + } + return Object.freeze({ + keygen, + getPublicKey, + getSharedSecret, + utils, + lengths, + Point, + sign, + verify, + recoverPublicKey, + Signature, + hash + }); +} + +// node_modules/@noble/curves/secp256k1.js +/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ +var secp256k1_CURVE = { + p: BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"), + n: BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), + h: BigInt(1), + a: BigInt(0), + b: BigInt(7), + Gx: BigInt("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), + Gy: BigInt("0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8") +}; +var secp256k1_ENDO = { + beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), + basises: [ + [BigInt("0x3086d221a7d46bcde86c90e49284eb15"), -BigInt("0xe4437ed6010e88286f547fa90abfe4c3")], + [BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"), BigInt("0x3086d221a7d46bcde86c90e49284eb15")] + ] +}; +var _2n3 = /* @__PURE__ */ BigInt(2); +function sqrtMod(y) { + const P = secp256k1_CURVE.p; + const _3n3 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22); + const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88); + const b2 = y * y * y % P; + const b3 = b2 * b2 * y % P; + const b6 = pow2(b3, _3n3, P) * b3 % P; + const b9 = pow2(b6, _3n3, P) * b3 % P; + const b11 = pow2(b9, _2n3, P) * b2 % P; + const b22 = pow2(b11, _11n, P) * b11 % P; + const b44 = pow2(b22, _22n, P) * b22 % P; + const b88 = pow2(b44, _44n, P) * b44 % P; + const b176 = pow2(b88, _88n, P) * b88 % P; + const b220 = pow2(b176, _44n, P) * b44 % P; + const b223 = pow2(b220, _3n3, P) * b3 % P; + const t1 = pow2(b223, _23n, P) * b22 % P; + const t2 = pow2(t1, _6n, P) * b2 % P; + const root = pow2(t2, _2n3, P); + if (!Fpk1.eql(Fpk1.sqr(root), y)) + throw new Error("Cannot find square root"); + return root; +} +var Fpk1 = Field(secp256k1_CURVE.p, { sqrt: sqrtMod }); +var Pointk1 = /* @__PURE__ */ weierstrass(secp256k1_CURVE, { + Fp: Fpk1, + endo: secp256k1_ENDO +}); +var secp256k1 = /* @__PURE__ */ ecdsa(Pointk1, sha256); + +// node_modules/@noble/hashes/sha3.js +var _0n5 = BigInt(0); +var _1n5 = BigInt(1); +var _2n4 = BigInt(2); +var _7n2 = BigInt(7); +var _256n = BigInt(256); +var _0x71n = BigInt(113); +var SHA3_PI = []; +var SHA3_ROTL = []; +var _SHA3_IOTA = []; +for (let round = 0, R = _1n5, x = 1, y = 0;round < 24; round++) { + [x, y] = [y, (2 * x + 3 * y) % 5]; + SHA3_PI.push(2 * (5 * y + x)); + SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64); + let t = _0n5; + for (let j = 0;j < 7; j++) { + R = (R << _1n5 ^ (R >> _7n2) * _0x71n) % _256n; + if (R & _2n4) + t ^= _1n5 << (_1n5 << BigInt(j)) - _1n5; + } + _SHA3_IOTA.push(t); +} +var IOTAS = split(_SHA3_IOTA, true); +var SHA3_IOTA_H = IOTAS[0]; +var SHA3_IOTA_L = IOTAS[1]; +var rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s); +var rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s); +function keccakP(s, rounds = 24) { + const B = new Uint32Array(5 * 2); + for (let round = 24 - rounds;round < 24; round++) { + for (let x = 0;x < 10; x++) + B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40]; + for (let x = 0;x < 10; x += 2) { + const idx1 = (x + 8) % 10; + const idx0 = (x + 2) % 10; + const B0 = B[idx0]; + const B1 = B[idx0 + 1]; + const Th = rotlH(B0, B1, 1) ^ B[idx1]; + const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1]; + for (let y = 0;y < 50; y += 10) { + s[x + y] ^= Th; + s[x + y + 1] ^= Tl; + } + } + let curH = s[2]; + let curL = s[3]; + for (let t = 0;t < 24; t++) { + const shift = SHA3_ROTL[t]; + const Th = rotlH(curH, curL, shift); + const Tl = rotlL(curH, curL, shift); + const PI = SHA3_PI[t]; + curH = s[PI]; + curL = s[PI + 1]; + s[PI] = Th; + s[PI + 1] = Tl; + } + for (let y = 0;y < 50; y += 10) { + for (let x = 0;x < 10; x++) + B[x] = s[y + x]; + for (let x = 0;x < 10; x++) + s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10]; + } + s[0] ^= SHA3_IOTA_H[round]; + s[1] ^= SHA3_IOTA_L[round]; + } + clean(B); +} + +class Keccak { + state; + pos = 0; + posOut = 0; + finished = false; + state32; + destroyed = false; + blockLen; + suffix; + outputLen; + enableXOF = false; + rounds; + constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) { + this.blockLen = blockLen; + this.suffix = suffix; + this.outputLen = outputLen; + this.enableXOF = enableXOF; + this.rounds = rounds; + anumber(outputLen, "outputLen"); + if (!(0 < blockLen && blockLen < 200)) + throw new Error("only keccak-f1600 function is supported"); + this.state = new Uint8Array(200); + this.state32 = u32(this.state); + } + clone() { + return this._cloneInto(); + } + keccak() { + swap32IfBE(this.state32); + keccakP(this.state32, this.rounds); + swap32IfBE(this.state32); + this.posOut = 0; + this.pos = 0; + } + update(data) { + aexists(this); + abytes(data); + const { blockLen, state } = this; + const len = data.length; + for (let pos = 0;pos < len; ) { + const take = Math.min(blockLen - this.pos, len - pos); + for (let i = 0;i < take; i++) + state[this.pos++] ^= data[pos++]; + if (this.pos === blockLen) + this.keccak(); + } + return this; + } + finish() { + if (this.finished) + return; + this.finished = true; + const { state, suffix, pos, blockLen } = this; + state[pos] ^= suffix; + if ((suffix & 128) !== 0 && pos === blockLen - 1) + this.keccak(); + state[blockLen - 1] ^= 128; + this.keccak(); + } + writeInto(out) { + aexists(this, false); + abytes(out); + this.finish(); + const bufferOut = this.state; + const { blockLen } = this; + for (let pos = 0, len = out.length;pos < len; ) { + if (this.posOut >= blockLen) + this.keccak(); + const take = Math.min(blockLen - this.posOut, len - pos); + out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); + this.posOut += take; + pos += take; + } + return out; + } + xofInto(out) { + if (!this.enableXOF) + throw new Error("XOF is not possible for this instance"); + return this.writeInto(out); + } + xof(bytes) { + anumber(bytes); + return this.xofInto(new Uint8Array(bytes)); + } + digestInto(out) { + aoutput(out, this); + if (this.finished) + throw new Error("digest() was already called"); + this.writeInto(out); + this.destroy(); + return out; + } + digest() { + return this.digestInto(new Uint8Array(this.outputLen)); + } + destroy() { + this.destroyed = true; + clean(this.state); + } + _cloneInto(to) { + const { blockLen, suffix, outputLen, rounds, enableXOF } = this; + to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds); + to.state32.set(this.state32); + to.pos = this.pos; + to.posOut = this.posOut; + to.finished = this.finished; + to.rounds = rounds; + to.suffix = suffix; + to.outputLen = outputLen; + to.enableXOF = enableXOF; + to.destroyed = this.destroyed; + return to; + } +} +var genKeccak = (suffix, blockLen, outputLen, info = {}) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info); +var keccak_256 = /* @__PURE__ */ genKeccak(1, 136, 32); + +// src/client/key-derivation.ts +class EncryptIDKeyManager { + masterKey = null; + derivedKeys = null; + fromPRF = false; + async initFromPRF(prfOutput) { + this.masterKey = await crypto.subtle.importKey("raw", prfOutput, { name: "HKDF" }, false, ["deriveKey", "deriveBits"]); + this.fromPRF = true; + this.derivedKeys = null; + } + async initFromPassphrase(passphrase, salt) { + const encoder = new TextEncoder; + const passphraseKey = await crypto.subtle.importKey("raw", encoder.encode(passphrase), { name: "PBKDF2" }, false, ["deriveBits"]); + const masterKeyMaterial = await crypto.subtle.deriveBits({ name: "PBKDF2", salt, iterations: 600000, hash: "SHA-256" }, passphraseKey, 256); + this.masterKey = await crypto.subtle.importKey("raw", masterKeyMaterial, { name: "HKDF" }, false, ["deriveKey", "deriveBits"]); + this.fromPRF = false; + this.derivedKeys = null; + } + static generateSalt() { + return crypto.getRandomValues(new Uint8Array(32)); + } + isInitialized() { + return this.masterKey !== null; + } + async getKeys() { + if (!this.masterKey) + throw new Error("Key manager not initialized"); + if (this.derivedKeys) + return this.derivedKeys; + const [encryptionKey, signingKeyPair, didSeed, ethereum] = await Promise.all([ + this.deriveEncryptionKey(), + this.deriveSigningKeyPair(), + this.deriveDIDSeed(), + this.deriveEthereumKeys() + ]); + const did = await this.generateDID(didSeed); + this.derivedKeys = { encryptionKey, signingKeyPair, didSeed, did, fromPRF: this.fromPRF, ethereum }; + return this.derivedKeys; + } + async deriveEncryptionKey() { + const encoder = new TextEncoder; + return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: encoder.encode("encryptid-encryption-key-v1"), info: encoder.encode("AES-256-GCM") }, this.masterKey, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt", "wrapKey", "unwrapKey"]); + } + async deriveSigningKeyPair() { + return crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign", "verify"]); + } + async deriveEthereumKeys() { + const encoder = new TextEncoder; + const privateKeyBits = await crypto.subtle.deriveBits({ + name: "HKDF", + hash: "SHA-256", + salt: encoder.encode("encryptid-ethereum-secp256k1-v1"), + info: encoder.encode("secp256k1-private-key") + }, this.masterKey, 256); + const privateKey = new Uint8Array(privateKeyBits); + const publicKey = secp256k1.getPublicKey(privateKey, true); + const uncompressedPubKey = secp256k1.getPublicKey(privateKey, false); + const pubKeyHash = keccak_256(uncompressedPubKey.slice(1)); + const addressBytes = pubKeyHash.slice(-20); + const address = toChecksumAddress(addressBytes); + return { address, publicKey, privateKey }; + } + async deriveDIDSeed() { + const encoder = new TextEncoder; + const seed = await crypto.subtle.deriveBits({ name: "HKDF", hash: "SHA-256", salt: encoder.encode("encryptid-did-key-v1"), info: encoder.encode("Ed25519-seed") }, this.masterKey, 256); + return new Uint8Array(seed); + } + async generateDID(seed) { + const publicKeyHash = await crypto.subtle.digest("SHA-256", seed); + const publicKeyBytes = new Uint8Array(publicKeyHash).slice(0, 32); + const multicodecPrefix = new Uint8Array([237, 1]); + const multicodecKey = new Uint8Array(34); + multicodecKey.set(multicodecPrefix); + multicodecKey.set(publicKeyBytes, 2); + const base58Encoded = bufferToBase64url(multicodecKey.buffer).replace(/-/g, "").replace(/_/g, ""); + return `did:key:z${base58Encoded}`; + } + clear() { + this.masterKey = null; + this.derivedKeys = null; + this.fromPRF = false; + } +} +function toChecksumAddress(addressBytes) { + const hex = Array.from(addressBytes).map((b) => b.toString(16).padStart(2, "0")).join(""); + const hash = keccak_256(new TextEncoder().encode(hex)); + let checksummed = "0x"; + for (let i = 0;i < 40; i++) { + const hashNibble = hash[Math.floor(i / 2)] >> (i % 2 === 0 ? 4 : 0) & 15; + checksummed += hashNibble >= 8 ? hex[i].toUpperCase() : hex[i]; + } + return checksummed; +} +function signEthHash(hash, privateKey) { + const sigBytes = secp256k1.sign(hash, privateKey, { prehash: false, format: "recovered" }); + const sig = secp256k1.Signature.fromBytes(sigBytes, "recovered"); + return { + r: sig.r.toString(16).padStart(64, "0"), + s: sig.s.toString(16).padStart(64, "0"), + v: (sig.recovery ?? 0) + 27, + signature: sig.toBytes("compact") + }; +} +var keyManagerInstance = null; +function getKeyManager() { + if (!keyManagerInstance) + keyManagerInstance = new EncryptIDKeyManager; + return keyManagerInstance; +} +// src/client/session.ts +var OPERATION_PERMISSIONS = { + "rspace:view-public": { minAuthLevel: 1 /* BASIC */ }, + "rspace:view-private": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:edit-board": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:create-board": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:delete-board": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rspace:encrypt-board": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "encrypt" }, + "rwallet:view-balance": { minAuthLevel: 1 /* BASIC */ }, + "rwallet:view-history": { minAuthLevel: 2 /* STANDARD */ }, + "rwallet:send-small": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "wallet" }, + "rwallet:send-large": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "wallet", maxAgeSeconds: 60 }, + "rwallet:add-guardian": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rwallet:remove-guardian": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rvote:view-proposals": { minAuthLevel: 1 /* BASIC */ }, + "rvote:cast-vote": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "sign", maxAgeSeconds: 300 }, + "rvote:delegate": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "wallet" }, + "rfiles:list-files": { minAuthLevel: 2 /* STANDARD */ }, + "rfiles:download-own": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "encrypt" }, + "rfiles:upload": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "encrypt" }, + "rfiles:share": { minAuthLevel: 3 /* ELEVATED */, requiresCapability: "encrypt" }, + "rfiles:delete": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rfiles:export-keys": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rmaps:view-public": { minAuthLevel: 1 /* BASIC */ }, + "rmaps:add-location": { minAuthLevel: 2 /* STANDARD */ }, + "rmaps:edit-location": { minAuthLevel: 2 /* STANDARD */, requiresCapability: "sign" }, + "account:view-profile": { minAuthLevel: 2 /* STANDARD */ }, + "account:edit-profile": { minAuthLevel: 3 /* ELEVATED */ }, + "account:export-data": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "account:delete": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rspace:create-space": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:configure-space": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rspace:delete-space": { minAuthLevel: 4 /* CRITICAL */, maxAgeSeconds: 60 }, + "rspace:invite-member": { minAuthLevel: 2 /* STANDARD */ }, + "rspace:remove-member": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rspace:change-visibility": { minAuthLevel: 3 /* ELEVATED */, maxAgeSeconds: 300 }, + "rfunds:create-space": { minAuthLevel: 2 /* STANDARD */ }, + "rfunds:edit-flows": { minAuthLevel: 2 /* STANDARD */ }, + "rfunds:share-space": { minAuthLevel: 2 /* STANDARD */ } +}; +var SESSION_STORAGE_KEY = "encryptid_session"; +var TOKEN_REFRESH_THRESHOLD = 5 * 60 * 1000; + +class SessionManager { + session = null; + refreshTimer = null; + constructor() { + this.restoreSession(); + } + async createSession(authResult, did, capabilities, walletAddress, username) { + const now = Math.floor(Date.now() / 1000); + const claims = { + iss: "https://encryptid.jeffemmett.com", + sub: did, + aud: ["rspace.online", "rwallet.online", "rvote.online", "rfiles.online", "rmaps.online", "rmail.online"], + iat: now, + exp: now + 15 * 60, + jti: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer), + username: username || "", + eid: { + walletAddress, + credentialId: authResult.credentialId, + authLevel: 3 /* ELEVATED */, + authTime: now, + capabilities, + recoveryConfigured: false + } + }; + const accessToken = this.createUnsignedToken(claims); + const refreshToken = this.createRefreshToken(did); + this.session = { accessToken, refreshToken, claims, lastAuthTime: Date.now() }; + this.persistSession(); + this.scheduleRefresh(); + return this.session; + } + getSession() { + return this.session; + } + getDID() { + return this.session?.claims.sub ?? null; + } + getAccessToken() { + return this.session?.accessToken ?? null; + } + getAuthLevel() { + if (!this.session) + return 1 /* BASIC */; + const now = Math.floor(Date.now() / 1000); + if (now >= this.session.claims.exp) + return 1 /* BASIC */; + const authAge = now - this.session.claims.eid.authTime; + if (authAge < 60) + return 3 /* ELEVATED */; + if (authAge < 15 * 60) + return 2 /* STANDARD */; + return 1 /* BASIC */; + } + canPerform(operation) { + const permission = OPERATION_PERMISSIONS[operation]; + if (!permission) + return { allowed: false, reason: "Unknown operation" }; + if (!this.session) + return { allowed: false, reason: "Not authenticated" }; + const currentLevel = this.getAuthLevel(); + if (currentLevel < permission.minAuthLevel) { + return { allowed: false, reason: `Requires ${AuthLevel[permission.minAuthLevel]} auth level (current: ${AuthLevel[currentLevel]})` }; + } + if (permission.requiresCapability) { + if (!this.session.claims.eid.capabilities[permission.requiresCapability]) { + return { allowed: false, reason: `Requires ${permission.requiresCapability} capability` }; + } + } + if (permission.maxAgeSeconds) { + const authAge = Math.floor(Date.now() / 1000) - this.session.claims.eid.authTime; + if (authAge > permission.maxAgeSeconds) { + return { allowed: false, reason: `Authentication too old (${authAge}s > ${permission.maxAgeSeconds}s)` }; + } + } + return { allowed: true }; + } + requiresFreshAuth(operation) { + const permission = OPERATION_PERMISSIONS[operation]; + if (!permission) + return true; + if (permission.minAuthLevel >= 4 /* CRITICAL */) + return true; + if (permission.maxAgeSeconds && permission.maxAgeSeconds <= 60) + return true; + return false; + } + upgradeAuthLevel(level = 3 /* ELEVATED */) { + if (!this.session) + return; + this.session.claims.eid.authLevel = level; + this.session.claims.eid.authTime = Math.floor(Date.now() / 1000); + this.session.lastAuthTime = Date.now(); + this.persistSession(); + } + clearSession() { + this.session = null; + if (this.refreshTimer) { + clearTimeout(this.refreshTimer); + this.refreshTimer = null; + } + try { + localStorage.removeItem(SESSION_STORAGE_KEY); + } catch {} + } + isValid() { + if (!this.session) + return false; + return Math.floor(Date.now() / 1000) < this.session.claims.exp; + } + createUnsignedToken(claims) { + const header = { alg: "none", typ: "JWT" }; + return `${btoa(JSON.stringify(header))}.${btoa(JSON.stringify(claims))}.`; + } + createRefreshToken(did) { + return btoa(JSON.stringify({ + sub: did, + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, + jti: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer) + })); + } + persistSession() { + if (!this.session) + return; + try { + localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(this.session)); + } catch {} + } + restoreSession() { + try { + const stored = localStorage.getItem(SESSION_STORAGE_KEY); + if (stored) { + const session = JSON.parse(stored); + if (Math.floor(Date.now() / 1000) < session.claims.exp) { + this.session = session; + this.scheduleRefresh(); + } else { + localStorage.removeItem(SESSION_STORAGE_KEY); + } + } + } catch {} + } + scheduleRefresh() { + if (!this.session) + return; + if (this.refreshTimer) + clearTimeout(this.refreshTimer); + const expiresAt = this.session.claims.exp * 1000; + const refreshAt = expiresAt - TOKEN_REFRESH_THRESHOLD; + const delay = Math.max(refreshAt - Date.now(), 0); + this.refreshTimer = setTimeout(() => this.refreshTokens(), delay); + } + async refreshTokens() { + if (!this.session) + return; + const now = Math.floor(Date.now() / 1000); + this.session.claims.eid.authLevel = Math.min(this.session.claims.eid.authLevel, 2 /* STANDARD */); + this.session.claims.iat = now; + this.session.claims.exp = now + 15 * 60; + this.session.claims.jti = bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer); + this.session.accessToken = this.createUnsignedToken(this.session.claims); + this.persistSession(); + this.scheduleRefresh(); + } +} +var sessionManagerInstance = null; +function getSessionManager() { + if (!sessionManagerInstance) + sessionManagerInstance = new SessionManager; + return sessionManagerInstance; +} +// src/client/recovery.ts +class RecoveryManager { + config = null; + activeRequest = null; + constructor() { + this.loadConfig(); + } + async initializeRecovery(threshold = 3) { + this.config = { + threshold, + delaySeconds: 48 * 60 * 60, + guardians: [], + guardianListHash: "", + updatedAt: Date.now() + }; + await this.saveConfig(); + return this.config; + } + async addGuardian(guardian) { + if (!this.config) + throw new Error("Recovery not initialized"); + if (this.config.guardians.length >= 7) + throw new Error("Maximum of 7 guardians allowed"); + const newGuardian = { + ...guardian, + id: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer), + addedAt: Date.now() + }; + this.config.guardians.push(newGuardian); + this.config.guardianListHash = await this.hashGuardianList(); + this.config.updatedAt = Date.now(); + await this.saveConfig(); + return newGuardian; + } + async removeGuardian(guardianId) { + if (!this.config) + throw new Error("Recovery not initialized"); + const index = this.config.guardians.findIndex((g) => g.id === guardianId); + if (index === -1) + throw new Error("Guardian not found"); + const remainingWeight = this.config.guardians.filter((g) => g.id !== guardianId).reduce((sum, g) => sum + g.weight, 0); + if (remainingWeight < this.config.threshold) + throw new Error("Cannot remove guardian: would make recovery impossible"); + this.config.guardians.splice(index, 1); + this.config.guardianListHash = await this.hashGuardianList(); + this.config.updatedAt = Date.now(); + await this.saveConfig(); + } + async setThreshold(threshold) { + if (!this.config) + throw new Error("Recovery not initialized"); + const totalWeight = this.config.guardians.reduce((sum, g) => sum + g.weight, 0); + if (threshold > totalWeight) + throw new Error("Threshold cannot exceed total guardian weight"); + if (threshold < 1) + throw new Error("Threshold must be at least 1"); + this.config.threshold = threshold; + this.config.updatedAt = Date.now(); + await this.saveConfig(); + } + async setDelay(delaySeconds) { + if (!this.config) + throw new Error("Recovery not initialized"); + if (delaySeconds < 3600 || delaySeconds > 7 * 24 * 3600) + throw new Error("Delay must be between 1 hour and 7 days"); + this.config.delaySeconds = delaySeconds; + this.config.updatedAt = Date.now(); + await this.saveConfig(); + } + getConfig() { + return this.config; + } + isConfigured() { + if (!this.config) + return false; + return this.config.guardians.reduce((sum, g) => sum + g.weight, 0) >= this.config.threshold; + } + async verifyGuardian(guardianId) { + if (!this.config) + throw new Error("Recovery not initialized"); + const guardian = this.config.guardians.find((g) => g.id === guardianId); + if (!guardian) + throw new Error("Guardian not found"); + guardian.lastVerified = Date.now(); + await this.saveConfig(); + return true; + } + async initiateRecovery(newCredentialId) { + if (!this.config) + throw new Error("Recovery not configured"); + if (this.activeRequest?.status === "pending") + throw new Error("Recovery already in progress"); + const now = Date.now(); + this.activeRequest = { + id: bufferToBase64url(crypto.getRandomValues(new Uint8Array(16)).buffer), + accountDID: "", + newCredentialId, + initiatedAt: now, + completesAt: now + this.config.delaySeconds * 1000, + status: "pending", + approvals: [], + approvalWeight: 0 + }; + return this.activeRequest; + } + async approveRecovery(guardianId, signature) { + if (!this.activeRequest || this.activeRequest.status !== "pending") + throw new Error("No pending recovery request"); + if (!this.config) + throw new Error("Recovery not configured"); + const guardian = this.config.guardians.find((g) => g.id === guardianId); + if (!guardian) + throw new Error("Guardian not found"); + if (this.activeRequest.approvals.some((a) => a.guardianId === guardianId)) + throw new Error("Guardian already approved"); + this.activeRequest.approvals.push({ guardianId, approvedAt: Date.now(), signature }); + this.activeRequest.approvalWeight += guardian.weight; + if (this.activeRequest.approvalWeight >= this.config.threshold) { + this.activeRequest.status = "approved"; + } + return this.activeRequest; + } + async cancelRecovery() { + if (!this.activeRequest || this.activeRequest.status !== "pending") + throw new Error("No pending recovery request to cancel"); + this.activeRequest.status = "cancelled"; + this.activeRequest = null; + } + async completeRecovery() { + if (!this.activeRequest) + throw new Error("No recovery request"); + if (this.activeRequest.status !== "approved") + throw new Error("Recovery not approved"); + if (Date.now() < this.activeRequest.completesAt) { + const remaining = this.activeRequest.completesAt - Date.now(); + throw new Error(`Time-lock not expired. ${Math.ceil(remaining / 1000 / 60)} minutes remaining.`); + } + this.activeRequest.status = "completed"; + this.activeRequest = null; + } + getActiveRequest() { + return this.activeRequest; + } + async hashGuardianList() { + if (!this.config) + return ""; + const sortedIds = this.config.guardians.map((g) => g.id).sort().join(","); + const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(sortedIds)); + return bufferToBase64url(hash); + } + async saveConfig() { + if (!this.config) + return; + try { + localStorage.setItem("encryptid_recovery", JSON.stringify(this.config)); + } catch {} + } + loadConfig() { + try { + const stored = localStorage.getItem("encryptid_recovery"); + if (stored) + this.config = JSON.parse(stored); + } catch {} + } +} +var recoveryManagerInstance = null; +function getRecoveryManager() { + if (!recoveryManagerInstance) + recoveryManagerInstance = new RecoveryManager; + return recoveryManagerInstance; +} +function getGuardianTypeInfo(type) { + switch (type) { + case "secondary_passkey" /* SECONDARY_PASSKEY */: + return { name: "Backup Passkey", description: "Another device you own (phone, YubiKey, etc.)", icon: "key", setupInstructions: "Register a passkey on a second device you control." }; + case "trusted_contact" /* TRUSTED_CONTACT */: + return { name: "Trusted Contact", description: "A friend or family member with their own EncryptID", icon: "user", setupInstructions: "Ask a trusted person to create an EncryptID account." }; + case "hardware_key" /* HARDWARE_KEY */: + return { name: "Hardware Security Key", description: "A YubiKey or similar device stored offline", icon: "shield", setupInstructions: "Register a hardware security key and store it safely." }; + case "institutional" /* INSTITUTIONAL */: + return { name: "Recovery Service", description: "A professional recovery service provider", icon: "building", setupInstructions: "Connect with a trusted recovery service." }; + case "time_delayed_self" /* TIME_DELAYED_SELF */: + return { name: "Time-Delayed Self", description: "Recover yourself after a waiting period", icon: "clock", setupInstructions: "Set up a recovery option that requires waiting before completing." }; + default: + return { name: "Unknown", description: "Unknown guardian type", icon: "question", setupInstructions: "" }; + } +} + +// src/index.ts +var VERSION = "0.1.0"; +var SPEC_VERSION = "2026-02"; +export { + startConditionalUI, + signEthHash, + registerPasskey, + getSessionManager, + getRecoveryManager, + getKeyManager, + getGuardianTypeInfo, + detectCapabilities, + bufferToBase64url, + base64urlToBuffer, + authenticatePasskey, + VERSION, + SessionManager, + SPEC_VERSION, + RecoveryManager, + OPERATION_PERMISSIONS, + GuardianType, + EncryptIDKeyManager, + EncryptIDClient, + AuthLevel +}; diff --git a/frontend/vendor/@encryptid/sdk/package.json b/frontend/vendor/@encryptid/sdk/package.json new file mode 100644 index 0000000..9535c59 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/package.json @@ -0,0 +1,129 @@ +{ + "name": "@encryptid/sdk", + "version": "0.1.0", + "description": "Unified identity SDK for the r-ecosystem — WebAuthn passkeys, key derivation, session management, and social recovery", + "type": "module", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "import": "./index.js", + "types": "./index.d.ts" + }, + "./client": { + "import": "./client/index.js", + "types": "./client/index.d.ts" + }, + "./server": { + "import": "./server/index.js", + "types": "./server/index.d.ts" + }, + "./server/nextjs": { + "import": "./server/middleware/nextjs.js", + "types": "./server/middleware/nextjs.d.ts" + }, + "./server/hono": { + "import": "./server/middleware/hono.js", + "types": "./server/middleware/hono.d.ts" + }, + "./server/express": { + "import": "./server/middleware/express.js", + "types": "./server/middleware/express.d.ts" + }, + "./server/space-auth": { + "import": "./server/space-auth.js", + "types": "./server/space-auth.d.ts" + }, + "./server/ws-auth": { + "import": "./server/ws-auth.js", + "types": "./server/ws-auth.d.ts" + }, + "./ui": { + "import": "./ui/index.js", + "types": "./ui/index.d.ts" + }, + "./ui/react": { + "import": "./ui/react/index.js", + "types": "./ui/react/index.d.ts" + }, + "./types": { + "import": "./types/index.js", + "types": "./types/index.d.ts" + }, + "./types/roles": { + "import": "./types/roles.js", + "types": "./types/roles.d.ts" + }, + "./types/module-permissions": { + "import": "./types/module-permissions.js", + "types": "./types/module-permissions.d.ts" + }, + "./types/modules": { + "import": "./types/modules/index.js", + "types": "./types/modules/index.d.ts" + }, + "./server/role-resolver": { + "import": "./server/role-resolver.js", + "types": "./server/role-resolver.d.ts" + }, + "./types/membership-events": { + "import": "./types/membership-events.js", + "types": "./types/membership-events.d.ts" + }, + "./client/token-relay": { + "import": "./client/token-relay.js", + "types": "./client/token-relay.d.ts" + }, + "./browser": { + "import": "./encryptid.browser.js", + "default": "./encryptid.browser.js" + } + }, + "files": [ + "dist", + "src/python", + "src/browser.ts" + ], + "scripts": { + "build": "bun build ./src/index.ts --outdir ./dist --target browser && tsc --emitDeclarationOnly", + "build:browser": "bun build ./src/browser.ts --outfile ./encryptid.browser.js --target browser --minify", + "build:node": "bun build ./src/server/index.ts --outdir ./server --target node", + "build:all": "bun run build && bun run build:browser", + "typecheck": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", + "hono": "^4.11.0" + }, + "peerDependencies": { + "next": ">=14.0.0", + "react": ">=18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "next": { + "optional": true + } + }, + "devDependencies": { + "@types/react": "^19.0.0", + "typescript": "^5.7.0" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://gitea.jeffemmett.com/jeffemmett/encryptid-sdk" + }, + "keywords": [ + "webauthn", + "passkey", + "identity", + "encryption", + "self-sovereign", + "social-recovery" + ] +} diff --git a/frontend/vendor/@encryptid/sdk/server/index.d.ts b/frontend/vendor/@encryptid/sdk/server/index.d.ts new file mode 100644 index 0000000..1720b41 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/index.d.ts @@ -0,0 +1,10 @@ +/** + * @encryptid/sdk/server — Server-side module + */ +export { verifyEncryptIDToken, getAuthLevel, checkPermission, } from './jwt-verify.js'; +export type { VerifyOptions } from './jwt-verify.js'; +export { evaluateSpaceAccess, extractToken, SpaceVisibility, } from './space-auth.js'; +export type { SpaceAuthConfig, SpaceAuthResult, SpaceAuthOptions } from './space-auth.js'; +export { authenticateWSUpgrade } from './ws-auth.js'; +export { resolveSpaceRole, resolveSpaceRoleRemote, createRemoteMembershipLookup, invalidateRoleCache } from './role-resolver.js'; +export type { RoleResolverOptions, RemoteResolverOptions } from './role-resolver.js'; diff --git a/frontend/vendor/@encryptid/sdk/server/index.js b/frontend/vendor/@encryptid/sdk/server/index.js new file mode 100644 index 0000000..f787184 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/index.js @@ -0,0 +1,24 @@ +import { + evaluateSpaceAccess, + extractToken +} from "../index-j6kh1974.js"; +import { + SpaceVisibility +} from "../index-5c1t4ftn.js"; +import { + authenticateWSUpgrade +} from "../index-2yszamrn.js"; +import { + checkPermission, + getAuthLevel, + verifyEncryptIDToken +} from "../index-stg63j73.js"; +export { + verifyEncryptIDToken, + getAuthLevel, + extractToken, + evaluateSpaceAccess, + checkPermission, + authenticateWSUpgrade, + SpaceVisibility +}; diff --git a/frontend/vendor/@encryptid/sdk/server/jwt-verify.d.ts b/frontend/vendor/@encryptid/sdk/server/jwt-verify.d.ts new file mode 100644 index 0000000..ffe9365 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/jwt-verify.d.ts @@ -0,0 +1,35 @@ +/** + * EncryptID JWT Verification + * + * Server-side utilities for verifying EncryptID JWT tokens. + * Can verify locally with shared secret or by calling the EncryptID server. + */ +import type { EncryptIDClaims, OperationPermission } from '../types/index.js'; +export interface VerifyOptions { + /** JWT secret for local verification (HS256) */ + secret?: string; + /** EncryptID server URL for remote verification */ + serverUrl?: string; + /** Expected audience (your app's origin) */ + audience?: string; + /** Clock tolerance in seconds for expiration check */ + clockTolerance?: number; +} +/** + * Verify an EncryptID JWT token + * + * If `secret` is provided, verifies locally using HMAC-SHA256. + * Otherwise, calls the EncryptID server's /api/session/verify endpoint. + */ +export declare function verifyEncryptIDToken(token: string, options?: VerifyOptions): Promise; +/** + * Extract the auth level from claims + */ +export declare function getAuthLevel(claims: EncryptIDClaims): number; +/** + * Check if claims satisfy an operation permission + */ +export declare function checkPermission(claims: EncryptIDClaims, permission: OperationPermission): { + allowed: boolean; + reason?: string; +}; diff --git a/frontend/vendor/@encryptid/sdk/server/middleware/express.d.ts b/frontend/vendor/@encryptid/sdk/server/middleware/express.d.ts new file mode 100644 index 0000000..0e2c3b2 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/middleware/express.d.ts @@ -0,0 +1,74 @@ +/** + * EncryptID Express Middleware + * + * Authentication middleware for Express and compatible frameworks. + */ +import { type VerifyOptions } from '../jwt-verify.js'; +import { type SpaceAuthOptions } from '../space-auth.js'; +import type { EncryptIDClaims, SpaceAuthResult } from '../../types/index.js'; +declare global { + namespace Express { + interface Request { + encryptid?: EncryptIDClaims; + spaceAuth?: SpaceAuthResult; + } + } +} +interface ExpressRequest { + headers: Record; + method: string; + encryptid?: EncryptIDClaims; + spaceAuth?: SpaceAuthResult; +} +interface ExpressResponse { + status(code: number): ExpressResponse; + json(body: unknown): void; +} +type NextFunction = () => void; +/** + * Express middleware that verifies EncryptID JWT tokens + * + * Usage: + * ```ts + * import express from 'express'; + * import { encryptIDAuth } from '@encryptid/sdk/server/express'; + * + * const app = express(); + * + * app.use('/api', encryptIDAuth()); + * + * app.get('/api/profile', (req, res) => { + * res.json({ did: req.encryptid.did }); + * }); + * ``` + */ +export declare function encryptIDAuth(options?: VerifyOptions): (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => Promise; +/** + * Optional auth — sets session if token present, continues either way + */ +export declare function encryptIDOptional(options?: VerifyOptions): (req: ExpressRequest, _res: ExpressResponse, next: NextFunction) => Promise; +export interface EncryptIDSpaceAuthConfig extends SpaceAuthOptions { + /** Function to extract space slug from request (default: req.params.slug) */ + getSlug?: (req: ExpressRequest & { + params?: Record; + }) => string; +} +/** + * Express middleware for space-aware auth. + * + * Usage: + * ```ts + * app.use('/api/spaces/:slug', encryptIDSpaceAuth({ + * getSpaceConfig: async (slug) => db.getSpace(slug), + * })); + * + * app.get('/api/spaces/:slug', (req, res) => { + * const { readOnly, isOwner } = req.spaceAuth; + * res.json({ canEdit: !readOnly, isOwner }); + * }); + * ``` + */ +export declare function encryptIDSpaceAuth(config: EncryptIDSpaceAuthConfig): (req: ExpressRequest & { + params?: Record; +}, res: ExpressResponse, next: NextFunction) => Promise; +export {}; diff --git a/frontend/vendor/@encryptid/sdk/server/middleware/express.js b/frontend/vendor/@encryptid/sdk/server/middleware/express.js new file mode 100644 index 0000000..efcdb85 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/middleware/express.js @@ -0,0 +1,59 @@ +import { + evaluateSpaceAccess, + extractToken +} from "../../index-j6kh1974.js"; +import"../../index-5c1t4ftn.js"; +import { + verifyEncryptIDToken +} from "../../index-stg63j73.js"; + +// src/server/middleware/express.ts +function encryptIDAuth(options = {}) { + return async (req, res, next) => { + const authHeader = req.headers.authorization; + if (!authHeader || typeof authHeader !== "string" || !authHeader.startsWith("Bearer ")) { + res.status(401).json({ error: "Missing EncryptID token" }); + return; + } + const token = authHeader.slice(7); + try { + req.encryptid = await verifyEncryptIDToken(token, options); + next(); + } catch (err) { + res.status(401).json({ error: err.message || "Invalid token" }); + } + }; +} +function encryptIDOptional(options = {}) { + return async (req, _res, next) => { + const authHeader = req.headers.authorization; + if (authHeader && typeof authHeader === "string" && authHeader.startsWith("Bearer ")) { + try { + req.encryptid = await verifyEncryptIDToken(authHeader.slice(7), options); + } catch {} + } + next(); + }; +} +function encryptIDSpaceAuth(config) { + const { getSlug, ...options } = config; + return async (req, res, next) => { + const slug = getSlug ? getSlug(req) : req.params?.slug || ""; + const token = extractToken(req.headers); + const result = await evaluateSpaceAccess(slug, token, req.method, options); + if (!result.allowed) { + res.status(result.claims ? 403 : 401).json({ error: result.reason }); + return; + } + if (result.claims) { + req.encryptid = result.claims; + } + req.spaceAuth = result; + next(); + }; +} +export { + encryptIDSpaceAuth, + encryptIDOptional, + encryptIDAuth +}; diff --git a/frontend/vendor/@encryptid/sdk/server/middleware/hono.d.ts b/frontend/vendor/@encryptid/sdk/server/middleware/hono.d.ts new file mode 100644 index 0000000..b01aead --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/middleware/hono.d.ts @@ -0,0 +1,100 @@ +/** + * EncryptID Hono Middleware + * + * Authentication middleware for Hono web framework. + */ +import type { MiddlewareHandler } from 'hono'; +import { type VerifyOptions } from '../jwt-verify.js'; +import { type SpaceAuthOptions } from '../space-auth.js'; +import type { EncryptIDClaims, SpaceAuthResult } from '../../types/index.js'; +import type { ResolvedRole } from '../../types/roles.js'; +declare module 'hono' { + interface ContextVariableMap { + encryptid: EncryptIDClaims; + spaceAuth: SpaceAuthResult; + spaceRole: ResolvedRole; + } +} +/** + * Hono middleware that verifies EncryptID JWT tokens + * + * Usage: + * ```ts + * import { Hono } from 'hono'; + * import { encryptIDAuth } from '@encryptid/sdk/server/hono'; + * + * const app = new Hono(); + * + * // Protect all /api routes + * app.use('/api/*', encryptIDAuth()); + * + * app.get('/api/profile', (c) => { + * const session = c.get('encryptid'); + * return c.json({ did: session.did, sub: session.sub }); + * }); + * ``` + */ +export declare function encryptIDAuth(options?: VerifyOptions): MiddlewareHandler; +/** + * Optional auth — sets session if token present, continues either way + */ +export declare function encryptIDOptional(options?: VerifyOptions): MiddlewareHandler; +export interface EncryptIDSpaceAuthConfig extends SpaceAuthOptions { + /** Route param name for the space slug (default: 'slug') */ + slugParam?: string; + /** Query param fallback for the space slug (default: 'space') */ + slugQuery?: string; +} +/** + * Hono middleware for space-aware auth. + * + * Reads the space slug from route params or query, evaluates access + * based on visibility, and sets `c.var.spaceAuth` with the result. + * + * Usage: + * ```ts + * app.use('/api/communities/:slug/*', encryptIDSpaceAuth({ + * getSpaceConfig: async (slug) => db.getCommunity(slug), + * })); + * + * app.get('/api/communities/:slug', (c) => { + * const auth = c.get('spaceAuth'); + * if (auth.readOnly) { // public_read, unauthenticated + * return c.json({ ...community, canEdit: false }); + * } + * }); + * ``` + */ +export declare function encryptIDSpaceAuth(config: EncryptIDSpaceAuthConfig): MiddlewareHandler; +export interface EncryptIDSpaceRoleConfig extends EncryptIDSpaceAuthConfig { + /** Look up membership for a DID in a space. You provide the DB query. */ + getMembership: (userDID: string, spaceSlug: string) => Promise; + /** Resolve visibility for a space slug (if not in SpaceAuthConfig). Defaults to using getSpaceConfig. */ + getVisibility?: (spaceSlug: string) => Promise; +} +/** + * Combined space auth + role resolution middleware for Hono. + * + * Sets `c.var.spaceAuth`, `c.var.spaceRole`, and optionally `c.var.encryptid`. + * + * Usage: + * ```ts + * import { encryptIDSpaceRoleAuth } from '@encryptid/sdk/server/hono'; + * import { hasCapability } from '@encryptid/sdk'; + * import { RVOTE_PERMISSIONS } from '@encryptid/sdk/types/modules'; + * + * app.use('/api/spaces/:slug/*', encryptIDSpaceRoleAuth({ + * getSpaceConfig: async (slug) => db.getSpace(slug), + * getMembership: async (did, slug) => db.getMembership(did, slug), + * })); + * + * app.post('/api/spaces/:slug/proposals', (c) => { + * const { role } = c.get('spaceRole'); + * if (!hasCapability(role, 'create_proposal', RVOTE_PERMISSIONS)) { + * return c.json({ error: 'Insufficient permissions' }, 403); + * } + * // ... + * }); + * ``` + */ +export declare function encryptIDSpaceRoleAuth(config: EncryptIDSpaceRoleConfig): MiddlewareHandler; diff --git a/frontend/vendor/@encryptid/sdk/server/middleware/hono.js b/frontend/vendor/@encryptid/sdk/server/middleware/hono.js new file mode 100644 index 0000000..0c2e3b5 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/middleware/hono.js @@ -0,0 +1,59 @@ +import { + evaluateSpaceAccess, + extractToken +} from "../../index-j6kh1974.js"; +import"../../index-5c1t4ftn.js"; +import { + verifyEncryptIDToken +} from "../../index-stg63j73.js"; + +// src/server/middleware/hono.ts +function encryptIDAuth(options = {}) { + return async (c, next) => { + const authHeader = c.req.header("Authorization"); + if (!authHeader?.startsWith("Bearer ")) { + return c.json({ error: "Missing EncryptID token" }, 401); + } + const token = authHeader.slice(7); + try { + const claims = await verifyEncryptIDToken(token, options); + c.set("encryptid", claims); + await next(); + } catch (err) { + return c.json({ error: err.message || "Invalid token" }, 401); + } + }; +} +function encryptIDOptional(options = {}) { + return async (c, next) => { + const authHeader = c.req.header("Authorization"); + if (authHeader?.startsWith("Bearer ")) { + try { + const claims = await verifyEncryptIDToken(authHeader.slice(7), options); + c.set("encryptid", claims); + } catch {} + } + await next(); + }; +} +function encryptIDSpaceAuth(config) { + const { slugParam = "slug", slugQuery = "space", ...options } = config; + return async (c, next) => { + const spaceSlug = c.req.param(slugParam) || c.req.query(slugQuery) || ""; + const token = extractToken(c.req.raw.headers); + const result = await evaluateSpaceAccess(spaceSlug, token, c.req.method, options); + if (!result.allowed) { + return c.json({ error: result.reason }, result.claims ? 403 : 401); + } + if (result.claims) { + c.set("encryptid", result.claims); + } + c.set("spaceAuth", result); + await next(); + }; +} +export { + encryptIDSpaceAuth, + encryptIDOptional, + encryptIDAuth +}; diff --git a/frontend/vendor/@encryptid/sdk/server/middleware/nextjs.d.ts b/frontend/vendor/@encryptid/sdk/server/middleware/nextjs.d.ts new file mode 100644 index 0000000..bfb51e8 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/middleware/nextjs.d.ts @@ -0,0 +1,136 @@ +/** + * EncryptID Next.js Middleware + * + * Helpers for protecting Next.js App Router routes and API endpoints. + */ +import { type VerifyOptions } from '../jwt-verify.js'; +import { type SpaceAuthOptions } from '../space-auth.js'; +import type { EncryptIDClaims, SpaceAuthResult, SpaceVisibility } from '../../types/index.js'; +import type { ResolvedRole, SpaceMembership } from '../../types/roles.js'; +export interface EncryptIDNextConfig extends VerifyOptions { + /** Paths that don't require authentication */ + publicPaths?: string[]; + /** Redirect URL for unauthenticated requests (null = return 401) */ + loginUrl?: string | null; +} +/** + * Get EncryptID session from a Next.js request + * + * Usage in API routes: + * ```ts + * import { getEncryptIDSession } from '@encryptid/sdk/server/nextjs'; + * + * export async function GET(req: Request) { + * const session = await getEncryptIDSession(req); + * if (!session) return new Response('Unauthorized', { status: 401 }); + * // session.sub, session.did, session.eid.authLevel, etc. + * } + * ``` + */ +export declare function getEncryptIDSession(request: Request, options?: VerifyOptions): Promise; +/** + * Protect a Next.js API route handler + * + * Usage: + * ```ts + * import { withEncryptID } from '@encryptid/sdk/server/nextjs'; + * + * export const GET = withEncryptID(async (req, session) => { + * return Response.json({ user: session.sub, did: session.did }); + * }); + * ``` + */ +export declare function withEncryptID(handler: (request: Request, session: EncryptIDClaims) => Promise, options?: VerifyOptions): (request: Request) => Promise; +/** + * Create Next.js middleware for EncryptID + * + * Usage in middleware.ts: + * ```ts + * import { createEncryptIDMiddleware } from '@encryptid/sdk/server/nextjs'; + * + * const encryptIDMiddleware = createEncryptIDMiddleware({ + * publicPaths: ['/auth/signin', '/api/auth'], + * loginUrl: '/auth/signin', + * }); + * + * export function middleware(request: NextRequest) { + * return encryptIDMiddleware(request); + * } + * ``` + */ +export declare function createEncryptIDMiddleware(config?: EncryptIDNextConfig): (request: Request) => Promise; +/** + * Check space access in a Next.js API route or server component. + * + * Usage: + * ```ts + * const result = await checkSpaceAccess(request, spaceSlug, { + * getSpaceConfig: async (slug) => { + * const space = await prisma.space.findUnique({ where: { slug } }); + * if (!space) return null; + * return { spaceSlug: slug, visibility: space.visibility, app: 'rvote' }; + * }, + * }); + * if (!result.allowed) return new Response(result.reason, { status: 401 }); + * ``` + */ +export declare function checkSpaceAccess(request: Request, spaceSlug: string, options: SpaceAuthOptions): Promise; +/** + * HOC that wraps a Next.js API route handler with space auth. + * + * Usage: + * ```ts + * export const POST = withSpaceAuth( + * async (req, spaceAuth, slug) => { + * return Response.json({ owner: spaceAuth.isOwner }); + * }, + * (req) => new URL(req.url).pathname.split('/')[2], + * { getSpaceConfig: async (slug) => { ... } }, + * ); + * ``` + */ +export declare function withSpaceAuth(handler: (request: Request, spaceAuth: SpaceAuthResult, spaceSlug: string) => Promise, getSlug: (request: Request) => string, options: SpaceAuthOptions): (request: Request) => Promise; +export interface SpaceRoleOptions extends SpaceAuthOptions { + /** Look up membership for a DID in a space. You provide the DB query. */ + getMembership: (userDID: string, spaceSlug: string) => Promise; + /** Resolve visibility for a space slug. If not provided, uses getSpaceConfig. */ + getVisibility?: (spaceSlug: string) => Promise; +} +export interface SpaceRoleResult { + spaceAuth: SpaceAuthResult; + resolvedRole: ResolvedRole; +} +/** + * Check space access AND resolve role in one call. + * + * Usage: + * ```ts + * const result = await checkSpaceRole(request, slug, { + * getSpaceConfig: async (slug) => prisma.space.findUnique({ where: { slug } }), + * getMembership: async (did, slug) => prisma.spaceMember.findUnique({ + * where: { userDID_spaceSlug: { userDID: did, spaceSlug: slug } } + * }), + * }); + * if (!result.spaceAuth.allowed) return deny(); + * if (hasCapability(result.resolvedRole.role, 'create_proposal', RVOTE_PERMISSIONS)) { ... } + * ``` + */ +export declare function checkSpaceRole(request: Request, spaceSlug: string, options: SpaceRoleOptions): Promise; +/** + * HOC that wraps a Next.js API route handler with space auth + role resolution. + * + * Usage: + * ```ts + * export const POST = withSpaceRole( + * async (req, spaceAuth, role, slug) => { + * if (!hasCapability(role.role, 'create_proposal', RVOTE_PERMISSIONS)) { + * return Response.json({ error: 'Forbidden' }, { status: 403 }); + * } + * return Response.json({ created: true }); + * }, + * (req) => new URL(req.url).pathname.split('/')[3], // extract slug + * { getSpaceConfig: ..., getMembership: ... }, + * ); + * ``` + */ +export declare function withSpaceRole(handler: (request: Request, spaceAuth: SpaceAuthResult, resolvedRole: ResolvedRole, spaceSlug: string) => Promise, getSlug: (request: Request) => string, options: SpaceRoleOptions): (request: Request) => Promise; diff --git a/frontend/vendor/@encryptid/sdk/server/middleware/nextjs.js b/frontend/vendor/@encryptid/sdk/server/middleware/nextjs.js new file mode 100644 index 0000000..72b2b9c --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/middleware/nextjs.js @@ -0,0 +1,83 @@ +import { + evaluateSpaceAccess, + extractToken +} from "../../index-j6kh1974.js"; +import"../../index-5c1t4ftn.js"; +import { + verifyEncryptIDToken +} from "../../index-stg63j73.js"; + +// src/server/middleware/nextjs.ts +async function getEncryptIDSession(request, options = {}) { + const authHeader = request.headers.get("Authorization"); + if (authHeader?.startsWith("Bearer ")) { + try { + return await verifyEncryptIDToken(authHeader.slice(7), options); + } catch { + return null; + } + } + const cookieHeader = request.headers.get("Cookie") || ""; + const tokenMatch = cookieHeader.match(/encryptid_token=([^;]+)/); + if (tokenMatch) { + try { + return await verifyEncryptIDToken(tokenMatch[1], options); + } catch { + return null; + } + } + return null; +} +function withEncryptID(handler, options = {}) { + return async (request) => { + const session = await getEncryptIDSession(request, options); + if (!session) { + return new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + headers: { "Content-Type": "application/json" } + }); + } + return handler(request, session); + }; +} +function createEncryptIDMiddleware(config = {}) { + const { publicPaths = [], loginUrl = null, ...verifyOptions } = config; + return async (request) => { + const url = new URL(request.url); + if (publicPaths.some((p) => url.pathname.startsWith(p))) { + return null; + } + const session = await getEncryptIDSession(request, verifyOptions); + if (!session) { + if (loginUrl) { + return Response.redirect(new URL(loginUrl, request.url)); + } + return new Response("Unauthorized", { status: 401 }); + } + return null; + }; +} +async function checkSpaceAccess(request, spaceSlug, options) { + const token = extractToken(request.headers); + return evaluateSpaceAccess(spaceSlug, token, request.method, options); +} +function withSpaceAuth(handler, getSlug, options) { + return async (request) => { + const slug = getSlug(request); + const result = await checkSpaceAccess(request, slug, options); + if (!result.allowed) { + return new Response(JSON.stringify({ error: result.reason }), { + status: result.claims ? 403 : 401, + headers: { "Content-Type": "application/json" } + }); + } + return handler(request, result, slug); + }; +} +export { + withSpaceAuth, + withEncryptID, + getEncryptIDSession, + createEncryptIDMiddleware, + checkSpaceAccess +}; diff --git a/frontend/vendor/@encryptid/sdk/server/role-resolver.d.ts b/frontend/vendor/@encryptid/sdk/server/role-resolver.d.ts new file mode 100644 index 0000000..dce650e --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/role-resolver.d.ts @@ -0,0 +1,94 @@ +/** + * EncryptID Space Role Resolver + * + * Resolves a user's effective SpaceRole given their space access result + * and a membership lookup function. This is the bridge between + * evaluateSpaceAccess() (layer 1: "can they enter?") and + * hasCapability() (layer 3: "what can they do?"). + */ +import type { ResolvedRole, SpaceMembership } from '../types/roles.js'; +import type { SpaceAuthResult } from '../types/index.js'; +import { SpaceVisibility } from '../types/index.js'; +export interface RoleResolverOptions { + /** + * Look up membership for a DID in a space. + * You provide the DB query — works with Prisma, Automerge, raw SQL, etc. + * Return null if no membership found. + */ + getMembership: (userDID: string, spaceSlug: string) => Promise; + /** The space's visibility setting */ + visibility: SpaceVisibility; +} +export interface RemoteResolverOptions { + /** EncryptID server URL (e.g., https://encryptid.jeffemmett.com) */ + serverUrl: string; + /** The space's visibility setting */ + visibility: SpaceVisibility; + /** Cache TTL in milliseconds (default: 5 minutes) */ + cacheTtlMs?: number; +} +/** + * Invalidate the role cache for a specific user/space or the entire cache. + */ +export declare function invalidateRoleCache(userDID?: string, spaceSlug?: string): void; +/** + * Create a getMembership function that calls the EncryptID server. + * This is a convenience wrapper for modules that don't have local membership data. + * + * @example + * ```ts + * const role = await resolveSpaceRole(spaceAuth, slug, { + * visibility: 'public', + * getMembership: createRemoteMembershipLookup('https://encryptid.jeffemmett.com'), + * }); + * ``` + */ +export declare function createRemoteMembershipLookup(serverUrl: string): (userDID: string, spaceSlug: string) => Promise; +/** + * Resolve a user's effective SpaceRole in a space. + * + * Decision flow: + * 1. Owner → ADMIN (always) + * 2. Has explicit membership → membership.role + * 3. No membership, apply defaults based on visibility: + * - PUBLIC: anonymous & authenticated → PARTICIPANT + * - PUBLIC_READ: anonymous → VIEWER, authenticated → PARTICIPANT + * - AUTHENTICATED: → VIEWER (must have membership for more) + * - MEMBERS_ONLY: should not reach here (denied at space access layer) + * + * @param spaceAuth - Result from evaluateSpaceAccess() + * @param spaceSlug - The space identifier + * @param options - Membership lookup and visibility config + * + * @example + * ```ts + * const spaceAuth = await evaluateSpaceAccess(slug, token, method, opts); + * if (!spaceAuth.allowed) return deny(); + * + * const { role, source } = await resolveSpaceRole(spaceAuth, slug, { + * visibility: space.visibility, + * getMembership: (did, slug) => db.membership.findUnique({ where: { did_slug: { did, slug } } }), + * }); + * + * if (hasCapability(role, 'create_proposal', RVOTE_PERMISSIONS)) { ... } + * ``` + */ +export declare function resolveSpaceRole(spaceAuth: SpaceAuthResult, spaceSlug: string, options: RoleResolverOptions): Promise; +/** + * Resolve a user's SpaceRole by querying the EncryptID server. + * + * This is the recommended function for modules that don't maintain + * their own membership table. It: + * 1. Checks the in-memory cache (5-min TTL) + * 2. If miss, queries EncryptID server for membership + * 3. Falls back to visibility-based defaults on network error + * + * @example + * ```ts + * const { role, source } = await resolveSpaceRoleRemote(spaceAuth, slug, { + * serverUrl: 'https://encryptid.jeffemmett.com', + * visibility: space.visibility, + * }); + * ``` + */ +export declare function resolveSpaceRoleRemote(spaceAuth: SpaceAuthResult, spaceSlug: string, options: RemoteResolverOptions): Promise; diff --git a/frontend/vendor/@encryptid/sdk/server/space-auth.d.ts b/frontend/vendor/@encryptid/sdk/server/space-auth.d.ts new file mode 100644 index 0000000..35fc039 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/space-auth.d.ts @@ -0,0 +1,35 @@ +/** + * EncryptID Space Auth Guard + * + * Framework-agnostic space-aware authentication. + * Evaluates whether a request should be allowed based on: + * 1. Space visibility configuration + * 2. Request method (GET/HEAD/OPTIONS = read, others = write) + * 3. EncryptID session (if present) + */ +import { type VerifyOptions } from './jwt-verify.js'; +import { SpaceVisibility } from '../types/index.js'; +import type { SpaceAuthConfig, SpaceAuthResult } from '../types/index.js'; +export { SpaceVisibility }; +export type { SpaceAuthConfig, SpaceAuthResult }; +export interface SpaceAuthOptions extends VerifyOptions { + /** Resolve a space slug to its auth config. You provide the DB/store query. */ + getSpaceConfig: (spaceSlug: string) => Promise; +} +/** + * Core space auth evaluation — framework-agnostic. + * + * Apps call this with the space slug, the extracted token (or null), + * the HTTP method, and a callback to look up the space's config. + */ +export declare function evaluateSpaceAccess(spaceSlug: string, token: string | null, method: string, options: SpaceAuthOptions): Promise; +/** + * Extract EncryptID token from request headers or cookies. + * Works with both the standard Headers API (fetch/Hono/Next.js) and + * Express-style header objects. + */ +export declare function extractToken(headers: { + get?: (name: string) => string | null | undefined; + authorization?: string; + cookie?: string; +}): string | null; diff --git a/frontend/vendor/@encryptid/sdk/server/space-auth.js b/frontend/vendor/@encryptid/sdk/server/space-auth.js new file mode 100644 index 0000000..876de6d --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/space-auth.js @@ -0,0 +1,13 @@ +import { + evaluateSpaceAccess, + extractToken +} from "../index-j6kh1974.js"; +import { + SpaceVisibility +} from "../index-5c1t4ftn.js"; +import"../index-stg63j73.js"; +export { + extractToken, + evaluateSpaceAccess, + SpaceVisibility +}; diff --git a/frontend/vendor/@encryptid/sdk/server/ws-auth.d.ts b/frontend/vendor/@encryptid/sdk/server/ws-auth.d.ts new file mode 100644 index 0000000..8d4f775 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/ws-auth.d.ts @@ -0,0 +1,18 @@ +/** + * EncryptID WebSocket Authentication + * + * Since WebSocket upgrade requests carry the initial HTTP headers, + * we verify the token during the upgrade handshake. + * + * Supported token locations (checked in order): + * 1. `token` query parameter: ws://host/ws?token=xxx + * 2. Sec-WebSocket-Protocol subprotocol: "encryptid.TOKEN_HERE" + * 3. Cookie: encryptid_token=xxx + */ +import { type VerifyOptions } from './jwt-verify.js'; +import type { EncryptIDClaims } from '../types/index.js'; +/** + * Authenticate a WebSocket upgrade request. + * Returns claims if a valid token is found, null otherwise. + */ +export declare function authenticateWSUpgrade(request: Request, options?: VerifyOptions): Promise; diff --git a/frontend/vendor/@encryptid/sdk/server/ws-auth.js b/frontend/vendor/@encryptid/sdk/server/ws-auth.js new file mode 100644 index 0000000..74a2840 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/server/ws-auth.js @@ -0,0 +1,7 @@ +import { + authenticateWSUpgrade +} from "../index-2yszamrn.js"; +import"../index-stg63j73.js"; +export { + authenticateWSUpgrade +}; diff --git a/frontend/vendor/@encryptid/sdk/types/index.d.ts b/frontend/vendor/@encryptid/sdk/types/index.d.ts new file mode 100644 index 0000000..2bae4de --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/index.d.ts @@ -0,0 +1,246 @@ +/** + * EncryptID SDK — Shared Types + */ +export interface EncryptIDCredential { + credentialId: string; + publicKey: ArrayBuffer; + userId: string; + username: string; + createdAt: number; + prfSupported: boolean; + transports?: AuthenticatorTransport[]; +} +export interface AuthenticationResult { + credentialId: string; + userId: string; + prfOutput?: ArrayBuffer; + signature: ArrayBuffer; + authenticatorData: ArrayBuffer; +} +export interface EncryptIDConfig { + rpId: string; + rpName: string; + origin: string; + userVerification: UserVerificationRequirement; + timeout: number; +} +export interface WebAuthnCapabilities { + webauthn: boolean; + platformAuthenticator: boolean; + conditionalUI: boolean; + prfExtension: boolean; +} +export interface DerivedKeys { + encryptionKey: CryptoKey; + signingKeyPair: CryptoKeyPair; + didSeed: Uint8Array; + did: string; + fromPRF: boolean; + /** Ethereum-compatible secp256k1 wallet derived from the same master key */ + ethereum?: { + address: string; + publicKey: Uint8Array; + privateKey: Uint8Array; + }; +} +export interface EncryptedData { + ciphertext: ArrayBuffer; + iv: Uint8Array; + tag?: ArrayBuffer; +} +export interface SignedData { + data: ArrayBuffer; + signature: ArrayBuffer; + publicKey: ArrayBuffer; +} +export declare enum AuthLevel { + BASIC = 1, + STANDARD = 2, + ELEVATED = 3, + CRITICAL = 4 +} +export interface EncryptIDClaims { + iss: string; + sub: string; + aud: string[]; + iat: number; + exp: number; + jti: string; + username: string; + did?: string; + eid: { + walletAddress?: string; + credentialId?: string; + authLevel: AuthLevel; + authTime: number; + capabilities: { + encrypt: boolean; + sign: boolean; + wallet: boolean; + }; + recoveryConfigured: boolean; + }; +} +export interface SessionState { + accessToken: string; + refreshToken: string; + claims: EncryptIDClaims; + lastAuthTime: number; +} +export interface OperationPermission { + minAuthLevel: AuthLevel; + requiresCapability?: 'encrypt' | 'sign' | 'wallet'; + maxAgeSeconds?: number; +} +export declare enum GuardianType { + SECONDARY_PASSKEY = "secondary_passkey", + TRUSTED_CONTACT = "trusted_contact", + HARDWARE_KEY = "hardware_key", + INSTITUTIONAL = "institutional", + TIME_DELAYED_SELF = "time_delayed_self" +} +export interface Guardian { + id: string; + type: GuardianType; + name: string; + weight: number; + credentialId?: string; + contactDID?: string; + contactEmail?: string; + serviceUrl?: string; + delaySeconds?: number; + addedAt: number; + lastVerified?: number; +} +export interface RecoveryConfig { + threshold: number; + delaySeconds: number; + guardians: Guardian[]; + guardianListHash: string; + updatedAt: number; +} +export interface RecoveryRequest { + id: string; + accountDID: string; + newCredentialId: string; + initiatedAt: number; + completesAt: number; + status: 'pending' | 'approved' | 'cancelled' | 'completed'; + approvals: { + guardianId: string; + approvedAt: number; + signature: string; + }[]; + approvalWeight: number; +} +export interface RegistrationStartResponse { + options: { + challenge: string; + rp: { + id: string; + name: string; + }; + user: { + id: string; + name: string; + displayName: string; + }; + pubKeyCredParams: { + alg: number; + type: string; + }[]; + authenticatorSelection: Record; + timeout: number; + attestation: string; + extensions?: Record; + }; + userId: string; +} +export interface RegistrationCompleteResponse { + success: boolean; + userId: string; + token: string; + did: string; +} +export interface AuthStartResponse { + options: { + challenge: string; + rpId: string; + userVerification: string; + timeout: number; + allowCredentials?: { + type: string; + id: string; + transports?: string[]; + }[]; + }; +} +export interface AuthCompleteResponse { + success: boolean; + userId: string; + username: string; + token: string; + did: string; +} +export interface SessionVerifyResponse { + valid: boolean; + userId?: string; + username?: string; + did?: string; + exp?: number; + error?: string; +} +export interface EmailRecoverySetResponse { + success: boolean; + email: string; +} +export interface EmailRecoveryRequestResponse { + success: boolean; + message: string; +} +export interface EmailRecoveryVerifyResponse { + success: boolean; + token: string; + userId: string; + username: string; + did: string; + message: string; +} +export declare enum SpaceVisibility { + /** Anyone can view and interact, no auth required */ + PUBLIC = "public", + /** Anyone can view, auth required for write/interact */ + PUBLIC_READ = "public_read", + /** Auth required for any access */ + AUTHENTICATED = "authenticated", + /** Only space members can access (app must check membership separately) */ + MEMBERS_ONLY = "members_only" +} +export type AppName = 'rspace' | 'rvote' | 'rfiles' | 'rmaps' | 'rwallet' | 'rfunds' | 'rnotes' | 'rtrips' | 'rnetwork' | 'rcart' | 'rmail' | 'rcal' | 'rtube' | 'rstack' | 'canvas'; +export { SpaceRole, SPACE_ROLE_LEVEL, roleAtLeast } from './roles.js'; +export type { SpaceMembership, ResolvedRole } from './roles.js'; +export { hasCapability, getCapabilities } from './module-permissions.js'; +export type { ModulePermissionMap } from './module-permissions.js'; +export type { SpaceMembershipEvent, SpaceMembershipEventType, AddMemberRequest, UpdateMemberRequest, MemberResponse, MemberListResponse, } from './membership-events.js'; +export interface SpaceAuthConfig { + /** Space identifier (slug) */ + spaceSlug: string; + /** Who can see/interact with this space */ + visibility: SpaceVisibility; + /** DID of the space creator/owner */ + ownerDID?: string; + /** App this space belongs to */ + app: AppName; +} +export interface SpaceAuthResult { + /** Whether access is allowed */ + allowed: boolean; + /** The authenticated user's claims (null if unauthenticated public access) */ + claims: EncryptIDClaims | null; + /** Why access was denied */ + reason?: string; + /** Whether the user is the space owner */ + isOwner: boolean; + /** Whether this is read-only access (public_read with no auth) */ + readOnly: boolean; +} diff --git a/frontend/vendor/@encryptid/sdk/types/index.js b/frontend/vendor/@encryptid/sdk/types/index.js new file mode 100644 index 0000000..0845545 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/index.js @@ -0,0 +1,10 @@ +import { + AuthLevel, + GuardianType, + SpaceVisibility +} from "../index-5c1t4ftn.js"; +export { + SpaceVisibility, + GuardianType, + AuthLevel +}; diff --git a/frontend/vendor/@encryptid/sdk/types/membership-events.d.ts b/frontend/vendor/@encryptid/sdk/types/membership-events.d.ts new file mode 100644 index 0000000..01fa624 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/membership-events.d.ts @@ -0,0 +1,49 @@ +/** + * EncryptID SDK — Space Membership Event Types + * + * Events emitted when membership changes in a space. + * Used by the membership sync system to keep all r*.online + * modules consistent when a user's role changes. + */ +import type { SpaceRole } from './roles.js'; +export type SpaceMembershipEventType = 'member.joined' | 'member.left' | 'member.role_changed'; +/** + * Emitted by EncryptID server when space membership changes. + * Modules can subscribe to these events to invalidate role caches. + */ +export interface SpaceMembershipEvent { + type: SpaceMembershipEventType; + /** Space identifier */ + spaceSlug: string; + /** DID of the user whose membership changed */ + userDID: string; + /** New role (undefined for member.left) */ + role?: SpaceRole; + /** Previous role (undefined for member.joined) */ + previousRole?: SpaceRole; + /** DID of user who initiated the change */ + changedBy?: string; + /** Unix timestamp (ms) */ + timestamp: number; +} +export interface AddMemberRequest { + /** DID of user to add */ + userDID: string; + /** Role to grant */ + role: SpaceRole; +} +export interface UpdateMemberRequest { + /** New role */ + role: SpaceRole; +} +export interface MemberResponse { + userDID: string; + spaceSlug: string; + role: SpaceRole; + joinedAt: number; + grantedBy?: string; +} +export interface MemberListResponse { + members: MemberResponse[]; + total: number; +} diff --git a/frontend/vendor/@encryptid/sdk/types/module-permissions.d.ts b/frontend/vendor/@encryptid/sdk/types/module-permissions.d.ts new file mode 100644 index 0000000..a715ed7 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/module-permissions.d.ts @@ -0,0 +1,53 @@ +/** + * EncryptID SDK — Module Permission Maps + * + * Each r*.online module declares a static mapping from capabilities + * to the minimum SpaceRole required. This is the central abstraction + * for permission inheritance across the ecosystem. + */ +import { SpaceRole } from './roles.js'; +import type { AppName } from './index.js'; +/** + * A module's permission declaration. + * Maps each capability string to the minimum SpaceRole required. + * + * @template TCapability - Union of capability string literals for this module + */ +export interface ModulePermissionMap { + /** Module identifier (matches AppName) */ + module: AppName; + /** Human-readable module name */ + displayName: string; + /** + * For each capability, the minimum SpaceRole required. + * If a capability is not listed, it requires ADMIN by default. + */ + capabilities: Record; +} +/** + * Check if a user's SpaceRole satisfies a module capability requirement. + * + * @param userRole - The user's resolved SpaceRole in the space + * @param capability - The capability to check + * @param permMap - The module's permission map + * @returns true if the user's role meets or exceeds the minimum for this capability + * + * @example + * ```ts + * import { hasCapability } from '@encryptid/sdk'; + * import { RVOTE_PERMISSIONS } from '@encryptid/sdk/types/modules'; + * + * if (hasCapability(userRole, 'create_proposal', RVOTE_PERMISSIONS)) { + * // user can create proposals + * } + * ``` + */ +export declare function hasCapability(userRole: SpaceRole, capability: T, permMap: ModulePermissionMap): boolean; +/** + * Get all capabilities a role has access to in a module. + * + * @param userRole - The user's resolved SpaceRole + * @param permMap - The module's permission map + * @returns Array of capability strings the user has access to + */ +export declare function getCapabilities(userRole: SpaceRole, permMap: ModulePermissionMap): T[]; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/index.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/index.d.ts new file mode 100644 index 0000000..8d16834 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/index.d.ts @@ -0,0 +1,15 @@ +/** + * Module Permission Maps — Barrel Export + * + * Import specific module permissions: + * import { RVOTE_PERMISSIONS } from '@encryptid/sdk/types/modules'; + * + * Or import all: + * import * as modules from '@encryptid/sdk/types/modules'; + */ +export { RSPACE_PERMISSIONS, type RSpaceCapability } from './rspace.js'; +export { RVOTE_PERMISSIONS, type RVoteCapability } from './rvote.js'; +export { RNOTES_PERMISSIONS, type RNotesCapability } from './rnotes.js'; +export { RFUNDS_PERMISSIONS, type RFundsCapability } from './rfunds.js'; +export { RMAPS_PERMISSIONS, type RMapsCapability } from './rmaps.js'; +export { RTUBE_PERMISSIONS, type RTubeCapability } from './rtube.js'; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/rfunds.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/rfunds.d.ts new file mode 100644 index 0000000..f572548 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/rfunds.d.ts @@ -0,0 +1,8 @@ +/** + * rFunds — Funding Flows & Treasury + * + * Permission capabilities for the rFunds funding/treasury module. + */ +import type { ModulePermissionMap } from '../module-permissions.js'; +export type RFundsCapability = 'view_flows' | 'create_flow' | 'contribute_funds' | 'moderate_flows' | 'configure_treasury'; +export declare const RFUNDS_PERMISSIONS: ModulePermissionMap; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/rmaps.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/rmaps.d.ts new file mode 100644 index 0000000..02ce498 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/rmaps.d.ts @@ -0,0 +1,8 @@ +/** + * rMaps — Spatial Intelligence + * + * Permission capabilities for the rMaps collaborative mapping module. + */ +import type { ModulePermissionMap } from '../module-permissions.js'; +export type RMapsCapability = 'view_map' | 'add_markers' | 'share_location' | 'moderate_markers' | 'configure_map'; +export declare const RMAPS_PERMISSIONS: ModulePermissionMap; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/rnotes.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/rnotes.d.ts new file mode 100644 index 0000000..e1d7872 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/rnotes.d.ts @@ -0,0 +1,10 @@ +/** + * rNotes — Collaborative Notebooks + * + * Permission capabilities for the rNotes note-taking module. + * Note: rNotes also has per-notebook CollaboratorRole overrides. + * Space-level role sets the default; notebook-level can narrow or widen. + */ +import type { ModulePermissionMap } from '../module-permissions.js'; +export type RNotesCapability = 'view_notebooks' | 'create_notebook' | 'edit_own_notes' | 'edit_any_notes' | 'manage_notebooks'; +export declare const RNOTES_PERMISSIONS: ModulePermissionMap; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/rspace.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/rspace.d.ts new file mode 100644 index 0000000..4d2f9a1 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/rspace.d.ts @@ -0,0 +1,8 @@ +/** + * rSpace — Canvas/Collaboration Platform + * + * Permission capabilities for the rSpace collaborative canvas. + */ +import type { ModulePermissionMap } from '../module-permissions.js'; +export type RSpaceCapability = 'view_canvas' | 'add_shapes' | 'edit_own_shapes' | 'edit_any_shape' | 'delete_any_shape' | 'configure_space'; +export declare const RSPACE_PERMISSIONS: ModulePermissionMap; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/rtube.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/rtube.d.ts new file mode 100644 index 0000000..298ef19 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/rtube.d.ts @@ -0,0 +1,8 @@ +/** + * rTube — Video Hosting & Streaming + * + * Permission capabilities for the rTube video module. + */ +import type { ModulePermissionMap } from '../module-permissions.js'; +export type RTubeCapability = 'view_videos' | 'upload_video' | 'start_stream' | 'moderate_videos' | 'configure_channel'; +export declare const RTUBE_PERMISSIONS: ModulePermissionMap; diff --git a/frontend/vendor/@encryptid/sdk/types/modules/rvote.d.ts b/frontend/vendor/@encryptid/sdk/types/modules/rvote.d.ts new file mode 100644 index 0000000..834f907 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/modules/rvote.d.ts @@ -0,0 +1,8 @@ +/** + * rVote — Decision Engine + * + * Permission capabilities for the rVote voting/governance module. + */ +import type { ModulePermissionMap } from '../module-permissions.js'; +export type RVoteCapability = 'view_proposals' | 'create_proposal' | 'cast_vote' | 'moderate_proposals' | 'configure_voting'; +export declare const RVOTE_PERMISSIONS: ModulePermissionMap; diff --git a/frontend/vendor/@encryptid/sdk/types/roles.d.ts b/frontend/vendor/@encryptid/sdk/types/roles.d.ts new file mode 100644 index 0000000..658fc14 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/types/roles.d.ts @@ -0,0 +1,57 @@ +/** + * EncryptID SDK — Space Role System + * + * Defines the unified role hierarchy for the r*.online ecosystem. + * Spaces have visibility (who can enter) and roles (what you can do). + */ +/** + * Ecosystem-wide role within a space. + * These are the ONLY roles stored at the space level. + * Modules interpret these into module-specific capabilities. + */ +export declare enum SpaceRole { + /** Can view public content in the space */ + VIEWER = "viewer", + /** Can participate: create, edit own content */ + PARTICIPANT = "participant", + /** Can moderate: edit/delete others' content, manage participants */ + MODERATOR = "moderator", + /** Full control: configure space, manage roles, delete space */ + ADMIN = "admin" +} +/** + * Ordered precedence (higher number = more powerful). + * Used by hasCapability() and role comparison helpers. + */ +export declare const SPACE_ROLE_LEVEL: Record; +/** + * Check if a role meets or exceeds a required minimum role. + */ +export declare function roleAtLeast(userRole: SpaceRole, requiredRole: SpaceRole): boolean; +/** + * Space membership record. + * Each app stores memberships in its own DB (Prisma, Automerge, etc.) + * but the shape is consistent across the ecosystem. + */ +export interface SpaceMembership { + /** User's DID (from EncryptID claims.sub) */ + userDID: string; + /** Space identifier (slug) */ + spaceSlug: string; + /** Role in this space */ + role: SpaceRole; + /** When the membership was granted (epoch ms) */ + joinedAt: number; + /** DID of user who granted this membership (null = self-join or owner) */ + grantedBy?: string; +} +/** + * Result of resolving a user's effective role in a space. + * Includes the source for debugging and audit. + */ +export interface ResolvedRole { + /** The effective role */ + role: SpaceRole; + /** How the role was determined */ + source: 'membership' | 'owner' | 'default' | 'anonymous'; +} diff --git a/frontend/vendor/@encryptid/sdk/ui/guardian-setup.d.ts b/frontend/vendor/@encryptid/sdk/ui/guardian-setup.d.ts new file mode 100644 index 0000000..fe7b00d --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/guardian-setup.d.ts @@ -0,0 +1,12 @@ +/** + * EncryptID Guardian Setup Web Component + * + * Custom element: + * Events: guardian-added, guardian-removed, guardian-verified + */ +export declare class GuardianSetupElement extends HTMLElement { + private shadow; + constructor(); + connectedCallback(): void; + private render; +} diff --git a/frontend/vendor/@encryptid/sdk/ui/index.d.ts b/frontend/vendor/@encryptid/sdk/ui/index.d.ts new file mode 100644 index 0000000..3cd84b1 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/index.d.ts @@ -0,0 +1,5 @@ +/** + * @encryptid/sdk/ui — Web Components + */ +export { EncryptIDLoginButton } from './login-button.js'; +export { GuardianSetupElement } from './guardian-setup.js'; diff --git a/frontend/vendor/@encryptid/sdk/ui/index.js b/frontend/vendor/@encryptid/sdk/ui/index.js new file mode 100644 index 0000000..860b81e --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/index.js @@ -0,0 +1,263 @@ +import { + getGuardianTypeInfo, + getKeyManager, + getRecoveryManager, + getSessionManager +} from "../index-24r9wkfe.js"; +import { + authenticatePasskey, + detectCapabilities, + registerPasskey, + startConditionalUI +} from "../index-2cp5044h.js"; +import { + AuthLevel +} from "../index-5c1t4ftn.js"; + +// src/ui/login-button.ts +var PASSKEY_ICON = ``; +var styles = ` +:host { --eid-primary: #06b6d4; --eid-primary-hover: #0891b2; --eid-bg: #0f172a; --eid-bg-hover: #1e293b; --eid-text: #f1f5f9; --eid-text-secondary: #94a3b8; --eid-radius: 8px; display: inline-block; font-family: system-ui, -apple-system, sans-serif; } +.login-btn { display: flex; align-items: center; gap: 12px; padding: 12px 24px; background: var(--eid-primary); color: white; border: none; border-radius: var(--eid-radius); font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3); } +.login-btn:hover { background: var(--eid-primary-hover); transform: translateY(-1px); } +.login-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } +.login-btn.outline { background: transparent; border: 2px solid var(--eid-primary); color: var(--eid-primary); } +.login-btn.outline:hover { background: var(--eid-primary); color: white; } +.login-btn.small { padding: 8px 16px; font-size: 0.875rem; } +.login-btn.large { padding: 16px 32px; font-size: 1.125rem; } +.passkey-icon { width: 24px; height: 24px; } +.user-info { display: flex; align-items: center; gap: 12px; padding: 8px 16px; background: var(--eid-bg); border-radius: var(--eid-radius); color: var(--eid-text); cursor: pointer; } +.user-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--eid-primary); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 0.875rem; } +.user-did { font-size: 0.75rem; color: var(--eid-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; } +.auth-level { font-size: 0.625rem; padding: 2px 6px; border-radius: 4px; } +.auth-level.elevated { background: rgba(34, 197, 94, 0.2); color: #22c55e; } +.auth-level.standard { background: rgba(234, 179, 8, 0.2); color: #eab308; } +.dropdown { position: absolute; top: 100%; right: 0; margin-top: 8px; background: var(--eid-bg); border-radius: var(--eid-radius); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3); min-width: 200px; z-index: 100; overflow: hidden; } +.dropdown-item { display: flex; align-items: center; gap: 12px; padding: 12px 16px; color: var(--eid-text); cursor: pointer; transition: background 0.2s; } +.dropdown-item:hover { background: var(--eid-bg-hover); } +.dropdown-item.danger { color: #ef4444; } +.dropdown-divider { height: 1px; background: #334155; margin: 4px 0; } +.loading-spinner { width: 20px; height: 20px; border: 2px solid transparent; border-top-color: currentColor; border-radius: 50%; animation: spin 0.8s linear infinite; } +@keyframes spin { to { transform: rotate(360deg); } } +`; + +class EncryptIDLoginButton extends HTMLElement { + shadow; + loading = false; + showDropdown = false; + capabilities = null; + static get observedAttributes() { + return ["size", "variant", "label", "show-user"]; + } + constructor() { + super(); + this.shadow = this.attachShadow({ mode: "open" }); + } + async connectedCallback() { + this.capabilities = await detectCapabilities(); + if (this.capabilities.conditionalUI) + this.startConditionalAuth(); + this.render(); + document.addEventListener("click", (e) => { + if (!this.contains(e.target)) { + this.showDropdown = false; + this.render(); + } + }); + } + attributeChangedCallback() { + this.render(); + } + get size() { + return this.getAttribute("size") || "medium"; + } + get variant() { + return this.getAttribute("variant") || "primary"; + } + get label() { + return this.getAttribute("label") || "Sign in with Passkey"; + } + get showUser() { + return this.hasAttribute("show-user"); + } + render() { + const session = getSessionManager(); + const isLoggedIn = session.isValid(); + const did = session.getDID(); + const authLevel = session.getAuthLevel(); + this.shadow.innerHTML = ``; + this.attachEventListeners(); + } + renderLoginButton() { + const sizeClass = this.size === "medium" ? "" : this.size; + const variantClass = this.variant === "primary" ? "" : this.variant; + return ``; + } + renderUserInfo(did, authLevel) { + const shortDID = did.slice(0, 20) + "..." + did.slice(-8); + const initial = did.slice(8, 10).toUpperCase(); + const levelName = AuthLevel[authLevel].toLowerCase(); + return ``; + } + renderDropdown() { + return ``; + } + attachEventListeners() { + const session = getSessionManager(); + if (session.isValid() && this.showUser) { + this.shadow.querySelector(".user-info")?.addEventListener("click", () => { + this.showDropdown = !this.showDropdown; + this.render(); + }); + this.shadow.querySelectorAll(".dropdown-item").forEach((item) => { + item.addEventListener("click", (e) => { + e.stopPropagation(); + this.handleDropdownAction(item.dataset.action); + }); + }); + } else { + this.shadow.querySelector(".login-btn")?.addEventListener("click", () => this.handleLogin()); + } + } + async handleLogin() { + if (this.loading) + return; + this.loading = true; + this.render(); + try { + const result = await authenticatePasskey(); + const keyManager = getKeyManager(); + if (result.prfOutput) + await keyManager.initFromPRF(result.prfOutput); + const keys = await keyManager.getKeys(); + await getSessionManager().createSession(result, keys.did, { encrypt: true, sign: true, wallet: false }); + this.dispatchEvent(new CustomEvent("login-success", { detail: { did: keys.did, credentialId: result.credentialId, prfAvailable: !!result.prfOutput }, bubbles: true })); + } catch (error) { + if (error.name === "NotAllowedError" || error.message?.includes("No credential")) { + this.dispatchEvent(new CustomEvent("login-register-needed", { bubbles: true })); + } else { + this.dispatchEvent(new CustomEvent("login-error", { detail: { error: error.message }, bubbles: true })); + } + } finally { + this.loading = false; + this.render(); + } + } + async handleDropdownAction(action) { + this.showDropdown = false; + if (action === "logout") { + getSessionManager().clearSession(); + getKeyManager().clear(); + this.dispatchEvent(new CustomEvent("logout", { bubbles: true })); + } else if (action === "upgrade") { + try { + await authenticatePasskey(); + getSessionManager().upgradeAuthLevel(3 /* ELEVATED */); + this.dispatchEvent(new CustomEvent("auth-upgraded", { detail: { level: 3 /* ELEVATED */ }, bubbles: true })); + } catch {} + } else { + this.dispatchEvent(new CustomEvent("navigate", { detail: { path: `/${action}` }, bubbles: true })); + } + this.render(); + } + async startConditionalAuth() { + try { + const result = await startConditionalUI(); + if (result) { + const keyManager = getKeyManager(); + if (result.prfOutput) + await keyManager.initFromPRF(result.prfOutput); + const keys = await keyManager.getKeys(); + await getSessionManager().createSession(result, keys.did, { encrypt: true, sign: true, wallet: false }); + this.dispatchEvent(new CustomEvent("login-success", { detail: { did: keys.did, credentialId: result.credentialId, viaConditionalUI: true }, bubbles: true })); + this.render(); + } + } catch {} + } + async register(username, displayName) { + this.loading = true; + this.render(); + try { + const credential = await registerPasskey(username, displayName); + this.dispatchEvent(new CustomEvent("register-success", { detail: { credentialId: credential.credentialId, prfSupported: credential.prfSupported }, bubbles: true })); + await this.handleLogin(); + } catch (error) { + this.dispatchEvent(new CustomEvent("register-error", { detail: { error: error.message }, bubbles: true })); + } finally { + this.loading = false; + this.render(); + } + } +} +customElements.define("encryptid-login", EncryptIDLoginButton); +// src/ui/guardian-setup.ts +class GuardianSetupElement extends HTMLElement { + shadow; + constructor() { + super(); + this.shadow = this.attachShadow({ mode: "open" }); + } + connectedCallback() { + const manager = getRecoveryManager(); + const config = manager.getConfig(); + if (!config) { + manager.initializeRecovery(3).then(() => this.render()); + } else { + this.render(); + } + } + render() { + const manager = getRecoveryManager(); + const config = manager.getConfig(); + const guardians = config?.guardians ?? []; + const threshold = config?.threshold ?? 3; + const totalWeight = guardians.reduce((sum, g) => sum + g.weight, 0); + const isConfigured = totalWeight >= threshold; + this.shadow.innerHTML = ` + +
+

Social Recovery

+

Set up guardians to recover your account without seed phrases

+
+
+
+
${isConfigured ? "Recovery Configured" : "Setup Incomplete"}
+
${totalWeight}/${threshold} guardians
+
+
+ ${guardians.map((g) => { + const info = getGuardianTypeInfo(g.type); + return `
${info.icon === "key" ? "\uD83D\uDD11" : info.icon === "user" ? "\uD83D\uDC64" : info.icon === "shield" ? "\uD83D\uDEE1️" : info.icon === "building" ? "\uD83C\uDFE2" : "⏰"}
${g.name}
${info.name}
`; + }).join("")} +
+ `; + } +} +customElements.define("encryptid-guardian-setup", GuardianSetupElement); +export { + GuardianSetupElement, + EncryptIDLoginButton +}; diff --git a/frontend/vendor/@encryptid/sdk/ui/login-button.d.ts b/frontend/vendor/@encryptid/sdk/ui/login-button.d.ts new file mode 100644 index 0000000..cca4d94 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/login-button.d.ts @@ -0,0 +1,30 @@ +/** + * EncryptID Login Button Web Component + * + * Custom element: + * Attributes: size (small|medium|large), variant (primary|outline), label, show-user + * Events: login-success, login-error, login-register-needed, logout, auth-upgraded + */ +export declare class EncryptIDLoginButton extends HTMLElement { + private shadow; + private loading; + private showDropdown; + private capabilities; + static get observedAttributes(): string[]; + constructor(); + connectedCallback(): Promise; + attributeChangedCallback(): void; + private get size(); + private get variant(); + private get label(); + private get showUser(); + private render; + private renderLoginButton; + private renderUserInfo; + private renderDropdown; + private attachEventListeners; + private handleLogin; + private handleDropdownAction; + private startConditionalAuth; + register(username: string, displayName: string): Promise; +} diff --git a/frontend/vendor/@encryptid/sdk/ui/react/EncryptIDProvider.d.ts b/frontend/vendor/@encryptid/sdk/ui/react/EncryptIDProvider.d.ts new file mode 100644 index 0000000..7c5193f --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/react/EncryptIDProvider.d.ts @@ -0,0 +1,39 @@ +/** + * EncryptID React Context Provider + * + * Wraps your app to provide EncryptID auth state to all components. + * Features: localStorage + cookie persistence, auto-refresh, session verification on mount. + */ +import React, { type ReactNode } from 'react'; +import { EncryptIDClient } from '../../client/api-client.js'; +import type { EncryptIDClaims } from '../../types/index.js'; +interface EncryptIDContextValue { + /** Whether the user is authenticated */ + isAuthenticated: boolean; + /** JWT token (null if not authenticated) */ + token: string | null; + /** Decoded claims (null if not authenticated) */ + claims: EncryptIDClaims | null; + /** User's DID (null if not authenticated) */ + did: string | null; + /** Username from EncryptID */ + username: string | null; + /** Whether auth state is being loaded */ + loading: boolean; + /** Full registration + authentication flow */ + register: (username: string, displayName?: string) => Promise; + /** Full authentication flow */ + login: (credentialId?: string) => Promise; + /** Clear session */ + logout: () => void; + /** The EncryptID API client */ + client: EncryptIDClient; +} +interface EncryptIDProviderProps { + children: ReactNode; + /** EncryptID server URL (default: https://encryptid.jeffemmett.com) */ + serverUrl?: string; +} +export declare function EncryptIDProvider({ children, serverUrl }: EncryptIDProviderProps): React.FunctionComponentElement>; +export declare function useEncryptID(): EncryptIDContextValue; +export {}; diff --git a/frontend/vendor/@encryptid/sdk/ui/react/LoginButton.d.ts b/frontend/vendor/@encryptid/sdk/ui/react/LoginButton.d.ts new file mode 100644 index 0000000..1caa867 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/react/LoginButton.d.ts @@ -0,0 +1,32 @@ +/** + * EncryptID Login Button — React Component + * + * Wraps the EncryptID client for easy React integration. + */ +import React from 'react'; +interface LoginButtonProps { + /** Button label (default: "Sign in with Passkey") */ + label?: string; + /** Button size */ + size?: 'small' | 'medium' | 'large'; + /** Visual variant */ + variant?: 'primary' | 'outline'; + /** Callback after successful login */ + onSuccess?: (result: { + token: string; + did: string; + }) => void; + /** Callback on error */ + onError?: (error: Error) => void; + /** Callback when registration is needed */ + onRegisterNeeded?: () => void; + /** Additional CSS class */ + className?: string; +} +export declare function LoginButton({ label, size, variant, onSuccess, onError, onRegisterNeeded, className, }: LoginButtonProps): React.DetailedReactHTMLElement<{ + onClick: () => Promise; + disabled: boolean; + style: React.CSSProperties; + className: string; +}, HTMLElement>; +export {}; diff --git a/frontend/vendor/@encryptid/sdk/ui/react/index.d.ts b/frontend/vendor/@encryptid/sdk/ui/react/index.d.ts new file mode 100644 index 0000000..8afd55f --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/react/index.d.ts @@ -0,0 +1,5 @@ +/** + * @encryptid/sdk/ui/react — React Components + */ +export { EncryptIDProvider, useEncryptID } from './EncryptIDProvider.js'; +export { LoginButton } from './LoginButton.js'; diff --git a/frontend/vendor/@encryptid/sdk/ui/react/index.js b/frontend/vendor/@encryptid/sdk/ui/react/index.js new file mode 100644 index 0000000..104ecf1 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/react/index.js @@ -0,0 +1,160 @@ +import { + EncryptIDClient +} from "../../index-7egxprg9.js"; +import"../../index-2cp5044h.js"; + +// src/ui/react/EncryptIDProvider.tsx +import React, { createContext, useContext, useEffect, useState, useCallback } from "react"; +var EncryptIDContext = createContext(null); +var TOKEN_KEY = "encryptid_token"; +function EncryptIDProvider({ children, serverUrl }) { + const [client] = useState(() => new EncryptIDClient(serverUrl)); + const [token, setToken] = useState(null); + const [claims, setClaims] = useState(null); + const [loading, setLoading] = useState(true); + useEffect(() => { + const stored = localStorage.getItem(TOKEN_KEY); + if (stored) { + client.verifySession(stored).then((res) => { + if (res.valid) { + setToken(stored); + try { + const payload = JSON.parse(atob(stored.split(".")[1])); + setClaims(payload); + } catch {} + } else { + localStorage.removeItem(TOKEN_KEY); + } + }).catch(() => { + localStorage.removeItem(TOKEN_KEY); + }).finally(() => setLoading(false)); + } else { + setLoading(false); + } + }, [client]); + const register = useCallback(async (username, displayName) => { + setLoading(true); + try { + const result = await client.register(username, displayName); + setToken(result.token); + localStorage.setItem(TOKEN_KEY, result.token); + try { + setClaims(JSON.parse(atob(result.token.split(".")[1]))); + } catch {} + } finally { + setLoading(false); + } + }, [client]); + const login = useCallback(async (credentialId) => { + setLoading(true); + try { + const result = await client.authenticate(credentialId); + setToken(result.token); + localStorage.setItem(TOKEN_KEY, result.token); + try { + setClaims(JSON.parse(atob(result.token.split(".")[1]))); + } catch {} + } finally { + setLoading(false); + } + }, [client]); + const logout = useCallback(() => { + setToken(null); + setClaims(null); + localStorage.removeItem(TOKEN_KEY); + localStorage.removeItem("encryptid_session"); + }, []); + const value = { + isAuthenticated: !!token, + token, + claims, + did: claims?.did || claims?.sub || null, + username: claims?.username || null, + loading, + register, + login, + logout, + client + }; + return React.createElement(EncryptIDContext.Provider, { value }, children); +} +function useEncryptID() { + const ctx = useContext(EncryptIDContext); + if (!ctx) + throw new Error("useEncryptID must be used within "); + return ctx; +} +// src/ui/react/LoginButton.tsx +import React2, { useState as useState2, useCallback as useCallback2 } from "react"; +function LoginButton({ + label = "Sign in with Passkey", + size = "medium", + variant = "primary", + onSuccess, + onError, + onRegisterNeeded, + className = "" +}) { + const { login, isAuthenticated, did, logout, loading: contextLoading } = useEncryptID(); + const [loading, setLoading] = useState2(false); + const handleClick = useCallback2(async () => { + if (isAuthenticated) { + logout(); + return; + } + setLoading(true); + try { + await login(); + const token = localStorage.getItem("encryptid_token") || ""; + const currentDid = did || ""; + onSuccess?.({ token, did: currentDid }); + } catch (error) { + if (error.name === "NotAllowedError") { + onRegisterNeeded?.(); + } else { + onError?.(error); + } + } finally { + setLoading(false); + } + }, [login, logout, isAuthenticated, did, onSuccess, onError, onRegisterNeeded]); + const isLoading = loading || contextLoading; + const sizeStyles = { + small: { padding: "8px 16px", fontSize: "0.875rem" }, + medium: { padding: "12px 24px", fontSize: "1rem" }, + large: { padding: "16px 32px", fontSize: "1.125rem" } + }; + const baseStyle = { + display: "inline-flex", + alignItems: "center", + gap: "12px", + border: "none", + borderRadius: "8px", + fontWeight: 500, + cursor: isLoading ? "not-allowed" : "pointer", + opacity: isLoading ? 0.6 : 1, + transition: "all 0.2s", + fontFamily: "system-ui, -apple-system, sans-serif", + ...sizeStyles[size], + ...variant === "primary" ? { background: "#06b6d4", color: "white" } : { background: "transparent", border: "2px solid #06b6d4", color: "#06b6d4" } + }; + return React2.createElement("button", { + onClick: handleClick, + disabled: isLoading, + style: baseStyle, + className + }, isLoading ? React2.createElement("span", null, "Authenticating...") : React2.createElement(React2.Fragment, null, React2.createElement("svg", { + viewBox: "0 0 24 24", + fill: "none", + stroke: "currentColor", + strokeWidth: 2, + strokeLinecap: "round", + strokeLinejoin: "round", + style: { width: "24px", height: "24px" } + }, React2.createElement("circle", { cx: 12, cy: 10, r: 3 }), React2.createElement("path", { d: "M12 13v8" }), React2.createElement("path", { d: "M9 18h6" }), React2.createElement("circle", { cx: 12, cy: 10, r: 7 })), React2.createElement("span", null, isAuthenticated ? "Sign Out" : label))); +} +export { + useEncryptID, + LoginButton, + EncryptIDProvider +}; diff --git a/frontend/vendor/@encryptid/sdk/ui/react/useEncryptID.d.ts b/frontend/vendor/@encryptid/sdk/ui/react/useEncryptID.d.ts new file mode 100644 index 0000000..4c6ecd6 --- /dev/null +++ b/frontend/vendor/@encryptid/sdk/ui/react/useEncryptID.d.ts @@ -0,0 +1,4 @@ +/** + * Re-export useEncryptID hook from Provider module + */ +export { useEncryptID } from './EncryptIDProvider.js';