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 ──────────────────────────────────── */}
-
+
{/* ── 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) + '...'}
+
+
+ Sign out
+
+
+ );
+ }
+
+ 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');
+ });
+ }
+ }}
+ />
+ {
+ if (!regUsername.trim()) return;
+ setError('');
+ try {
+ await register(regUsername.trim());
+ } catch (e: unknown) {
+ setError(e instanceof Error ? e.message : 'Registration failed');
+ }
+ }}
+ disabled={loading || !regUsername.trim()}
+ className="text-sm py-1 px-3 bg-primary text-primary-foreground rounded-md font-medium hover:bg-primary/90 transition-colors disabled:opacity-50"
+ >
+ {loading ? '...' : 'Register'}
+
+ setShowRegister(false)}
+ className="text-xs text-white/40 hover:text-white/60"
+ >
+ Cancel
+
+ {error && {error} }
+
+ );
+ }
+
+ return (
+
+
{
+ setError('');
+ try {
+ await login();
+ } catch (e: unknown) {
+ if (e instanceof DOMException && (e.name === 'NotAllowedError' || e.name === 'SecurityError' || e.name === 'AbortError')) {
+ setShowRegister(true);
+ } else {
+ setError(e instanceof Error ? e.message : 'Sign in failed');
+ }
+ }
+ }}
+ disabled={loading}
+ className="text-sm text-white/60 hover:text-primary transition-colors flex items-center gap-1.5"
+ >
+
+
+
+
+
+
+ {loading ? 'Signing in...' : 'Sign in with Passkey'}
+
+ {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 (
+
+ );
+}
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=`
+ ${t&&this.showUser?this.renderUserInfo(o,r):this.renderLoginButton()}
+ ${this.showDropdown?this.renderDropdown():""}
+
`,this.attachEventListeners()}renderLoginButton(){let i=this.size==="medium"?"":this.size,t=this.variant==="primary"?"":this.variant;return`
+ ${this.loading?'
':ii}
+ ${this.loading?"Authenticating...":this.label} `}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`
+
Profile
+
Recovery Settings
+
Upgrade Auth Level
+
+
Sign Out
`}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":"⏰"}
`}).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)+"..."}
+ Sign Out
+
`,document.getElementById("eid-signout")?.addEventListener("click",()=>{P(),o(),t.dispatchEvent(new CustomEvent("encryptid-logout",{bubbles:!0}))});else t.innerHTML=`
+
+
+
+
+ Sign in with Passkey
+ `,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 = `
+ ${isLoggedIn && this.showUser ? this.renderUserInfo(did, authLevel) : this.renderLoginButton()}
+ ${this.showDropdown ? this.renderDropdown() : ""}
+
`;
+ this.attachEventListeners();
+ }
+ renderLoginButton() {
+ const sizeClass = this.size === "medium" ? "" : this.size;
+ const variantClass = this.variant === "primary" ? "" : this.variant;
+ return `
+ ${this.loading ? '
' : PASSKEY_ICON}
+ ${this.loading ? "Authenticating..." : this.label} `;
+ }
+ 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 `
+
Profile
+
Recovery Settings
+
Upgrade Auth Level
+
+
Sign Out
`;
+ }
+ 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" : "⏰"}
`;
+ }).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';