diff --git a/.env.example b/.env.example index 6a38e2f9d..be43754db 100644 --- a/.env.example +++ b/.env.example @@ -262,3 +262,17 @@ WORLDMONITOR_VALID_KEYS= # Convex deployment URL for email registration storage. # Set up at: https://dashboard.convex.dev/ CONVEX_URL= + +# ------ Auth (Clerk) ------ + +# Clerk publishable key (browser-side, safe to expose) +# Get from: Clerk Dashboard -> API Keys +VITE_CLERK_PUBLISHABLE_KEY= + +# Clerk secret key (server-side only, never expose to browser) +# Get from: Clerk Dashboard -> API Keys +CLERK_SECRET_KEY= + +# Clerk JWT issuer domain (for Convex auth config) +# Format: https://your-clerk-app.clerk.accounts.dev +CLERK_JWT_ISSUER_DOMAIN= diff --git a/DEPLOYMENT-PLAN.md b/DEPLOYMENT-PLAN.md new file mode 100644 index 000000000..6b1a5eab3 --- /dev/null +++ b/DEPLOYMENT-PLAN.md @@ -0,0 +1,99 @@ +# Deployment Plan — Clerk Auth + Dodo Payments + +## Merge Order + +**PR #1812 first, then PR #2024.** Dodo billing functions depend on Clerk auth being registered in Convex. + +1. Merge `feat/better-auth` → `main` (PR #1812) +2. Rebase `dodo_payments` on updated `main`, resolve conflicts +3. Merge `dodo_payments` → `main` (PR #2024) + +--- + +## Environment Variables + +### Clerk Auth (PR #1812) + +All values from **Clerk Dashboard → API Keys** ([dashboard.clerk.com](https://dashboard.clerk.com)) + +| Variable | Set in | Value | +|----------|--------|-------| +| `VITE_CLERK_PUBLISHABLE_KEY` | **Vercel** | Clerk Dashboard → API Keys → Publishable Key (`pk_live_...`) | +| `CLERK_SECRET_KEY` | **Vercel** (secret) | Clerk Dashboard → API Keys → Secret Key (`sk_live_...`) | +| `CLERK_JWT_ISSUER_DOMAIN` | **Vercel** | Your Clerk app domain, e.g. `https://worldmonitor.clerk.accounts.dev` | + +#### Clerk Dashboard Setup + +1. **JWT Template**: Create a template named **`convex`** with custom claim: + ```json + { "plan": "{{user.public_metadata.plan}}" } + ``` +2. **Pro users**: Set `public_metadata.plan` to `"pro"` on test users to verify premium access +3. **Sign-in methods**: Configure email OTP (or whichever methods you want) under User & Authentication + +--- + +### Dodo Payments (PR #2024) + +API key + webhook secret from **Dodo Dashboard** ([app.dodopayments.com](https://app.dodopayments.com)) + +| Variable | Set in | Value | +|----------|--------|-------| +| `DODO_API_KEY` | **Convex Dashboard** | Dodo → Settings → API Keys | +| `DODO_PAYMENTS_ENVIRONMENT` | **Convex Dashboard** | `test_mode` or `live_mode` | +| `DODO_PAYMENTS_WEBHOOK_SECRET` | **Convex Dashboard** | Dodo → Developers → Webhooks → signing secret | +| `DODO_WEBHOOK_SECRET` | **Convex Dashboard** | Same value as above | +| `VITE_DODO_ENVIRONMENT` | **Vercel** | `test_mode` or `live_mode` (must match server-side) | +| `VITE_CONVEX_URL` | **Vercel** | Convex Dashboard → Settings → Deployment URL (`https://xxx.convex.cloud`) | + +#### Dodo Dashboard Setup + +1. **Webhook endpoint**: Create a webhook pointing to `https://.convex.site/dodo/webhook` +2. **Events to subscribe**: `subscription.active`, `subscription.renewed`, `subscription.on_hold`, `subscription.cancelled`, `subscription.expired`, `subscription.plan_changed`, `payment.succeeded`, `payment.failed`, `refund.succeeded`, `refund.failed`, `dispute.*` +3. **Products**: Ensure product IDs match the seed data in `convex/payments/seedProductPlans.ts` — run `seedProductPlans` mutation after deploy + +--- + +## Deployment Steps + +### Step 1 — Merge PR #1812 (Clerk Auth) + +``` +1. Set Clerk env vars on Vercel (all 3) +2. Create Clerk JWT template named "convex" +3. Merge feat/better-auth → main +4. Deploy to Vercel +5. Verify: Sign in works, Pro user sees premium panels, bearer tokens appear on premium API routes +``` + +### Step 2 — Merge PR #2024 (Dodo Payments) + +``` +1. Set Dodo env vars on Convex Dashboard (4 vars) +2. Set Dodo + Convex env vars on Vercel (2 vars) +3. Rebase dodo_payments on main, resolve conflicts +4. Merge dodo_payments → main +5. Deploy to Vercel + Convex +6. Run seedProductPlans mutation in Convex Dashboard +7. Create webhook endpoint in Dodo Dashboard +8. Verify: Checkout flow → webhook → entitlements granted → panels unlock +``` + +### Post-Deploy Verification + +- [ ] Anonymous user sees locked premium panels +- [ ] Clerk sign-in works (email OTP or configured method) +- [ ] Pro user (`public_metadata.plan: "pro"`) sees unlocked panels + data loads +- [ ] Dodo test checkout (`4242 4242 4242 4242`) creates subscription +- [ ] Webhook fires → subscription + entitlements appear in Convex Dashboard +- [ ] Billing portal opens from Settings +- [ ] Desktop API key flow still works unchanged + +--- + +## Summary + +| Where | Variables to set | +|-------|-----------------| +| **Vercel** | `VITE_CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, `CLERK_JWT_ISSUER_DOMAIN`, `VITE_DODO_ENVIRONMENT`, `VITE_CONVEX_URL` | +| **Convex Dashboard** | `DODO_API_KEY`, `DODO_PAYMENTS_ENVIRONMENT`, `DODO_PAYMENTS_WEBHOOK_SECRET`, `DODO_WEBHOOK_SECRET` | diff --git a/convex/_generated/registerInterest.js b/convex/_generated/registerInterest.js deleted file mode 100644 index 7f8b5595b..000000000 --- a/convex/_generated/registerInterest.js +++ /dev/null @@ -1,117 +0,0 @@ -import { mutation, query } from "./_generated/server"; -import { v } from "convex/values"; -function hashCode(str) { - let hash = 0; - for (let i = 0; i < str.length; i++) { - hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0; - } - return Math.abs(hash); -} -async function generateUniqueReferralCode(db, email) { - for (let attempt = 0; attempt < 10; attempt++) { - const input = attempt === 0 ? email : `${email}:${attempt}`; - const code = hashCode(input).toString(36).padStart(6, "0").slice(0, 8); - const existing = await db - .query("registrations") - .withIndex("by_referral_code", (q) => q.eq("referralCode", code)) - .first(); - if (!existing) - return code; - } - // Fallback: timestamp-based code (extremely unlikely path) - return Date.now().toString(36).slice(-8); -} -async function getCounter(db, name) { - const counter = await db - .query("counters") - .withIndex("by_name", (q) => q.eq("name", name)) - .first(); - return counter?.value ?? 0; -} -async function incrementCounter(db, name) { - const counter = await db - .query("counters") - .withIndex("by_name", (q) => q.eq("name", name)) - .first(); - const newVal = (counter?.value ?? 0) + 1; - if (counter) { - await db.patch(counter._id, { value: newVal }); - } - else { - await db.insert("counters", { name, value: newVal }); - } - return newVal; -} -export const register = mutation({ - args: { - email: v.string(), - source: v.optional(v.string()), - appVersion: v.optional(v.string()), - referredBy: v.optional(v.string()), - }, - handler: async (ctx, args) => { - const normalizedEmail = args.email.trim().toLowerCase(); - const existing = await ctx.db - .query("registrations") - .withIndex("by_normalized_email", (q) => q.eq("normalizedEmail", normalizedEmail)) - .first(); - if (existing) { - let code = existing.referralCode; - if (!code) { - code = await generateUniqueReferralCode(ctx.db, normalizedEmail); - await ctx.db.patch(existing._id, { referralCode: code }); - } - return { - status: "already_registered", - referralCode: code, - referralCount: existing.referralCount ?? 0, - }; - } - const referralCode = await generateUniqueReferralCode(ctx.db, normalizedEmail); - // Credit the referrer - if (args.referredBy) { - const referrer = await ctx.db - .query("registrations") - .withIndex("by_referral_code", (q) => q.eq("referralCode", args.referredBy)) - .first(); - if (referrer) { - await ctx.db.patch(referrer._id, { - referralCount: (referrer.referralCount ?? 0) + 1, - }); - } - } - const position = await incrementCounter(ctx.db, "registrations_total"); - await ctx.db.insert("registrations", { - email: args.email.trim(), - normalizedEmail, - registeredAt: Date.now(), - source: args.source ?? "unknown", - appVersion: args.appVersion ?? "unknown", - referralCode, - referredBy: args.referredBy, - referralCount: 0, - }); - return { - status: "registered", - referralCode, - referralCount: 0, - position, - }; - }, -}); -export const getPosition = query({ - args: { referralCode: v.string() }, - handler: async (ctx, args) => { - const reg = await ctx.db - .query("registrations") - .withIndex("by_referral_code", (q) => q.eq("referralCode", args.referralCode)) - .first(); - if (!reg) - return null; - const total = await getCounter(ctx.db, "registrations_total"); - return { - referralCount: reg.referralCount ?? 0, - total, - }; - }, -}); diff --git a/convex/_generated/schema.js b/convex/_generated/schema.js deleted file mode 100644 index 87b8ac6af..000000000 --- a/convex/_generated/schema.js +++ /dev/null @@ -1,20 +0,0 @@ -import { defineSchema, defineTable } from "convex/server"; -import { v } from "convex/values"; -export default defineSchema({ - registrations: defineTable({ - email: v.string(), - normalizedEmail: v.string(), - registeredAt: v.number(), - source: v.optional(v.string()), - appVersion: v.optional(v.string()), - referralCode: v.optional(v.string()), - referredBy: v.optional(v.string()), - referralCount: v.optional(v.number()), - }) - .index("by_normalized_email", ["normalizedEmail"]) - .index("by_referral_code", ["referralCode"]), - counters: defineTable({ - name: v.string(), - value: v.number(), - }).index("by_name", ["name"]), -}); diff --git a/convex/auth.config.ts b/convex/auth.config.ts new file mode 100644 index 000000000..aafd76ab6 --- /dev/null +++ b/convex/auth.config.ts @@ -0,0 +1,11 @@ +const domain = process.env.CLERK_JWT_ISSUER_DOMAIN; +if (!domain) throw new Error('CLERK_JWT_ISSUER_DOMAIN is not set'); + +export default { + providers: [ + { + domain, + applicationID: "convex", + }, + ], +}; diff --git a/convex/convex.config.ts b/convex/convex.config.ts new file mode 100644 index 000000000..802c7d6bd --- /dev/null +++ b/convex/convex.config.ts @@ -0,0 +1,3 @@ +import { defineApp } from "convex/server"; +const app = defineApp(); +export default app; diff --git a/docs/authentication.mdx b/docs/authentication.mdx new file mode 100644 index 000000000..88acb781d --- /dev/null +++ b/docs/authentication.mdx @@ -0,0 +1,144 @@ +--- +title: "Authentication & Panel Gating" +description: "How user authentication, premium panel gating, and server-side session enforcement work in WorldMonitor." +--- + +WorldMonitor uses [Clerk](https://clerk.com) for authentication. The auth system gates premium panels behind sign-in and tier checks, and enforces session-based access on server-side API endpoints via local JWT verification. + +--- + +## Auth Stack + +| Layer | Technology | Purpose | +|-------|-----------|---------| +| Auth provider | Clerk | Sign-in (email, social), session management, hosted UI | +| JWT verification | jose + Clerk JWKS | Server-side bearer token validation (no round-trip) | +| Convex integration | Clerk JWT template (`convex`) | Convex auth with `applicationID: "convex"` | +| Auth state | `auth-state.ts` | Reactive browser auth state, role caching | + +### Key Files + +| File | Purpose | +|------|---------| +| `convex/auth.config.ts` | Convex auth provider config — Clerk JWT issuer + applicationID | +| `src/services/clerk.ts` | Clerk instance init, `getClerkToken()` for Convex JWT template | +| `src/services/auth-state.ts` | Reactive auth state, role fetching, session hydration | +| `src/components/AuthHeaderWidget.ts` | Header sign-in button, Clerk UserButton | +| `server/auth-session.ts` | Server-side JWT validation with jose + cached JWKS | + +--- + +## Panel Gating + +Premium panels show a CTA overlay instead of content until the user meets the access requirements. + +### Gate Reasons + +| Reason | What the user sees | Resolution | +|--------|-------------------|------------| +| `ANONYMOUS` | "Sign In to Unlock" | Sign in via Clerk | +| `FREE_TIER` | "Upgrade to Pro" | Upgrade subscription | +| `NONE` | Normal panel content | Already unlocked | + +### How to Configure Which Panels Are Premium + +Three files control gating. **All three must stay in sync** when adding or removing premium panels. + +#### 1. Panel config — `src/config/panels.ts` + +Add `premium: 'locked'` to the panel entry in the relevant variant: + +```ts +// In FULL_PANELS, FINANCE_PANELS, etc. +'my-panel': { name: 'My Panel', enabled: true, premium: 'locked' } +``` + +#### 2. Client-side gate set — `src/app/panel-layout.ts` + +Add the panel key to `WEB_PREMIUM_PANELS`: + +```ts +const WEB_PREMIUM_PANELS = new Set([ + 'stock-analysis', + 'stock-backtest', + 'daily-market-brief', + 'my-panel', // <-- add here +]); +``` + +This set drives the reactive UI gating — when auth state changes, panels in this set get checked and show/hide CTAs accordingly. + +#### 3. Server-side API enforcement (if the panel calls premium APIs) + +**Client token injection** — `src/services/runtime.ts` (`WEB_PREMIUM_API_PATHS`): + +```ts +const WEB_PREMIUM_API_PATHS = new Set([ + '/api/market/v1/analyze-stock', + '/api/market/v1/get-stock-analysis-history', + '/api/market/v1/backtest-stock', + '/api/market/v1/list-stored-stock-backtests', + '/api/my-domain/v1/my-endpoint', // <-- add here +]); +``` + +When a fetch request matches a path in this set and the user has a Clerk session, the client automatically injects `Authorization: Bearer `. + +**Server gateway** — `server/gateway.ts` (`PREMIUM_RPC_PATHS`): + +```ts +const PREMIUM_RPC_PATHS = new Set([ + '/api/market/v1/analyze-stock', + '/api/market/v1/get-stock-analysis-history', + '/api/market/v1/backtest-stock', + '/api/market/v1/list-stored-stock-backtests', + '/api/my-domain/v1/my-endpoint', // <-- add here +]); +``` + +The gateway validates the bearer token via local JWKS verification (jose) and checks `session.role === 'pro'`. Returns 403 if the user isn't pro. + +### Currently Gated Panels + +| Panel | Variants | Gate type | +|-------|----------|-----------| +| `stock-analysis` | full, finance | `locked` (web) | +| `stock-backtest` | full, finance | `locked` (web) | +| `daily-market-brief` | full, finance | `locked` (web) | + +### Desktop Behavior + +Desktop users with a valid `WORLDMONITOR_API_KEY` in the Tauri keychain bypass all panel gating. The existing API key flow is unaffected — bearer tokens are a **second auth path**, not a replacement. + +--- + +## Server-Side Session Enforcement + +The Vercel API gateway accepts two forms of authentication for premium endpoints: + +1. **Static API key** — `X-WorldMonitor-Key` header (existing flow, unchanged) +2. **Bearer token** — `Authorization: Bearer ` (for web users) + +The gateway tries the API key first. If that fails on a premium endpoint, it falls back to bearer token validation using local JWKS verification via `server/auth-session.ts`. The JWT is verified against: +- **Issuer**: `CLERK_JWT_ISSUER_DOMAIN` +- **Audience**: `convex` (matches the Clerk JWT template) +- **Signature**: RSA256 via Clerk's published JWKS + +Non-premium endpoints don't require any authentication from web origins. + +--- + +## Environment Variables + +| Variable | Where | Purpose | +|----------|-------|---------| +| `CLERK_JWT_ISSUER_DOMAIN` | Convex + Vercel | Clerk issuer domain for JWT verification | +| `VITE_CLERK_PUBLISHABLE_KEY` | Vercel | Client-side Clerk publishable key | + +--- + +## User Roles + +User roles (`pro` / `free`) are stored as a `plan` claim in the Clerk JWT. The server extracts this from the verified token payload. Unknown or missing `plan` values default to `free` (fail closed — never pro). + +On the client side, `getAuthState().user?.role` exposes the role. Both `isProUser()` and `hasPremiumAccess()` check this alongside legacy API key gates. diff --git a/docs/docs.json b/docs/docs.json index 9c7a52b37..3e8568757 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -113,6 +113,7 @@ "group": "Developer Guide", "pages": [ "contributing", + "authentication", "adding-endpoints", "api-key-deployment", "release-packaging", diff --git a/e2e/auth-ui.spec.ts b/e2e/auth-ui.spec.ts new file mode 100644 index 000000000..b684ce344 --- /dev/null +++ b/e2e/auth-ui.spec.ts @@ -0,0 +1,52 @@ +import { expect, test, type Page } from '@playwright/test'; + +/** Click an element via JS to bypass overlay interception. */ +async function jsClick(page: Page, selector: string) { + await page.evaluate((sel) => { + (document.querySelector(sel) as HTMLElement)?.click(); + }, selector); +} + +test.describe('auth UI (anonymous state)', () => { + test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + // Dismiss the layer performance warning overlay + localStorage.setItem('wm-layer-warning-dismissed', 'true'); + }); + }); + + test('Sign In button visible with readable text', async ({ page }) => { + await page.goto('/'); + const signInBtn = page.locator('.auth-signin-btn'); + await signInBtn.waitFor({ timeout: 20000 }); + await expect(signInBtn).toBeVisible(); + await expect(signInBtn).toHaveText('Sign In'); + + const styles = await signInBtn.evaluate((el) => { + const cs = getComputedStyle(el); + return { color: cs.color, background: cs.backgroundColor }; + }); + expect(styles.color).not.toBe(styles.background); + }); + + test('Sign In click triggers Clerk modal or overlay', async ({ page }) => { + await page.goto('/'); + await page.locator('.auth-signin-btn').waitFor({ timeout: 20000 }); + await jsClick(page, '.auth-signin-btn'); + + // Clerk renders its modal into .cl-rootBox or an iframe. + // When Clerk JS is not configured (no publishable key in test env), + // the click simply invokes openSignIn() which is a no-op -- verify + // no uncaught errors instead. + const errors: string[] = []; + page.on('pageerror', (err) => errors.push(err.message)); + await page.waitForTimeout(1000); + expect(errors.filter((e) => e.includes('auth'))).toHaveLength(0); + }); + + test('premium panels gated for anonymous users', async ({ page }) => { + await page.goto('/'); + await page.waitForSelector('.panel', { timeout: 20000 }); + await expect(page.locator('.panel-is-locked').first()).toBeVisible({ timeout: 15000 }); + }); +}); diff --git a/index.html b/index.html index b34cea595..1a4b0ed45 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - + diff --git a/package-lock.json b/package-lock.json index a1ee2af48..e57d00adb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "dependencies": { "@anthropic-ai/sdk": "^0.79.0", "@aws-sdk/client-s3": "^3.1009.0", + "@clerk/clerk-js": "^5.56.0", "@deck.gl/aggregation-layers": "^9.2.6", "@deck.gl/core": "^9.2.6", "@deck.gl/geo-layers": "^9.2.6", @@ -33,6 +34,7 @@ "hls.js": "^1.6.15", "i18next": "^25.8.10", "i18next-browser-languagedetector": "^8.2.1", + "jose": "^6.0.11", "maplibre-gl": "^5.16.0", "marked": "^17.0.3", "onnxruntime-web": "^1.23.2", @@ -72,6 +74,12 @@ "vite-plugin-pwa": "^1.2.0" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, "node_modules/@amcharts/amcharts5": { "version": "5.14.4", "resolved": "https://registry.npmjs.org/@amcharts/amcharts5/-/amcharts5-5.14.4.tgz", @@ -1542,6 +1550,61 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", @@ -1562,7 +1625,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -1574,6 +1636,32 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", @@ -1590,6 +1678,116 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", @@ -2663,6 +2861,32 @@ "node": ">=6.9.0" } }, + "node_modules/@base-org/account": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@base-org/account/-/account-2.0.1.tgz", + "integrity": "sha512-tySVNx+vd6XEynZL0uvB10uKiwnAfThr8AbKTwILVG86mPbLAhEOInQIk+uDnvpTvfdUhC1Bi5T/46JvFoLZQQ==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "1.4.0", + "clsx": "1.2.1", + "eventemitter3": "5.0.1", + "idb-keyval": "6.2.1", + "ox": "0.6.9", + "preact": "10.24.2", + "viem": "^2.31.7", + "zustand": "5.0.3" + } + }, + "node_modules/@base-org/account/node_modules/preact": { + "version": "10.24.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.2.tgz", + "integrity": "sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/@biomejs/biome": { "version": "2.4.7", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.7.tgz", @@ -3012,6 +3236,114 @@ "sisteransi": "^1.0.5" } }, + "node_modules/@clerk/clerk-js": { + "version": "5.125.7", + "resolved": "https://registry.npmjs.org/@clerk/clerk-js/-/clerk-js-5.125.7.tgz", + "integrity": "sha512-LTLky3yyqz128tnAQKtvgZMGX2eAji4t2nNNu6WZFeez2kiSHd9XL0JWS1EFi7IWJOUkWkCqQtdPdjE9mBdppQ==", + "license": "MIT", + "dependencies": { + "@base-org/account": "2.0.1", + "@clerk/localizations": "^3.37.3", + "@clerk/shared": "^3.47.3", + "@coinbase/wallet-sdk": "4.3.0", + "@emotion/cache": "11.11.0", + "@emotion/react": "11.11.1", + "@floating-ui/react": "0.27.12", + "@floating-ui/react-dom": "^2.1.3", + "@formkit/auto-animate": "^0.8.2", + "@solana/wallet-adapter-base": "0.9.27", + "@solana/wallet-adapter-react": "0.15.39", + "@solana/wallet-standard": "1.1.4", + "@stripe/stripe-js": "5.6.0", + "@swc/helpers": "^0.5.17", + "@tanstack/query-core": "5.87.4", + "@wallet-standard/core": "1.1.1", + "@zxcvbn-ts/core": "3.0.4", + "@zxcvbn-ts/language-common": "3.0.4", + "alien-signals": "2.0.6", + "browser-tabs-lock": "1.3.0", + "copy-to-clipboard": "3.3.3", + "core-js": "3.41.0", + "crypto-js": "^4.2.0", + "dequal": "2.0.3", + "input-otp": "1.4.2", + "qrcode.react": "4.2.0", + "regenerator-runtime": "0.14.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/clerk-js/node_modules/alien-signals": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.6.tgz", + "integrity": "sha512-P3TxJSe31bUHBiblg59oU1PpaWPtmxF9GhJ/cB7OkgJ0qN/ifFSKUI25/v8ZhsT+lIG6ac8DpTOplXxORX6F3Q==", + "license": "MIT" + }, + "node_modules/@clerk/localizations": { + "version": "3.37.3", + "resolved": "https://registry.npmjs.org/@clerk/localizations/-/localizations-3.37.3.tgz", + "integrity": "sha512-xWDJngJdAKu9wU9xHy2BfkJmAISmb/AMz40ET27g/75EHfix98EKcfJsNmZ5F08/mtmFoKquLv1mOJdBosbf3Q==", + "license": "MIT", + "dependencies": { + "@clerk/types": "^4.101.21" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/shared": { + "version": "3.47.3", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.47.3.tgz", + "integrity": "sha512-jG0wMIZuuc8zaKieg9Os8ocTphG+llluRukUUdyVnu4+ZI1syVf+dkpDP3ZK69yLavTX3D0KAmkmQqTPzQV/Nw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "csstype": "3.1.3", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.5", + "std-env": "^3.9.0", + "swr": "2.3.4" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/shared/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/@clerk/types": { + "version": "4.101.21", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.101.21.tgz", + "integrity": "sha512-/70W603A6bRv1n24dDNAs3kWHLSIgXebEyzXZ46IuROWcq0+guSqqLa+nKekxxIdk6I/vnI9SWjBvBRuZVMnhQ==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.47.3" + }, + "engines": { + "node": ">=18.17.0" + } + }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", @@ -3022,6 +3354,18 @@ "node": ">=18.0.0" } }, + "node_modules/@coinbase/wallet-sdk": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@coinbase/wallet-sdk/-/wallet-sdk-4.3.0.tgz", + "integrity": "sha512-T3+SNmiCw4HzDm4we9wCHCxlP0pqCiwKe4sOwPH3YAK2KSKjxPRydKu6UQJrdONFVLG7ujXvbd/6ZqmvJb8rkw==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "^1.4.0", + "clsx": "^1.2.1", + "eventemitter3": "^5.0.1", + "preact": "^10.24.2" + } + }, "node_modules/@cryptography/aes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@cryptography/aes/-/aes-0.1.1.tgz", @@ -3383,6 +3727,159 @@ "tslib": "^2.4.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "license": "MIT" + }, "node_modules/@epic-web/invariant": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", @@ -3854,32 +4351,57 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", "license": "MIT", - "peer": true, "dependencies": { - "@floating-ui/utils": "^0.2.10" + "@floating-ui/utils": "^0.2.11" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", "license": "MIT", - "peer": true, "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.12.tgz", + "integrity": "sha512-kKlWNrpIQxF1B/a2MZvE0/uyKby4960yjO91W7nVyNKmmfNi62xU9HCjL1M1eWzx/LFj/VPSwJVbwQk9Pq/68A==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.3", + "@floating-ui/utils": "^0.2.9", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "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", - "peer": true + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" }, "node_modules/@foliojs-fork/fontkit": { "version": "1.9.2", @@ -3937,6 +4459,12 @@ "license": "MIT", "peer": true }, + "node_modules/@formkit/auto-animate": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.4.tgz", + "integrity": "sha512-DHHC01EJ1p70Q0z/ZFRBIY8NDnmfKccQoyoM84Tgb6omLMat6jivCdf272Y8k3nf4Lzdin/Y4R9q8uFtU0GbnA==", + "license": "MIT" + }, "node_modules/@huggingface/jinja": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.2.2.tgz", @@ -3982,6 +4510,253 @@ "node": ">=18.0.0" } }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "peer": true + }, + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "license": "ISC", + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -4705,6 +5480,57 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "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", @@ -6763,6 +7589,331 @@ "generate_style": "src/cli.ts" } }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.24.0.tgz", + "integrity": "sha512-W4/vbwUOYOjco0x3toB8QCr7EjIP6nE9G7o8PMguvvjYT5Awg09lyV4enACRx4s++PPulBiBSjL0KTFx2u0Z/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || >=0.60 <1.0" + } + }, + "node_modules/@react-native/assets-registry": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.84.1.tgz", + "integrity": "sha512-lAJ6PDZv95FdT9s9uhc9ivhikW1Zwh4j9XdXM7J2l4oUA3t37qfoBmTSDLuPyE3Bi+Xtwa11hJm0BUTT2sc/gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/codegen": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.84.1.tgz", + "integrity": "sha512-n1RIU0QAavgCg1uC5+s53arL7/mpM+16IBhJ3nCFSd/iK5tUmCwxQDcIDC703fuXfpub/ZygeSjVN8bcOWn0gA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.25.3", + "hermes-parser": "0.32.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "tinyglobby": "^0.2.15", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.84.1.tgz", + "integrity": "sha512-f6a+mJEJ6Joxlt/050TqYUr7uRRbeKnz8lnpL7JajhpsgZLEbkJRjH8HY5QiLcRdUwWFtizml4V+vcO3P4RxoQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@react-native/dev-middleware": "0.84.1", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.83.3", + "metro-config": "^0.83.3", + "metro-core": "^0.83.3", + "semver": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "node_modules/@react-native/debugger-frontend": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.84.1.tgz", + "integrity": "sha512-rUU/Pyh3R5zT0WkVgB+yA6VwOp7HM5Hz4NYE97ajFS07OUIcv8JzBL3MXVdSSjLfldfqOuPEuKUaZcAOwPgabw==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/debugger-shell": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.84.1.tgz", + "integrity": "sha512-LIGhh4q4ette3yW5OzmukNMYwmINYrRGDZqKyTYc/VZyNpblZPw72coXVHXdfpPT6+YlxHqXzn3UjFZpNODGCQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "debug": "^4.4.0", + "fb-dotslash": "0.5.8" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.84.1.tgz", + "integrity": "sha512-Z83ra+Gk6ElAhH3XRrv3vwbwCPTb04sPPlNpotxcFZb5LtRQZwT91ZQEXw3GOJCVIFp9EQ/gj8AQbVvtHKOUlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.84.1", + "@react-native/debugger-shell": "0.84.1", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.2.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^7.5.10" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "peer": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/dev-middleware/node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "peer": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.84.1.tgz", + "integrity": "sha512-7uVlPBE3uluRNRX4MW7PUJIO1LDBTpAqStKHU7LHH+GRrdZbHsWtOEAX8PiY4GFfBEvG8hEjiuTOqAxMjV+hDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.84.1.tgz", + "integrity": "sha512-UsTe2AbUugsfyI7XIHMQq4E7xeC8a6GrYwuK+NohMMMJMxmyM3JkzIk+GB9e2il6ScEQNMJNaj+q+i5za8itxQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.84.1.tgz", + "integrity": "sha512-/UPaQ4jl95soXnLDEJ6Cs6lnRXhwbxtT4KbZz+AFDees7prMV2NOLcHfCnzmTabf5Y3oxENMVBL666n4GMLcTA==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.84.1.tgz", + "integrity": "sha512-sJoDunzhci8ZsqxlUiKoLut4xQeQcmbIgvDHGQKeBz6uEq9HgU+hCWOijMRr6sLP0slQVfBAza34Rq7IbXZZOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", @@ -7305,6 +8456,66 @@ "win32" ] }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sentry-internal/browser-utils": { "version": "10.39.0", "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.39.0.tgz", @@ -7380,6 +8591,13 @@ "node": ">=18" } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, "node_modules/@sindresorhus/is": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", @@ -7405,6 +8623,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, "node_modules/@smithy/abort-controller": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.12.tgz", @@ -8138,6 +9376,484 @@ "node": ">=18.0.0" } }, + "node_modules/@solana-mobile/mobile-wallet-adapter-protocol": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@solana-mobile/mobile-wallet-adapter-protocol/-/mobile-wallet-adapter-protocol-2.2.6.tgz", + "integrity": "sha512-4mktUZRXdOcNHaMF6MrxN1yZpV32q616IpqsJLq/eI9Agz/+h31v5mzejIjtXCeorI7G0awfmI4ZtGIs+N/iYQ==", + "license": "Apache-2.0", + "dependencies": { + "@solana/codecs-strings": "^4.0.0", + "@solana/wallet-standard-features": "^1.3.0", + "@solana/wallet-standard-util": "^1.1.2", + "@wallet-standard/core": "^1.1.1", + "js-base64": "^3.7.5" + }, + "peerDependencies": { + "react-native": ">0.74" + } + }, + "node_modules/@solana-mobile/mobile-wallet-adapter-protocol-web3js": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@solana-mobile/mobile-wallet-adapter-protocol-web3js/-/mobile-wallet-adapter-protocol-web3js-2.2.6.tgz", + "integrity": "sha512-akbJgxlYR/BbcNPNQW5bwHv4Bf85iMu+YsUy3KJgfQympQzOQaK9/24monwCMZUG2IfQ3lBL4pi18Z1doq2BnA==", + "license": "Apache-2.0", + "dependencies": { + "@solana-mobile/mobile-wallet-adapter-protocol": "^2.2.6", + "bs58": "^6.0.0", + "js-base64": "^3.7.5" + }, + "peerDependencies": { + "@solana/web3.js": "^1.58.0" + } + }, + "node_modules/@solana-mobile/wallet-adapter-mobile": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@solana-mobile/wallet-adapter-mobile/-/wallet-adapter-mobile-2.2.6.tgz", + "integrity": "sha512-6m+h0pasnafcFfeJma+hhWKE6QSPFyIhR5QUwTAy495Y3M/aCNzmcKWRgQ6NrrfFcU7Vzuky19P13EOv2OBP2Q==", + "license": "Apache-2.0", + "dependencies": { + "@solana-mobile/mobile-wallet-adapter-protocol": "^2.2.6", + "@solana-mobile/mobile-wallet-adapter-protocol-web3js": "^2.2.6", + "@solana-mobile/wallet-standard-mobile": "^0.5.0", + "@solana/wallet-adapter-base": "^0.9.23", + "@solana/wallet-standard-features": "^1.2.0", + "@wallet-standard/core": "^1.1.1", + "bs58": "^6.0.0", + "js-base64": "^3.7.5", + "tslib": "^2.8.1" + }, + "optionalDependencies": { + "@react-native-async-storage/async-storage": "^1.17.7" + }, + "peerDependencies": { + "@solana/web3.js": "^1.98.0", + "react-native": ">0.74" + } + }, + "node_modules/@solana-mobile/wallet-standard-mobile": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@solana-mobile/wallet-standard-mobile/-/wallet-standard-mobile-0.5.0.tgz", + "integrity": "sha512-4eTrdw6hxMIBohJD+tGeNGv1MaXbyPHCtFxJ1Ru4olphiTrD6u6PvAYBL2WebQAMSWzZzDN3fCCTzludpYmwyg==", + "license": "Apache-2.0", + "dependencies": { + "@solana-mobile/mobile-wallet-adapter-protocol": "^2.2.6", + "@solana/wallet-standard-chains": "^1.1.0", + "@solana/wallet-standard-features": "^1.2.0", + "@wallet-standard/base": "^1.0.1", + "@wallet-standard/features": "^1.0.3", + "@wallet-standard/wallet": "^1.1.0", + "bs58": "^6.0.0", + "js-base64": "^3.7.5", + "qrcode": "^1.5.4", + "tslib": "^2.8.1" + }, + "optionalDependencies": { + "@react-native-async-storage/async-storage": "^1.17.7" + } + }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "license": "MIT", + "peer": true, + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/buffer-layout/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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", + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@solana/codecs-core": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-4.0.0.tgz", + "integrity": "sha512-28kNUsyIlhU3MO3/7ZLDqeJf2YAm32B4tnTjl5A9HrbBqsTZ+upT/RzxZGP1MMm7jnPuIKCMwmTpsyqyR6IUpw==", + "license": "MIT", + "dependencies": { + "@solana/errors": "4.0.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/codecs-numbers": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-4.0.0.tgz", + "integrity": "sha512-z9zpjtcwzqT9rbkKVZpkWB5/0V7+6YRKs6BccHkGJlaDx8Pe/+XOvPi2rEdXPqrPd9QWb5Xp1iBfcgaDMyiOiA==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "4.0.0", + "@solana/errors": "4.0.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/codecs-strings": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-4.0.0.tgz", + "integrity": "sha512-XvyD+sQ1zyA0amfxbpoFZsucLoe+yASQtDiLUGMDg5TZ82IHE3B7n82jE8d8cTAqi0HgqQiwU13snPhvg1O0Ow==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "4.0.0", + "@solana/codecs-numbers": "4.0.0", + "@solana/errors": "4.0.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "fastestsmallesttextencoderdecoder": "^1.0.22", + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-4.0.0.tgz", + "integrity": "sha512-3YEtvcMvtcnTl4HahqLt0VnaGVf7vVWOnt6/uPky5e0qV6BlxDSbGkbBzttNjxLXHognV0AQi3pjvrtfUnZmbg==", + "license": "MIT", + "dependencies": { + "chalk": "5.6.2", + "commander": "14.0.1" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/errors/node_modules/commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@solana/wallet-adapter-base": { + "version": "0.9.27", + "resolved": "https://registry.npmjs.org/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.27.tgz", + "integrity": "sha512-kXjeNfNFVs/NE9GPmysBRKQ/nf+foSaq3kfVSeMcO/iVgigyRmB551OjU3WyAolLG/1jeEfKLqF9fKwMCRkUqg==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-features": "^1.3.0", + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0", + "eventemitter3": "^5.0.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@solana/web3.js": "^1.98.0" + } + }, + "node_modules/@solana/wallet-adapter-react": { + "version": "0.15.39", + "resolved": "https://registry.npmjs.org/@solana/wallet-adapter-react/-/wallet-adapter-react-0.15.39.tgz", + "integrity": "sha512-WXtlo88ith5m22qB+qiGw301/Zb9r5pYr4QdXWmlXnRNqwST5MGmJWhG+/RVrzc+OG7kSb3z1gkVNv+2X/Y0Gg==", + "license": "Apache-2.0", + "dependencies": { + "@solana-mobile/wallet-adapter-mobile": "^2.2.0", + "@solana/wallet-adapter-base": "^0.9.27", + "@solana/wallet-standard-wallet-adapter-react": "^1.1.4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@solana/web3.js": "^1.98.0", + "react": "*" + } + }, + "node_modules/@solana/wallet-standard": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard/-/wallet-standard-1.1.4.tgz", + "integrity": "sha512-NF+MI5tOxyvfTU4A+O5idh/gJFmjm52bMwsPpFGRSL79GECSN0XLmpVOO/jqTKJgac2uIeYDpQw/eMaQuWuUXw==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-core": "^1.1.2", + "@solana/wallet-standard-wallet-adapter": "^1.1.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-chains": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-chains/-/wallet-standard-chains-1.1.1.tgz", + "integrity": "sha512-Us3TgL4eMVoVWhuC4UrePlYnpWN+lwteCBlhZDUhFZBJ5UMGh94mYPXno3Ho7+iHPYRtuCi/ePvPcYBqCGuBOw==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-core/-/wallet-standard-core-1.1.2.tgz", + "integrity": "sha512-FaSmnVsIHkHhYlH8XX0Y4TYS+ebM+scW7ZeDkdXo3GiKge61Z34MfBPinZSUMV08hCtzxxqH2ydeU9+q/KDrLA==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-chains": "^1.1.1", + "@solana/wallet-standard-features": "^1.3.0", + "@solana/wallet-standard-util": "^1.1.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-features": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-features/-/wallet-standard-features-1.3.0.tgz", + "integrity": "sha512-ZhpZtD+4VArf6RPitsVExvgkF+nGghd1rzPjd97GmBximpnt1rsUxMOEyoIEuH3XBxPyNB6Us7ha7RHWQR+abg==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-util": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-util/-/wallet-standard-util-1.1.2.tgz", + "integrity": "sha512-rUXFNP4OY81Ddq7qOjQV4Kmkozx4wjYAxljvyrqPx8Ycz0FYChG/hQVWqvgpK3sPsEaO/7ABG1NOACsyAKWNOA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.8.0", + "@solana/wallet-standard-chains": "^1.1.1", + "@solana/wallet-standard-features": "^1.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-wallet-adapter": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter/-/wallet-standard-wallet-adapter-1.1.4.tgz", + "integrity": "sha512-YSBrxwov4irg2hx9gcmM4VTew3ofNnkqsXQ42JwcS6ykF1P1ecVY8JCbrv75Nwe6UodnqeoZRbN7n/p3awtjNQ==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-wallet-adapter-base": "^1.1.4", + "@solana/wallet-standard-wallet-adapter-react": "^1.1.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-wallet-adapter-base": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter-base/-/wallet-standard-wallet-adapter-base-1.1.4.tgz", + "integrity": "sha512-Q2Rie9YaidyFA4UxcUIxUsvynW+/gE2noj/Wmk+IOwDwlVrJUAXCvFaCNsPDSyKoiYEKxkSnlG13OA1v08G4iw==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-adapter-base": "^0.9.23", + "@solana/wallet-standard-chains": "^1.1.1", + "@solana/wallet-standard-features": "^1.3.0", + "@solana/wallet-standard-util": "^1.1.2", + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0", + "@wallet-standard/wallet": "^1.1.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.98.0", + "bs58": "^6.0.0" + } + }, + "node_modules/@solana/wallet-standard-wallet-adapter-react": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter-react/-/wallet-standard-wallet-adapter-react-1.1.4.tgz", + "integrity": "sha512-xa4KVmPgB7bTiWo4U7lg0N6dVUtt2I2WhEnKlIv0jdihNvtyhOjCKMjucWet6KAVhir6I/mSWrJk1U9SvVvhCg==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-wallet-adapter-base": "^1.1.4", + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/wallet-adapter-base": "*", + "react": "*" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.98.4", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.4.tgz", + "integrity": "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@solana/buffer-layout": "^4.0.1", + "@solana/codecs-numbers": "^2.1.0", + "agentkeepalive": "^4.5.0", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.1", + "node-fetch": "^2.7.0", + "rpc-websockets": "^9.0.2", + "superstruct": "^2.0.2" + } + }, + "node_modules/@solana/web3.js/node_modules/@solana/codecs-core": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.3.0.tgz", + "integrity": "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@solana/errors": "2.3.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/web3.js/node_modules/@solana/codecs-numbers": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz", + "integrity": "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@solana/codecs-core": "2.3.0", + "@solana/errors": "2.3.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/web3.js/node_modules/@solana/errors": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.3.0.tgz", + "integrity": "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^5.4.1", + "commander": "^14.0.0" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/web3.js/node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@solana/web3.js/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/@solana/web3.js/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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", + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@solana/web3.js/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20" + } + }, "node_modules/@speed-highlight/core": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz", @@ -8145,6 +9861,15 @@ "license": "CC0-1.0", "peer": true }, + "node_modules/@stripe/stripe-js": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-5.6.0.tgz", + "integrity": "sha512-w8CEY73X/7tw2KKlL3iOk679V9bWseE4GzNz3zlaYxcTjmcmWOathRb0emgo/QQ3eoNzmq68+2Y2gxluAv3xGw==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -8158,6 +9883,25 @@ "string.prototype.matchall": "^4.0.6" } }, + "node_modules/@swc/helpers": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", + "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.87.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.87.4.tgz", + "integrity": "sha512-uNsg6zMxraEPDVO2Bn+F3/ctHi+Zsk+MMpcN8h6P7ozqD088F6mFY5TfGM7zuyIrL7HKpDyu6QHfLWiDxh3cuw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tauri-apps/cli": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.0.tgz", @@ -8491,6 +10235,51 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.28.2" + } + }, "node_modules/@types/brotli": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/@types/brotli/-/brotli-1.3.4.tgz", @@ -8507,6 +10296,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/crypto-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", @@ -8840,6 +10639,43 @@ "integrity": "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==", "license": "MIT" }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/katex": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", @@ -8908,6 +10744,12 @@ "@types/node": "*" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, "node_modules/@types/polylabel": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.1.3.tgz", @@ -8928,6 +10770,13 @@ "license": "MIT", "peer": true }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/stats.js": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", @@ -9015,6 +10864,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT", + "peer": true + }, "node_modules/@types/webxr": { "version": "0.5.24", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", @@ -9022,6 +10878,33 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT", + "peer": true + }, "node_modules/@unhead/vue": { "version": "2.1.12", "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.12.tgz", @@ -9721,6 +11604,92 @@ "license": "MIT", "peer": true }, + "node_modules/@wallet-standard/app": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wallet-standard/app/-/app-1.1.0.tgz", + "integrity": "sha512-3CijvrO9utx598kjr45hTbbeeykQrQfKmSnxeWOgU25TOEpvcipD/bYDQWIqUv1Oc6KK4YStokSMu/FBNecGUQ==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wallet-standard/base/-/base-1.1.0.tgz", + "integrity": "sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@wallet-standard/core/-/core-1.1.1.tgz", + "integrity": "sha512-5Xmjc6+Oe0hcPfVc5n8F77NVLwx1JVAoCVgQpLyv/43/bhtIif+Gx3WUrDlaSDoM8i2kA2xd6YoFbHCxs+e0zA==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/errors": "^0.1.1", + "@wallet-standard/features": "^1.1.0", + "@wallet-standard/wallet": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/errors": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@wallet-standard/errors/-/errors-0.1.1.tgz", + "integrity": "sha512-V8Ju1Wvol8i/VDyQOHhjhxmMVwmKiwyxUZBnHhtiPZJTWY0U/Shb2iEWyGngYEbAkp2sGTmEeNX1tVyGR7PqNw==", + "license": "Apache-2.0", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/errors/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@wallet-standard/features": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wallet-standard/features/-/features-1.1.0.tgz", + "integrity": "sha512-hiEivWNztx73s+7iLxsuD1sOJ28xtRix58W7Xnz4XzzA/pF0+aicnWgjOdA10doVDEDZdUuZCIIqG96SFNlDUg==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/wallet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wallet-standard/wallet/-/wallet-1.1.0.tgz", + "integrity": "sha512-Gt8TnSlDZpAl+RWOOAB/kuvC7RpcdWAlFbHNoi4gsXsfaWa1QCT6LBcfIYTPdOZC9OVZUDwqGuGAcqZejDmHjg==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@webcomponents/shadycss": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.11.2.tgz", @@ -9787,6 +11756,21 @@ "node": ">=18.0.0" } }, + "node_modules/@zxcvbn-ts/core": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-3.0.4.tgz", + "integrity": "sha512-aQeiT0F09FuJaAqNrxynlAwZ2mW/1MdXakKWNmGM1Qp/VaY6CnB/GfnMS2T8gB2231Esp1/maCWd8vTG4OuShw==", + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "1.0.16" + } + }, + "node_modules/@zxcvbn-ts/language-common": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@zxcvbn-ts/language-common/-/language-common-3.0.4.tgz", + "integrity": "sha512-viSNNnRYtc7ULXzxrQIVUNwHAPSXRtoIwy/Tq4XQQdIknBzw4vz36lQLF6mvhMlTIlpjoN/Z1GFu/fwiAlUSsw==", + "license": "MIT" + }, "node_modules/a5-js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/a5-js/-/a5-js-0.5.0.tgz", @@ -9806,6 +11790,27 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -9819,6 +11824,20 @@ "node": ">=6.5" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/accessor-fn": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz", @@ -9860,6 +11879,19 @@ "node": ">= 14" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", @@ -9884,6 +11916,13 @@ "license": "MIT", "peer": true }, + "node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT", + "peer": true + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -9901,7 +11940,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9917,7 +11955,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -9929,8 +11966,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ansis": { "version": "4.2.0", @@ -10370,6 +12406,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT", + "peer": true + }, "node_modules/ast-kit": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", @@ -10513,6 +12556,116 @@ } } }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", @@ -10565,6 +12718,60 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-parser": "0.32.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/balanced-match": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz", @@ -10668,6 +12875,12 @@ "bare-path": "^3.0.0" } }, + "node_modules/base-x": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", + "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==", + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -10751,6 +12964,13 @@ "node": ">= 6" } }, + "node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "license": "MIT", + "peer": true + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -10758,6 +12978,38 @@ "license": "ISC", "peer": true }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/borsh/node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/borsh/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/bowser": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", @@ -10797,6 +13049,16 @@ "base64-js": "^1.1.2" } }, + "node_modules/browser-tabs-lock": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.3.0.tgz", + "integrity": "sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "lodash": ">=4.17.21" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -10830,6 +13092,25 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", + "dependencies": { + "base-x": "^5.0.0" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, "node_modules/buf-compare": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buf-compare/-/buf-compare-1.0.1.tgz", @@ -11033,6 +13314,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -11085,6 +13384,18 @@ "colorbrewer": "1.5.6" } }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -11149,6 +13460,140 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chrome-launcher/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chrome-launcher/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chromium-edge-launcher": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", + "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "node_modules/chromium-edge-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-edge-launcher/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-edge-launcher/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/citty": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", @@ -11237,6 +13682,15 @@ "node": ">=0.8" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -11448,6 +13902,13 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT", + "peer": true + }, "node_modules/confbox": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", @@ -11455,6 +13916,39 @@ "license": "MIT", "peer": true }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", @@ -11990,6 +14484,15 @@ "license": "MIT", "peer": true }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-assert": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/core-assert/-/core-assert-0.2.1.tgz", @@ -12003,6 +14506,17 @@ "node": ">=0.10.0" } }, + "node_modules/core-js": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", + "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.48.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", @@ -12023,6 +14537,31 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -12187,8 +14726,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/crypto-random-string": { "version": "2.0.0", @@ -12442,8 +14980,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/d": { "version": "1.0.2", @@ -13141,6 +15678,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deck.gl": { "version": "9.2.6", "resolved": "https://registry.npmjs.org/deck.gl/-/deck.gl-9.2.6.tgz", @@ -13351,6 +15897,19 @@ "robust-predicates": "^3.0.2" } }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -13375,7 +15934,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -13388,6 +15946,17 @@ "license": "MIT", "peer": true }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -13435,6 +16004,12 @@ "node": ">=0.3.1" } }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -13629,8 +16204,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", @@ -13663,6 +16237,31 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/error-stack-parser-es": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", @@ -13858,6 +16457,23 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT", + "peer": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/es6-symbol": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", @@ -13956,6 +16572,20 @@ "node": ">=0.10" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esri-loader": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/esri-loader/-/esri-loader-3.7.0.tgz", @@ -14009,6 +16639,12 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -14087,6 +16723,13 @@ "node": ">=6" } }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -14103,6 +16746,15 @@ "type": "^2.7.2" } }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "peer": true, + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -14136,7 +16788,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-npm-meta": { @@ -14152,6 +16803,13 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==", + "license": "MIT", + "peer": true + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -14204,6 +16862,22 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "license": "CC0-1.0", + "peer": true + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -14213,6 +16887,29 @@ "reusify": "^1.0.4" } }, + "node_modules/fb-dotslash": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", + "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", + "license": "(MIT OR Apache-2.0)", + "peer": true, + "bin": { + "dotslash": "bin/dotslash" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -14295,6 +16992,94 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "peer": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/flatbuffers": { "version": "25.9.23", "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", @@ -14322,6 +17107,13 @@ "node": ">=12" } }, + "node_modules/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT", + "peer": true + }, "node_modules/focus-trap": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", @@ -14419,6 +17211,13 @@ "node": ">=10" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", + "peer": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -14519,7 +17318,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -14568,6 +17366,16 @@ "dev": true, "license": "ISC" }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-port-please": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", @@ -14690,6 +17498,12 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, "node_modules/global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", @@ -14859,6 +17673,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -14926,12 +17750,45 @@ "node": ">= 0.4" } }, + "node_modules/hermes-compiler": { + "version": "250829098.0.9", + "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.9.tgz", + "integrity": "sha512-hZ5O7PDz1vQ99TS7HD3FJ9zVynfU1y+VWId6U1Pldvd8hmAYrNec/XLPYJKD3dLOW6NXak6aAQAuMuSo3ji0tQ==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-estree": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.32.0" + } + }, "node_modules/hls.js": { "version": "1.6.15", "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==", "license": "Apache-2.0" }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", @@ -15030,6 +17887,16 @@ "node": ">=16.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/i18next": { "version": "25.8.10", "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.10.tgz", @@ -15089,6 +17956,12 @@ "dev": true, "license": "ISC" }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "license": "Apache-2.0" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -15143,6 +18016,31 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "license": "MIT" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/impound": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/impound/-/impound-1.1.5.tgz", @@ -15175,6 +18073,18 @@ "node": ">=12" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "peer": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -15187,6 +18097,16 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/input-otp": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", + "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/interactjs": { "version": "1.10.27", "resolved": "https://registry.npmjs.org/interactjs/-/interactjs-1.10.27.tgz", @@ -15221,6 +18141,16 @@ "node": ">=12" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ioredis": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.0.tgz", @@ -15516,7 +18446,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -15680,6 +18609,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -15906,6 +18845,68 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/jackspeak": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", @@ -15939,6 +18940,363 @@ "node": ">=10" } }, + "node_modules/jayson": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.3.0.tgz", + "integrity": "sha512-AauzHcUcqs8OBnCHOkJY280VaTiCm57AbuO7lqzcw7JapGj50BisE3xhksye4zlTSR1+1tAz67wLTl8tEH1obQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "stream-json": "^1.9.1", + "uuid": "^8.3.2", + "ws": "^7.5.10" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "license": "MIT", + "peer": true + }, + "node_modules/jayson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT", + "peer": true + }, + "node_modules/jayson/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -15949,6 +19307,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/jpeg-exif": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", @@ -15957,6 +19324,21 @@ "license": "MIT", "peer": true }, + "node_modules/js-base64": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", + "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "license": "BSD-3-Clause" + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -15983,6 +19365,13 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/jsc-safe-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD", + "peer": true + }, "node_modules/jsep": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", @@ -16004,6 +19393,12 @@ "node": ">=6" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -16037,6 +19432,13 @@ "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC", + "peer": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -16197,7 +19599,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -16212,6 +19613,34 @@ "immediate": "~3.0.5" } }, + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -16225,6 +19654,12 @@ "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/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -16335,6 +19770,18 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lodash": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", @@ -16382,6 +19829,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT", + "peer": true + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -16398,6 +19852,19 @@ "node": ">=0.6" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -16530,6 +19997,16 @@ "source-map-js": "^1.2.1" } }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, "node_modules/maplibre-gl": { "version": "5.16.0", "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.16.0.tgz", @@ -16709,6 +20186,13 @@ "license": "SEE LICENSE IN LICENSE", "peer": true }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -16743,6 +20227,26 @@ "dev": true, "license": "MIT" }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT", + "peer": true + }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -16775,6 +20279,427 @@ "dev": true, "license": "MIT" }, + "node_modules/metro": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.5.tgz", + "integrity": "sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.33.3", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.83.5", + "metro-cache": "0.83.5", + "metro-cache-key": "0.83.5", + "metro-config": "0.83.5", + "metro-core": "0.83.5", + "metro-file-map": "0.83.5", + "metro-resolver": "0.83.5", + "metro-runtime": "0.83.5", + "metro-source-map": "0.83.5", + "metro-symbolicate": "0.83.5", + "metro-transform-plugins": "0.83.5", + "metro-transform-worker": "0.83.5", + "mime-types": "^3.0.1", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.5.tgz", + "integrity": "sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.33.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", + "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", + "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.33.3" + } + }, + "node_modules/metro-cache": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.5.tgz", + "integrity": "sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng==", + "license": "MIT", + "peer": true, + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.83.5" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache-key": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.5.tgz", + "integrity": "sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-config": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.5.tgz", + "integrity": "sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w==", + "license": "MIT", + "peer": true, + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.83.5", + "metro-cache": "0.83.5", + "metro-core": "0.83.5", + "metro-runtime": "0.83.5", + "yaml": "^2.6.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-core": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.5.tgz", + "integrity": "sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.83.5" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-file-map": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.5.tgz", + "integrity": "sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.5.tgz", + "integrity": "sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-resolver": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.5.tgz", + "integrity": "sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-runtime": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.5.tgz", + "integrity": "sha512-f+b3ue9AWTVlZe2Xrki6TAoFtKIqw30jwfk7GQ1rDUBQaE0ZQ+NkiMEtb9uwH7uAjJ87U7Tdx1Jg1OJqUfEVlA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-source-map": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.5.tgz", + "integrity": "sha512-VT9bb2KO2/4tWY9Z2yeZqTUao7CicKAOps9LUg2aQzsz+04QyuXL3qgf1cLUVRjA/D6G5u1RJAlN1w9VNHtODQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.83.5", + "nullthrows": "^1.1.1", + "ob1": "0.83.5", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-source-map/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.5.tgz", + "integrity": "sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.83.5", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-symbolicate/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.5.tgz", + "integrity": "sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.5.tgz", + "integrity": "sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "metro": "0.83.5", + "metro-babel-transformer": "0.83.5", + "metro-cache": "0.83.5", + "metro-cache-key": "0.83.5", + "metro-minify-terser": "0.83.5", + "metro-source-map": "0.83.5", + "metro-transform-plugins": "0.83.5", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/hermes-estree": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", + "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", + "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.33.3" + } + }, + "node_modules/metro/node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "peer": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/metro/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -17452,6 +21377,19 @@ "integrity": "sha512-siX3YCG7N2HnmN1xMH3cK4JkUZJhbkhRFJL+G5N1vH0mh1t5088rJknIoqDFWDIU6NPGvRRgLnYW3ZHjSMEBLA==", "license": "MIT" }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -17578,6 +21516,16 @@ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -17854,6 +21802,13 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT", + "peer": true + }, "node_modules/node-localstorage": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz", @@ -17947,6 +21902,13 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT", + "peer": true + }, "node_modules/nuxt": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.3.1.tgz", @@ -18060,6 +22022,19 @@ "node": ">=18" } }, + "node_modules/ob1": { + "version": "0.83.5", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.5.tgz", + "integrity": "sha512-vNKPYC8L5ycVANANpF/S+WZHpfnRWKx/F3AYP4QMn6ZJTh+l2HOrId0clNkEmua58NB9vmI9Qh7YOoV/4folYg==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -18354,6 +22329,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ox": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz", + "integrity": "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/oxc-minify": { "version": "0.112.0", "resolved": "https://registry.npmjs.org/oxc-minify/-/oxc-minify-0.112.0.tgz", @@ -18475,6 +22491,42 @@ "oxc-parser": ">=0.98.0" } }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -18493,6 +22545,18 @@ "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", "license": "MIT" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -18513,6 +22577,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -18529,6 +22611,15 @@ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "license": "MIT" }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-expression-matcher": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", @@ -18544,6 +22635,16 @@ "node": ">=14.0.0" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -18586,6 +22687,15 @@ "node": "20 || >=22" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -18664,6 +22774,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "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", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-types": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", @@ -18750,6 +22870,15 @@ "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==", "peer": true }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/point-in-polygon-hao": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", @@ -19397,6 +23526,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT", + "peer": true + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -19413,6 +23577,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, "node_modules/protobufjs": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", @@ -19479,6 +23653,133 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode.react": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", + "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/qrcode/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/quadbin": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/quadbin/-/quadbin-0.4.2.tgz", @@ -19508,6 +23809,16 @@ "license": "MIT", "peer": true }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -19587,6 +23898,39 @@ "node": ">=0.10.0" } }, + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-devtools-core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", @@ -19600,6 +23944,130 @@ "react": "^19.2.3" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-native": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.84.1.tgz", + "integrity": "sha512-0PjxOyXRu3tZ8EobabxSukvhKje2HJbsZikR0U+pvS0pYZza2hXKjcSBiBdFN4h9D0S3v6a8kkrDK6WTRKMwzg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@react-native/assets-registry": "0.84.1", + "@react-native/codegen": "0.84.1", + "@react-native/community-cli-plugin": "0.84.1", + "@react-native/gradle-plugin": "0.84.1", + "@react-native/js-polyfills": "0.84.1", + "@react-native/normalize-colors": "0.84.1", + "@react-native/virtualized-lists": "0.84.1", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "0.32.0", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "hermes-compiler": "250829098.0.9", + "invariant": "^2.2.4", + "jest-environment-node": "^29.7.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.83.3", + "metro-source-map": "^0.83.3", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.27.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.15", + "whatwg-fetch": "^3.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.1", + "react": "^19.2.3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/react-native/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "peer": true + }, + "node_modules/react-native/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -19741,6 +24209,12 @@ "node": ">=4" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -19814,7 +24288,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -19829,6 +24302,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -19888,6 +24367,76 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "peer": true + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/robust-predicates": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", @@ -19986,6 +24535,94 @@ "license": "MIT", "peer": true }, + "node_modules/rpc-websockets": { + "version": "9.3.6", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.3.6.tgz", + "integrity": "sha512-RzuOQDGd+EtR/cBYQAH/0jjaBzhyvXXGROhxigGJPf+q3XKyvtelZCucylzxiq5MaGlfBx1075djTsxFsFDgrA==", + "license": "LGPL-3.0-only", + "peer": true, + "dependencies": { + "@swc/helpers": "^0.5.11", + "@types/uuid": "^10.0.0", + "@types/ws": "^8.2.2", + "buffer": "^6.0.3", + "eventemitter3": "^5.0.1", + "uuid": "^11.0.0", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^6.0.0" + } + }, + "node_modules/rpc-websockets/node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/rpc-websockets/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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", + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/rpc-websockets/node_modules/utf-8-validate": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.6.tgz", + "integrity": "sha512-q3l3P9UtEEiAHcsgsqTgf9PPjctrDWoIXW3NpOHFdRDbLvu4DLIcxHangJ4RLrWkBcKjmcs/6NkerI8T/rE4LA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/rpc-websockets/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/run-applescript": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", @@ -20185,6 +24822,16 @@ "url": "https://opencollective.com/express" } }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/serialize-javascript": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", @@ -20234,6 +24881,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -20714,6 +25367,59 @@ "node": ">=20.16.0" } }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT", + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -20735,8 +25441,7 @@ "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", @@ -20758,6 +25463,23 @@ "integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==", "license": "MIT" }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/stream-json": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "stream-chain": "^2.2.5" + } + }, "node_modules/streamx": { "version": "2.23.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", @@ -21080,6 +25802,12 @@ "postcss": "^8.4.32" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/supercluster": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", @@ -21089,6 +25817,16 @@ "kdbush": "^4.0.2" } }, + "node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", @@ -21157,6 +25895,19 @@ "node": ">=16" } }, + "node_modules/swr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz", + "integrity": "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/system-architecture": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", @@ -21174,8 +25925,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tagged-tag": { "version": "1.0.0", @@ -21374,6 +26124,74 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "peer": true + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-decoder": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", @@ -21383,6 +26201,12 @@ "b4a": "^1.6.4" } }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==", + "peer": true + }, "node_modules/texture-compressor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/texture-compressor/-/texture-compressor-1.0.2.tgz", @@ -21519,6 +26343,13 @@ "three": ">=0.154" } }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT", + "peer": true + }, "node_modules/timezone-groups": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/timezone-groups/-/timezone-groups-0.10.4.tgz", @@ -21591,6 +26422,13 @@ "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", "license": "ISC" }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -21603,6 +26441,12 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -21712,6 +26556,16 @@ "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", "license": "ISC" }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -21823,7 +26677,6 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -22134,6 +26987,16 @@ "node": ">= 10.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unplugin": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz", @@ -22467,6 +27330,15 @@ "license": "MIT", "peer": true }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -22486,6 +27358,134 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/viem": { + "version": "2.47.6", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.47.6.tgz", + "integrity": "sha512-zExmbI99NGvMdYa7fmqSTLgkwh48dmhgEqFrUgkpL4kfG4XkVefZ8dZqIKVUhZo6Uhf0FrrEXOsHm9LUyIvI2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.14.7", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/ox": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.14.7.tgz", + "integrity": "sha512-zSQ/cfBdolj7U4++NAvH7sI+VG0T3pEohITCgcQj8KlawvTDY4vGVhDT64Atsm0d6adWfIYHDpu88iUBMMp+AQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", @@ -23429,6 +28429,13 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT", + "peer": true + }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", @@ -23491,6 +28498,16 @@ "vue": "^3.5.0" } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -23543,6 +28560,13 @@ "integrity": "sha512-BQWBIsOn411M+ffBxmA6QRLvAOVbuz3Uk4NusxnqC1H7aeQcVLhdA3k2k/EFFFtqVjhz3z7JOOZF1a9hj2tv4Q==", "license": "MIT" }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT", + "peer": true + }, "node_modules/whatwg-url": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", @@ -23644,6 +28668,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.20", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", @@ -24430,6 +29460,35 @@ "integrity": "sha512-v3fyjpK8S/dpY/X5WxqTK3IoCnp/ZOLxn144GZVlNUjtwAchzrVo03h+oMATFhCIiJ5KTr4V3vDQQYz4RU684g==", "license": "MIT", "optional": true + }, + "node_modules/zustand": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz", + "integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==", + "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 + } + } } } } diff --git a/package.json b/package.json index 19d906aae..4a0f3daa6 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "dependencies": { "@anthropic-ai/sdk": "^0.79.0", "@aws-sdk/client-s3": "^3.1009.0", + "@clerk/clerk-js": "^5.56.0", "@deck.gl/aggregation-layers": "^9.2.6", "@deck.gl/core": "^9.2.6", "@deck.gl/geo-layers": "^9.2.6", @@ -109,6 +110,7 @@ "fast-xml-parser": "^5.3.7", "globe.gl": "^2.45.0", "hls.js": "^1.6.15", + "jose": "^6.0.11", "i18next": "^25.8.10", "i18next-browser-languagedetector": "^8.2.1", "maplibre-gl": "^5.16.0", diff --git a/scripts/vercel-ignore.sh b/scripts/vercel-ignore.sh index f174f5969..a86c6b5a1 100755 --- a/scripts/vercel-ignore.sh +++ b/scripts/vercel-ignore.sh @@ -6,7 +6,7 @@ if [ "$VERCEL_GIT_COMMIT_REF" = "main" ] && [ -n "$VERCEL_GIT_PREVIOUS_SHA" ]; then git cat-file -e "$VERCEL_GIT_PREVIOUS_SHA" 2>/dev/null && { WEB_CHANGES=$(git diff --name-only "$VERCEL_GIT_PREVIOUS_SHA" HEAD -- \ - 'src/' 'api/' 'server/' 'shared/' 'public/' 'blog-site/' 'pro-test/' 'proto/' \ + 'src/' 'api/' 'server/' 'shared/' 'public/' 'blog-site/' 'pro-test/' 'proto/' 'convex/' \ 'package.json' 'package-lock.json' 'vite.config.ts' 'tsconfig.json' \ 'tsconfig.api.json' 'vercel.json' 'middleware.ts' | head -1) [ -z "$WEB_CHANGES" ] && echo "Skipping: no web-relevant changes on main" && exit 0 @@ -35,6 +35,7 @@ git diff --name-only "$COMPARE_SHA" HEAD -- \ 'blog-site/' \ 'pro-test/' \ 'proto/' \ + 'convex/' \ 'package.json' \ 'package-lock.json' \ 'vite.config.ts' \ diff --git a/server/auth-session.ts b/server/auth-session.ts new file mode 100644 index 000000000..e18132b49 --- /dev/null +++ b/server/auth-session.ts @@ -0,0 +1,61 @@ +/** + * Server-side session validation for the Vercel edge gateway. + * + * Validates Clerk-issued bearer tokens using local JWT verification + * with jose + cached JWKS. No Convex round-trip needed. + * + * This module must NOT import anything from `src/` -- it runs in the + * Vercel edge runtime, not the browser. + */ + +import { createRemoteJWKSet, jwtVerify } from 'jose'; + +// Clerk JWT issuer domain -- set in Vercel env vars +const CLERK_JWT_ISSUER_DOMAIN = process.env.CLERK_JWT_ISSUER_DOMAIN ?? ''; + +// Module-scope JWKS resolver -- cached across warm invocations. +// jose handles key rotation and caching internally. +let _jwks: ReturnType | null = null; +function getJWKS() { + if (!_jwks && CLERK_JWT_ISSUER_DOMAIN) { + const jwksUrl = new URL('/.well-known/jwks.json', CLERK_JWT_ISSUER_DOMAIN); + _jwks = createRemoteJWKSet(jwksUrl); + } + return _jwks; +} + +export interface SessionResult { + valid: boolean; + userId?: string; + role?: 'free' | 'pro'; +} + +/** + * Validate a Clerk-issued bearer token using local JWKS verification. + * Extracts `sub` (user ID) and `plan` (entitlement) from verified claims. + * Fails closed: invalid/expired/unverifiable tokens return { valid: false }. + */ +export async function validateBearerToken(token: string): Promise { + const jwks = getJWKS(); + if (!jwks) return { valid: false }; + + try { + const { payload } = await jwtVerify(token, jwks, { + issuer: CLERK_JWT_ISSUER_DOMAIN, + audience: 'convex', + algorithms: ['RS256'], + }); + + const userId = payload.sub; + if (!userId) return { valid: false }; + + // Normalize plan claim -- unknown/missing = free (never pro) + const rawPlan = (payload as Record).plan; + const role: 'free' | 'pro' = rawPlan === 'pro' ? 'pro' : 'free'; + + return { valid: true, userId, role }; + } catch { + // Signature verification failed, expired, wrong issuer, etc. + return { valid: false }; + } +} diff --git a/server/gateway.ts b/server/gateway.ts index 127fe17cb..a9cb59a14 100644 --- a/server/gateway.ts +++ b/server/gateway.ts @@ -189,14 +189,7 @@ const RPC_CACHE_TIER: Record = { '/api/economic/v1/get-economic-calendar': 'slow', }; -// TODO(payment-pr): PREMIUM_RPC_PATHS is intentionally empty until the payment/pro-user -// system is implemented. The original set of stock analysis paths used forceKey=true, -// which broke web pro users because isTrustedBrowserOrigin() is header-only (Origin can be -// spoofed) and the web client has no mechanism to forward a server-validated entitlement. -// When the payment PR lands, re-populate this set and have the web client send a -// server-validated pro token (e.g. X-WorldMonitor-Key) so the entitlement check is -// meaningful. Until then, access is gated client-side by isProUser() + WORLDMONITOR_API_KEY. -const PREMIUM_RPC_PATHS = new Set(); +import { PREMIUM_RPC_PATHS } from '../src/shared/premium-paths'; /** * Creates a Vercel Edge handler for a single domain's routes. @@ -234,15 +227,41 @@ export function createDomainGateway( return new Response(null, { status: 204, headers: corsHeaders }); } - // API key validation (origin-aware) + // API key validation const keyCheck = validateApiKey(request, { forceKey: PREMIUM_RPC_PATHS.has(pathname), }); if (keyCheck.required && !keyCheck.valid) { - return new Response(JSON.stringify({ error: keyCheck.error }), { - status: 401, - headers: { 'Content-Type': 'application/json', ...corsHeaders }, - }); + if (PREMIUM_RPC_PATHS.has(pathname)) { + const authHeader = request.headers.get('Authorization'); + if (authHeader?.startsWith('Bearer ')) { + const { validateBearerToken } = await import('./auth-session'); + const session = await validateBearerToken(authHeader.slice(7)); + if (!session.valid) { + return new Response(JSON.stringify({ error: 'Invalid or expired session' }), { + status: 401, + headers: { 'Content-Type': 'application/json', ...corsHeaders }, + }); + } + if (session.role !== 'pro') { + return new Response(JSON.stringify({ error: 'Pro subscription required' }), { + status: 403, + headers: { 'Content-Type': 'application/json', ...corsHeaders }, + }); + } + // Valid pro session — fall through to route handling + } else { + return new Response(JSON.stringify({ error: keyCheck.error }), { + status: 401, + headers: { 'Content-Type': 'application/json', ...corsHeaders }, + }); + } + } else { + return new Response(JSON.stringify({ error: keyCheck.error }), { + status: 401, + headers: { 'Content-Type': 'application/json', ...corsHeaders }, + }); + } } // IP-based rate limiting — two-phase: endpoint-specific first, then global fallback diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 8479b3cbd..e7dcbf196 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ } ], "security": { - "csp": "default-src 'self'; connect-src 'self' https: http://localhost:5173 http://127.0.0.1:* ws: wss: blob: data: https://abacus.worldmonitor.app; img-src 'self' data: blob: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'wasm-unsafe-eval' https://www.youtube.com https://abacus.worldmonitor.app; worker-src 'self' blob:; font-src 'self' data: https:; media-src 'self' data: blob: https: http://127.0.0.1:* http://localhost:*; frame-src 'self' http://127.0.0.1:* http://localhost:* https://worldmonitor.app https://tech.worldmonitor.app https://finance.worldmonitor.app https://commodity.worldmonitor.app https://happy.worldmonitor.app https://www.youtube.com https://www.youtube-nocookie.com https://webcams.windy.com;" + "csp": "default-src 'self'; connect-src 'self' https: http://localhost:5173 http://127.0.0.1:* ws: wss: blob: data: https://abacus.worldmonitor.app; img-src 'self' data: blob: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'wasm-unsafe-eval' https://www.youtube.com https://abacus.worldmonitor.app https://*.clerk.accounts.dev; worker-src 'self' blob:; font-src 'self' data: https:; media-src 'self' data: blob: https: http://127.0.0.1:* http://localhost:*; frame-src 'self' http://127.0.0.1:* http://localhost:* https://worldmonitor.app https://tech.worldmonitor.app https://finance.worldmonitor.app https://commodity.worldmonitor.app https://happy.worldmonitor.app https://www.youtube.com https://www.youtube-nocookie.com https://webcams.windy.com https://*.clerk.accounts.dev;" } }, "bundle": { diff --git a/src/App.ts b/src/App.ts index 937ce7965..72eb2f77b 100644 --- a/src/App.ts +++ b/src/App.ts @@ -16,6 +16,7 @@ import { import { sanitizeLayersForVariant } from '@/config/map-layer-definitions'; import type { MapVariant } from '@/config/map-layer-definitions'; import { initDB, cleanOldSnapshots, isAisConfigured, initAisStream, isOutagesConfigured, disconnectAisStream } from '@/services'; +import { isProUser } from '@/services/widget-store'; import { mlWorker } from '@/services/ml-worker'; import { getAiFlowSettings, subscribeAiFlowChange, isHeadlineMemoryEnabled } from '@/services/ai-flow-settings'; import { startLearning } from '@/services/country-instability'; @@ -44,9 +45,9 @@ import type { EconomicCalendarPanel } from '@/components/EconomicCalendarPanel'; import type { CotPositioningPanel } from '@/components/CotPositioningPanel'; import { isDesktopRuntime, waitForSidecarReady } from '@/services/runtime'; import { getSecretState } from '@/services/runtime-config'; -import { isProUser } from '@/services/widget-store'; +import { getAuthState } from '@/services/auth-state'; import { BETA_MODE } from '@/config/beta'; -import { trackEvent, trackDeeplinkOpened } from '@/services/analytics'; +import { trackEvent, trackDeeplinkOpened, initAuthAnalytics } from '@/services/analytics'; import { preloadCountryGeometry, getCountryNameByCode } from '@/services/country-geometry'; import { initI18n, t } from '@/services/i18n'; @@ -62,6 +63,7 @@ import { DataLoaderManager } from '@/app/data-loader'; import { EventHandlerManager } from '@/app/event-handlers'; import { resolveUserRegion, resolvePreciseUserCoordinates, type PreciseCoordinates } from '@/utils/user-location'; import { showProBanner } from '@/components/ProBanner'; +import { initAuthState, subscribeAuthState } from '@/services/auth-state'; import { CorrelationEngine, militaryAdapter, @@ -91,6 +93,7 @@ export class App { private modules: { destroy(): void }[] = []; private unsubAiFlow: (() => void) | null = null; + private unsubFreeTier: (() => void) | null = null; private visiblePanelPrimed = new Set(); private visiblePanelPrimeRaf: number | null = null; private bootstrapHydrationState: BootstrapHydrationState = getBootstrapHydrationState(); @@ -330,7 +333,7 @@ export class App { primeTask('crossSourceSignals', () => this.dataLoader.loadCrossSourceSignals()); } - const _wmAccess = getSecretState('WORLDMONITOR_API_KEY').present || isProUser(); + const _wmAccess = getSecretState('WORLDMONITOR_API_KEY').present || getAuthState().user?.role === 'pro'; if (_wmAccess) { if (shouldPrime('stock-analysis')) { primeTask('stockAnalysis', () => this.dataLoader.loadStockAnalysis()); @@ -565,31 +568,6 @@ export class App { } } - // Enforce free-tier panel limit on every launch (handles legacy/downgraded users). - if (!isProUser()) { - // cw-* (custom widget) panels are not loaded for free users — disable them so they - // don't silently consume quota slots that count against visible standard panels. - let cwDisabled = false; - for (const key of Object.keys(panelSettings)) { - if (key.startsWith('cw-') && panelSettings[key]?.enabled) { - panelSettings[key] = { ...panelSettings[key]!, enabled: false }; - cwDisabled = true; - } - } - const enabledKeys = Object.entries(panelSettings) - .filter(([k, v]) => v.enabled && !k.startsWith('cw-')) - .sort(([ka, a], [kb, b]) => (a.priority ?? 99) - (b.priority ?? 99) || ka.localeCompare(kb)) - .map(([k]) => k); - const needsTrim = enabledKeys.length > FREE_MAX_PANELS; - if (needsTrim) { - for (const key of enabledKeys.slice(FREE_MAX_PANELS)) { - panelSettings[key] = { ...panelSettings[key]!, enabled: false }; - } - console.log(`[App] Free tier: trimmed ${enabledKeys.length - FREE_MAX_PANELS} panel(s) to enforce ${FREE_MAX_PANELS}-panel limit`); - } - if (cwDisabled || needsTrim) saveToStorage(STORAGE_KEYS.panels, panelSettings); - } - const initialUrlState: ParsedMapUrlState | null = parseMapUrlState(window.location.search, mapLayers); if (initialUrlState.layers) { mapLayers = sanitizeLayersForVariant(initialUrlState.layers, currentVariant as MapVariant); @@ -625,26 +603,6 @@ export class App { const disabledSources = new Set(loadFromStorage(STORAGE_KEYS.disabledFeeds, [])); - // Enforce free-tier source limit on every launch (handles legacy/downgraded users). - if (!isProUser()) { - const allSourceNames = (() => { - const s = new Set(); - Object.values(FEEDS).forEach(feeds => feeds?.forEach(f => s.add(f.name))); - INTEL_SOURCES.forEach(f => s.add(f.name)); - return Array.from(s).sort((a, b) => a.localeCompare(b)); - })(); - const currentlyEnabled = allSourceNames.filter(n => !disabledSources.has(n)); - const enabledCount = currentlyEnabled.length; - if (enabledCount > FREE_MAX_SOURCES) { - const toDisable = enabledCount - FREE_MAX_SOURCES; - for (const name of currentlyEnabled.slice(FREE_MAX_SOURCES)) { - disabledSources.add(name); - } - saveToStorage(STORAGE_KEYS.disabledFeeds, Array.from(disabledSources)); - console.log(`[App] Free tier: disabled ${toDisable} source(s) to enforce ${FREE_MAX_SOURCES}-source limit`); - } - } - // Build shared state object this.state = { map: null, @@ -688,6 +646,8 @@ export class App { digestPanel: null, speciesPanel: null, renewablePanel: null, + authModal: null, + authHeaderWidget: null, tvMode: null, happyAllItems: [], isDestroyed: false, @@ -811,6 +771,15 @@ export class App { await fetchBootstrapData(); this.bootstrapHydrationState = getBootstrapHydrationState(); + // Verify OAuth OTT and hydrate auth session BEFORE any UI subscribes to auth state + if (isProUser()) { + await initAuthState(); + initAuthAnalytics(); + } + this.enforceFreeTierLimits(); + this.unsubFreeTier = subscribeAuthState(() => { this.enforceFreeTierLimits(); }); + + const geoCoordsPromise: Promise = this.state.isMobile && this.state.initialUrlState?.lat === undefined && this.state.initialUrlState?.lon === undefined ? resolvePreciseUserCoordinates(5000) @@ -876,6 +845,7 @@ export class App { correlationEngine.registerAdapter(disasterAdapter); this.state.correlationEngine = correlationEngine; this.eventHandlers.setupUnifiedSettings(); + if (isProUser()) this.eventHandlers.setupAuthWidget(); // Phase 4: SearchManager, MapLayerHandlers, CountryIntel this.searchManager.init(); @@ -958,6 +928,56 @@ export class App { this.eventHandlers.setupPanelViewTracking(); } + /** + * Enforce free-tier panel and source limits. + * Reads current values from storage, trims if necessary, and saves back. + * Safe to call multiple times (idempotent) — e.g. on auth state changes. + */ + private enforceFreeTierLimits(): void { + if (isProUser()) return; + + // --- Panel limit --- + const panelSettings = loadFromStorage>(STORAGE_KEYS.panels, {}); + let cwDisabled = false; + for (const key of Object.keys(panelSettings)) { + if (key.startsWith('cw-') && panelSettings[key]?.enabled) { + panelSettings[key] = { ...panelSettings[key]!, enabled: false }; + cwDisabled = true; + } + } + const enabledKeys = Object.entries(panelSettings) + .filter(([k, v]) => v.enabled && !k.startsWith('cw-')) + .sort(([ka, a], [kb, b]) => (a.priority ?? 99) - (b.priority ?? 99) || ka.localeCompare(kb)) + .map(([k]) => k); + const needsTrim = enabledKeys.length > FREE_MAX_PANELS; + if (needsTrim) { + for (const key of enabledKeys.slice(FREE_MAX_PANELS)) { + panelSettings[key] = { ...panelSettings[key]!, enabled: false }; + } + console.log(`[App] Free tier: trimmed ${enabledKeys.length - FREE_MAX_PANELS} panel(s) to enforce ${FREE_MAX_PANELS}-panel limit`); + } + if (cwDisabled || needsTrim) saveToStorage(STORAGE_KEYS.panels, panelSettings); + + // --- Source limit --- + const disabledSources = new Set(loadFromStorage(STORAGE_KEYS.disabledFeeds, [])); + const allSourceNames = (() => { + const s = new Set(); + Object.values(FEEDS).forEach(feeds => feeds?.forEach(f => s.add(f.name))); + INTEL_SOURCES.forEach(f => s.add(f.name)); + return Array.from(s).sort((a, b) => a.localeCompare(b)); + })(); + const currentlyEnabled = allSourceNames.filter(n => !disabledSources.has(n)); + const enabledCount = currentlyEnabled.length; + if (enabledCount > FREE_MAX_SOURCES) { + const toDisable = enabledCount - FREE_MAX_SOURCES; + for (const name of currentlyEnabled.slice(FREE_MAX_SOURCES)) { + disabledSources.add(name); + } + saveToStorage(STORAGE_KEYS.disabledFeeds, Array.from(disabledSources)); + console.log(`[App] Free tier: disabled ${toDisable} source(s) to enforce ${FREE_MAX_SOURCES}-source limit`); + } + } + public destroy(): void { this.state.isDestroyed = true; window.removeEventListener('scroll', this.handleViewportPrime); @@ -976,6 +996,7 @@ export class App { // Clean up subscriptions, map, AIS, and breaking news this.unsubAiFlow?.(); + this.unsubFreeTier?.(); this.state.breakingBanner?.destroy(); destroyBreakingNewsAlerts(); this.cachedModeBannerEl?.remove(); @@ -1074,19 +1095,19 @@ export class App { 'stock-analysis', () => this.dataLoader.loadStockAnalysis(), REFRESH_INTERVALS.stockAnalysis, - () => (getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) && this.isPanelNearViewport('stock-analysis'), + () => (getSecretState('WORLDMONITOR_API_KEY').present || getAuthState().user?.role === 'pro') && this.isPanelNearViewport('stock-analysis'), ); this.refreshScheduler.scheduleRefresh( 'daily-market-brief', () => this.dataLoader.loadDailyMarketBrief(), REFRESH_INTERVALS.dailyMarketBrief, - () => (getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) && this.isPanelNearViewport('daily-market-brief'), + () => (getSecretState('WORLDMONITOR_API_KEY').present || getAuthState().user?.role === 'pro') && this.isPanelNearViewport('daily-market-brief'), ); this.refreshScheduler.scheduleRefresh( 'stock-backtest', () => this.dataLoader.loadStockBacktest(), REFRESH_INTERVALS.stockBacktest, - () => (getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) && this.isPanelNearViewport('stock-backtest'), + () => (getSecretState('WORLDMONITOR_API_KEY').present || getAuthState().user?.role === 'pro') && this.isPanelNearViewport('stock-backtest'), ); this.refreshScheduler.scheduleRefresh( 'market-implications', diff --git a/src/app/app-context.ts b/src/app/app-context.ts index 6ada9423d..dd7628998 100644 --- a/src/app/app-context.ts +++ b/src/app/app-context.ts @@ -74,6 +74,8 @@ export interface AppContext { digestPanel: import('@/components/GoodThingsDigestPanel').GoodThingsDigestPanel | null; speciesPanel: import('@/components/SpeciesComebackPanel').SpeciesComebackPanel | null; renewablePanel: import('@/components/RenewableEnergyPanel').RenewableEnergyPanel | null; + authModal: { open(): void; close(): void; destroy(): void } | null; + authHeaderWidget: import('@/components/AuthHeaderWidget').AuthHeaderWidget | null; tvMode: import('@/services/tv-mode').TvModeController | null; happyAllItems: NewsItem[]; isDestroyed: boolean; diff --git a/src/app/data-loader.ts b/src/app/data-loader.ts index 944e137c1..f3cc87a7e 100644 --- a/src/app/data-loader.ts +++ b/src/app/data-loader.ts @@ -114,7 +114,14 @@ import { fetchOrefAlerts, startOrefPolling, stopOrefPolling, onOrefAlertsUpdate import { enrichEventsWithExposure } from '@/services/population-exposure'; import { debounce, getCircuitBreakerCooldownInfo } from '@/utils'; import { getSecretState, isFeatureAvailable, isFeatureEnabled } from '@/services/runtime-config'; -import { isProUser } from '@/services/widget-store'; +import { getAuthState } from '@/services/auth-state'; + +/** True when the user has premium data access — desktop API key OR web Clerk Pro. */ +function hasPremiumAccess(): boolean { + if (getSecretState('WORLDMONITOR_API_KEY').present) return true; + if (getAuthState().user?.role === 'pro') return true; + return false; +} import { isDesktopRuntime, toApiUrl } from '@/services/runtime'; import { getAiFlowSettings } from '@/services/ai-flow-settings'; import { t, getCurrentLanguage } from '@/services/i18n'; @@ -255,7 +262,7 @@ export class DataLoaderManager implements AppModule { init(): void { this.boundMarketWatchlistHandler = () => { void this.loadMarkets().then(async () => { - if (getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) { + if (hasPremiumAccess()) { await this.loadStockAnalysis(); await this.loadStockBacktest(); await this.loadDailyMarketBrief(true); @@ -388,14 +395,15 @@ export class DataLoaderManager implements AppModule { if (shouldLoadAny(['markets', 'heatmap', 'commodities', 'crypto', 'energy-complex', 'crypto-heatmap', 'defi-tokens', 'ai-tokens', 'other-tokens'])) { tasks.push({ name: 'markets', task: runGuarded('markets', () => this.loadMarkets()) }); } - // TODO(payment-pr): isProUser() checks localStorage/cookie only — not server-validated. - // Replace with a server-verified entitlement check once the payment system is in place. - if ((getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) && shouldLoad('stock-analysis')) { + if (hasPremiumAccess() && shouldLoad('stock-analysis')) { tasks.push({ name: 'stockAnalysis', task: runGuarded('stockAnalysis', () => this.loadStockAnalysis()) }); } - if ((getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) && shouldLoad('stock-backtest')) { + if (hasPremiumAccess() && shouldLoad('stock-backtest')) { tasks.push({ name: 'stockBacktest', task: runGuarded('stockBacktest', () => this.loadStockBacktest()) }); } + if (hasPremiumAccess() && shouldLoad('daily-market-brief')) { + tasks.push({ name: 'dailyMarketBrief', task: runGuarded('dailyMarketBrief', () => this.loadDailyMarketBrief()) }); + } if (shouldLoad('polymarket')) { tasks.push({ name: 'predictions', task: runGuarded('predictions', () => this.loadPredictions()) }); } @@ -541,7 +549,7 @@ export class DataLoaderManager implements AppModule { this.updateSearchIndex(); - if (getSecretState('WORLDMONITOR_API_KEY').present || isProUser()) { + if (hasPremiumAccess()) { await Promise.allSettled([ this.loadDailyMarketBrief(), this.loadMarketImplications(), @@ -1387,7 +1395,7 @@ export class DataLoaderManager implements AppModule { } async loadDailyMarketBrief(force = false): Promise { - if (!getSecretState('WORLDMONITOR_API_KEY').present && !isProUser()) return; + if (!hasPremiumAccess()) return; if (this.ctx.isDestroyed || this.ctx.inFlight.has('dailyMarketBrief')) return; this.ctx.inFlight.add('dailyMarketBrief'); @@ -1536,7 +1544,7 @@ export class DataLoaderManager implements AppModule { } async loadMarketImplications(): Promise { - if (!getSecretState('WORLDMONITOR_API_KEY').present && !isProUser()) return; + if (!hasPremiumAccess()) return; if (this.ctx.isDestroyed || this.ctx.inFlight.has('marketImplications')) return; this.ctx.inFlight.add('marketImplications'); try { diff --git a/src/app/event-handlers.ts b/src/app/event-handlers.ts index decab04e0..5d165b3a5 100644 --- a/src/app/event-handlers.ts +++ b/src/app/event-handlers.ts @@ -52,6 +52,7 @@ import { trackMapLayerToggle, trackPanelToggled, trackDownloadClicked, + trackGateHit, } from '@/services/analytics'; import { detectPlatform, allButtons, buttonsForPlatform } from '@/components/DownloadBanner'; import type { Platform } from '@/components/DownloadBanner'; @@ -60,8 +61,11 @@ import { getCachedGpsInterference } from '@/services/gps-interference'; import { dataFreshness } from '@/services/data-freshness'; import { mlWorker } from '@/services/ml-worker'; import { UnifiedSettings } from '@/components/UnifiedSettings'; +import { AuthLauncher } from '@/components/AuthLauncher'; +import { AuthHeaderWidget } from '@/components/AuthHeaderWidget'; import { t } from '@/services/i18n'; import { TvModeController } from '@/services/tv-mode'; +import { getAuthState, subscribeAuthState } from '@/services/auth-state'; export interface EventHandlerCallbacks { updateSearchIndex: () => void; @@ -101,6 +105,7 @@ export class EventHandlerManager implements AppModule { private boundPanelCloseHandler: ((e: Event) => void) | null = null; private boundWidgetModifyHandler: ((e: Event) => void) | null = null; private boundUndoHandler: ((e: KeyboardEvent) => void) | null = null; + private proGateUnsubscribers: Array<() => void> = []; private closedPanelStack: string[] = []; // max-items: 20 private idleTimeoutId: ReturnType | null = null; private snapshotIntervalId: ReturnType | null = null; @@ -288,10 +293,16 @@ export class EventHandlerManager implements AppModule { document.removeEventListener('keydown', this.boundUndoHandler); this.boundUndoHandler = null; } + for (const unsub of this.proGateUnsubscribers) unsub(); + this.proGateUnsubscribers = []; this.ctx.tvMode?.destroy(); this.ctx.tvMode = null; this.ctx.unifiedSettings?.destroy(); this.ctx.unifiedSettings = null; + this.ctx.authHeaderWidget?.destroy(); + this.ctx.authHeaderWidget = null; + this.ctx.authModal?.destroy(); + this.ctx.authModal = null; } private setupEventListeners(): void { @@ -326,6 +337,7 @@ export class EventHandlerManager implements AppModule { }); this.initDownloadDropdown(); + this.initFooterDownload(); this.boundStorageHandler = (e: StorageEvent) => { if (e.key === STORAGE_KEYS.panels && e.newValue) { @@ -797,6 +809,28 @@ export class EventHandlerManager implements AppModule { document.addEventListener('keydown', this.boundDropdownKeydownHandler); } + private initFooterDownload(): void { + const mount = document.getElementById('footerDownloadMount'); + if (!mount) return; + const platform = detectPlatform(); + const primary = buttonsForPlatform(platform); + const btn = primary[0]; + if (!btn) return; + const a = document.createElement('a'); + a.href = btn.href; + a.textContent = t('header.downloadApp'); + a.className = 'site-footer-download-link'; + a.target = '_blank'; + a.rel = 'noopener'; + a.addEventListener('click', (e) => { + e.preventDefault(); + const plat = new URL(btn.href, location.origin).searchParams.get('platform') || 'unknown'; + trackDownloadClicked(plat); + window.open(btn.href, '_blank'); + }); + mount.replaceWith(a); + } + private setCopyLinkFeedback(button: HTMLElement | null, message: string): void { if (!button) return; const originalText = button.textContent ?? ''; @@ -927,7 +961,7 @@ export class EventHandlerManager implements AppModule { } setupExportPanel(): void { - if (!isProUser()) return; + // Always create — show/hide reactively via auth state subscription below. this.ctx.exportPanel = new ExportPanel(() => { const allCards = this.ctx.correlationEngine?.getAllCards() ?? []; const disabledCount = this.ctx.disabledSources.size; @@ -952,10 +986,18 @@ export class EventHandlerManager implements AppModule { }; }); + const el = this.ctx.exportPanel.getElement(); const headerRight = this.ctx.container.querySelector('.header-right'); if (headerRight) { - headerRight.insertBefore(this.ctx.exportPanel.getElement(), headerRight.firstChild); + headerRight.insertBefore(el, headerRight.firstChild); } + + const applyProGate = (isPro: boolean, initial = false) => { + el.style.display = isPro ? '' : 'none'; + if (initial && !isPro) trackGateHit('export'); + }; + applyProGate(getAuthState().user?.role === 'pro', true); + this.proGateUnsubscribers.push(subscribeAuthState(state => applyProGate(state.user?.role === 'pro'))); } setupUnifiedSettings(): void { @@ -1040,8 +1082,23 @@ export class EventHandlerManager implements AppModule { } } + setupAuthWidget(): void { + const modal = new AuthLauncher(); + this.ctx.authModal = modal; + + const widget = new AuthHeaderWidget( + () => modal.open(), + () => this.ctx.unifiedSettings?.open(), + ); + this.ctx.authHeaderWidget = widget; + const mount = document.getElementById('authWidgetMount'); + if (mount) { + mount.appendChild(widget.getElement()); + } + } + setupPlaybackControl(): void { - if (!isProUser()) return; + // Always create — show/hide reactively via auth state subscription below. this.ctx.playbackControl = new PlaybackControl(); this.ctx.playbackControl.onSnapshot((snapshot) => { if (snapshot) { @@ -1053,10 +1110,18 @@ export class EventHandlerManager implements AppModule { } }); + const el = this.ctx.playbackControl.getElement(); const headerRight = this.ctx.container.querySelector('.header-right'); if (headerRight) { - headerRight.insertBefore(this.ctx.playbackControl.getElement(), headerRight.firstChild); + headerRight.insertBefore(el, headerRight.firstChild); } + + const applyProGate = (isPro: boolean, initial = false) => { + el.style.display = isPro ? '' : 'none'; + if (initial && !isPro) trackGateHit('playback'); + }; + applyProGate(getAuthState().user?.role === 'pro', true); + this.proGateUnsubscribers.push(subscribeAuthState(state => applyProGate(state.user?.role === 'pro'))); } setupSnapshotSaving(): void { diff --git a/src/app/panel-layout.ts b/src/app/panel-layout.ts index 0ec03714f..13848265c 100644 --- a/src/app/panel-layout.ts +++ b/src/app/panel-layout.ts @@ -88,6 +88,17 @@ import { McpDataPanel } from '@/components/McpDataPanel'; import { openMcpConnectModal } from '@/components/McpConnectModal'; import { loadMcpPanels, saveMcpPanel } from '@/services/mcp-store'; import type { McpPanelSpec } from '@/services/mcp-store'; +import { getAuthState, subscribeAuthState } from '@/services/auth-state'; +import type { AuthSession } from '@/services/auth-state'; +import { PanelGateReason, getPanelGateReason } from '@/services/panel-gating'; +import type { Panel } from '@/components/Panel'; + +/** Panels that require premium access on the web. Auth-based gating applies to these. */ +const WEB_PREMIUM_PANELS = new Set([ + 'stock-analysis', + 'stock-backtest', + 'daily-market-brief', +]); export interface PanelLayoutManagerCallbacks { openCountryStory: (code: string, name: string) => void; @@ -106,6 +117,8 @@ export class PanelLayoutManager implements AppModule { private criticalBannerEl: HTMLElement | null = null; private aviationCommandBar: AviationCommandBar | null = null; private readonly applyTimeRangeFilterDebounced: (() => void) & { cancel(): void }; + private unsubscribeAuth: (() => void) | null = null; + private proBlockUnsubscribe: (() => void) | null = null; constructor(ctx: AppContext, callbacks: PanelLayoutManagerCallbacks) { this.ctx = ctx; @@ -117,12 +130,21 @@ export class PanelLayoutManager implements AppModule { init(): void { this.renderLayout(); + + // Subscribe to auth state for reactive panel gating on web + this.unsubscribeAuth = subscribeAuthState((state) => { + this.updatePanelGating(state); + }); this.fetchGitHubStars(); } destroy(): void { clearAllPendingCalls(); this.applyTimeRangeFilterDebounced.cancel(); + this.unsubscribeAuth?.(); + this.unsubscribeAuth = null; + this.proBlockUnsubscribe?.(); + this.proBlockUnsubscribe = null; this.panelDragCleanupHandlers.forEach((cleanup) => cleanup()); this.panelDragCleanupHandlers = []; if (this.criticalBannerEl) { @@ -148,6 +170,35 @@ export class PanelLayoutManager implements AppModule { window.removeEventListener('resize', this.ensureCorrectZones); } + /** Reactively update premium panel gating based on auth state. */ + private updatePanelGating(state: AuthSession): void { + for (const [key, panel] of Object.entries(this.ctx.panels)) { + const isPremium = WEB_PREMIUM_PANELS.has(key); + const reason = getPanelGateReason(state, isPremium); + + if (reason === PanelGateReason.NONE) { + // User has access -- unlock if previously locked + (panel as Panel).unlockPanel(); + } else { + // User does NOT have access -- show appropriate CTA + const onAction = this.getGateAction(reason); + (panel as Panel).showGatedCta(reason, onAction); + } + } + } + + /** Return the action callback for a given gate reason. */ + private getGateAction(reason: PanelGateReason): () => void { + switch (reason) { + case PanelGateReason.ANONYMOUS: + return () => this.ctx.authModal?.open(); + case PanelGateReason.FREE_TIER: + return () => window.open('https://worldmonitor.app/pro', '_blank'); + default: + return () => {}; + } + } + private async fetchGitHubStars(): Promise { try { const response = await fetch('https://api.github.com/repos/koala73/worldmonitor'); @@ -256,18 +307,12 @@ export class PanelLayoutManager implements AppModule {
- ${this.ctx.isDesktopApp ? '' : `
- -
-
`} ${this.ctx.isDesktopApp ? '' : ``} ${this.ctx.isDesktopApp ? '' : ``} ${SITE_VARIANT === 'happy' ? `` : ''} +
@@ -378,7 +423,7 @@ export class PanelLayoutManager implements AppModule { © ${new Date().getFullYear()} World Monitor @@ -583,22 +629,10 @@ export class PanelLayoutManager implements AppModule { this.createPanel('heatmap', () => new HeatmapPanel()); this.createPanel('markets', () => new MarketPanel()); - const stockAnalysisPanel = this.createPanel('stock-analysis', () => new StockAnalysisPanel()); - if (stockAnalysisPanel && !getSecretState('WORLDMONITOR_API_KEY').present && !isProUser()) { - stockAnalysisPanel.showLocked([ - 'AI stock briefs with technical + news synthesis', - 'Trend scoring from MA, MACD, RSI, and volume structure', - 'Actionable watchlist monitoring for your premium workspace', - ]); - } - const stockBacktestPanel = this.createPanel('stock-backtest', () => new StockBacktestPanel()); - if (stockBacktestPanel && !getSecretState('WORLDMONITOR_API_KEY').present && !isProUser()) { - stockBacktestPanel.showLocked([ - 'Historical replay of premium stock-analysis signals', - 'Win-rate, accuracy, and simulated-return metrics', - 'Recent evaluation samples for your tracked symbols', - ]); - } + this.createPanel('stock-analysis', () => new StockAnalysisPanel()); + this.createPanel('stock-backtest', () => new StockBacktestPanel()); + // Web premium gating for stock-analysis and stock-backtest is handled + // reactively by updatePanelGating() via auth state subscription. const monitorPanel = this.createPanel('monitors', () => new MonitorPanel(this.ctx.monitors)); monitorPanel?.onChanged((monitors) => { @@ -794,9 +828,9 @@ export class PanelLayoutManager implements AppModule { this.lazyPanel('daily-market-brief', () => import('@/components/DailyMarketBriefPanel').then(m => new m.DailyMarketBriefPanel()), - undefined, - (!_wmKeyPresent && !isProUser()) ? ['Pre-market watchlist priorities', 'Action plan for the session', 'Risk watch tied to current finance headlines'] : undefined, ); + // Web premium gating for daily-market-brief is handled reactively + // by updatePanelGating() via auth state subscription. this.lazyPanel('market-implications', () => import('@/components/MarketImplicationsPanel').then(m => new m.MarketImplicationsPanel()), @@ -991,13 +1025,12 @@ export class PanelLayoutManager implements AppModule { ); } - if (isProUser()) { - for (const spec of loadWidgets()) { - const panel = new CustomWidgetPanel(spec); - this.ctx.panels[spec.id] = panel; - if (!this.ctx.panelSettings[spec.id]) { - this.ctx.panelSettings[spec.id] = { name: spec.title, enabled: true, priority: 3 }; - } + // Always load custom widgets — Pro gating is handled reactively by auth state. + for (const spec of loadWidgets()) { + const panel = new CustomWidgetPanel(spec); + this.ctx.panels[spec.id] = panel; + if (!this.ctx.panelSettings[spec.id]) { + this.ctx.panelSettings[spec.id] = { name: spec.title, enabled: true, priority: 3 }; } } @@ -1107,51 +1140,62 @@ export class PanelLayoutManager implements AppModule { }); panelsGrid.appendChild(addPanelBlock); - if (isProUser()) { - const proBlock = document.createElement('button'); - proBlock.className = 'add-panel-block ai-widget-block ai-widget-block-pro'; - proBlock.setAttribute('aria-label', t('widgets.createInteractive')); - const proIcon = document.createElement('span'); - proIcon.className = 'add-panel-block-icon'; - proIcon.textContent = '\u26a1'; - const proLabel = document.createElement('span'); - proLabel.className = 'add-panel-block-label'; - proLabel.textContent = t('widgets.createInteractive'); - const proBadge = document.createElement('span'); - proBadge.className = 'widget-pro-badge'; - proBadge.textContent = t('widgets.proBadge'); - proBlock.appendChild(proIcon); - proBlock.appendChild(proLabel); - proBlock.appendChild(proBadge); - proBlock.addEventListener('click', () => { - openWidgetChatModal({ - mode: 'create', - tier: 'pro', - onComplete: (spec) => this.addCustomWidget(spec), - }); + // Always create Pro and MCP add-panel blocks — show/hide reactively via auth state. + const proBlock = document.createElement('button'); + proBlock.className = 'add-panel-block ai-widget-block ai-widget-block-pro'; + proBlock.setAttribute('aria-label', t('widgets.createInteractive')); + const proIcon = document.createElement('span'); + proIcon.className = 'add-panel-block-icon'; + proIcon.textContent = '\u26a1'; + const proLabel = document.createElement('span'); + proLabel.className = 'add-panel-block-label'; + proLabel.textContent = t('widgets.createInteractive'); + const proBadge = document.createElement('span'); + proBadge.className = 'widget-pro-badge'; + proBadge.textContent = t('widgets.proBadge'); + proBlock.appendChild(proIcon); + proBlock.appendChild(proLabel); + proBlock.appendChild(proBadge); + proBlock.addEventListener('click', () => { + openWidgetChatModal({ + mode: 'create', + tier: 'pro', + onComplete: (spec) => this.addCustomWidget(spec), }); - panelsGrid.appendChild(proBlock); - } + }); + panelsGrid.appendChild(proBlock); - if (isProUser()) { - const mcpBlock = document.createElement('button'); - mcpBlock.className = 'add-panel-block mcp-panel-block'; - mcpBlock.setAttribute('aria-label', t('mcp.connectPanel')); - const mcpIcon = document.createElement('span'); - mcpIcon.className = 'add-panel-block-icon'; - mcpIcon.textContent = '\u26a1'; - const mcpLabel = document.createElement('span'); - mcpLabel.className = 'add-panel-block-label'; - mcpLabel.textContent = t('mcp.connectPanel'); - mcpBlock.appendChild(mcpIcon); - mcpBlock.appendChild(mcpLabel); - mcpBlock.addEventListener('click', () => { - openMcpConnectModal({ - onComplete: (spec) => this.addMcpPanel(spec), - }); + const mcpBlock = document.createElement('button'); + mcpBlock.className = 'add-panel-block mcp-panel-block'; + mcpBlock.setAttribute('aria-label', t('mcp.connectPanel')); + const mcpIcon = document.createElement('span'); + mcpIcon.className = 'add-panel-block-icon'; + mcpIcon.textContent = '\u26a1'; + const mcpLabel = document.createElement('span'); + mcpLabel.className = 'add-panel-block-label'; + mcpLabel.textContent = t('mcp.connectPanel'); + mcpBlock.appendChild(mcpIcon); + mcpBlock.appendChild(mcpLabel); + mcpBlock.addEventListener('click', () => { + openMcpConnectModal({ + onComplete: (spec) => this.addMcpPanel(spec), }); - panelsGrid.appendChild(mcpBlock); - } + }); + panelsGrid.appendChild(mcpBlock); + + // Reactively show/hide Pro-only UI blocks based on auth state + const proBlocks = [proBlock, mcpBlock]; + const applyProBlockGating = (isPro: boolean) => { + for (const block of proBlocks) { + block.style.display = isPro ? '' : 'none'; + } + }; + applyProBlockGating( + isProUser() || getAuthState().user?.role === 'pro' + ); + this.proBlockUnsubscribe = subscribeAuthState((state) => { + applyProBlockGating(isProUser() || state.user?.role === 'pro'); + }); const bottomGrid = document.getElementById('mapBottomGrid'); if (bottomGrid) { diff --git a/src/app/search-manager.ts b/src/app/search-manager.ts index 31d3ca70a..ff382dcc2 100644 --- a/src/app/search-manager.ts +++ b/src/app/search-manager.ts @@ -29,6 +29,7 @@ import type { PositionSample } from '@/services/aviation'; import { fetchAircraftPositions } from '@/services/aviation'; import type { MilitaryFlight } from '@/types'; import { isProUser } from '@/services/widget-store'; +import { getAuthState } from '@/services/auth-state'; export interface SearchManagerCallbacks { openCountryBriefByCode: (code: string, country: string) => void; @@ -213,8 +214,11 @@ export class SearchManager implements AppModule { this.ctx.searchModal.setOnSelect((result) => this.handleSearchResult(result)); this.ctx.searchModal.setOnCommand((cmd) => this.handleCommand(cmd)); - if (isProUser()) { + // Always wire flight search — check pro status reactively inside the callback + // so mid-session sign-ins get the feature without a page reload. + { this.ctx.searchModal.setOnFlightSearch((callsign) => { + if (!isProUser() && getAuthState().user?.role !== 'pro') return; fetchAircraftPositions({ callsign }).then((positions) => { if (!this.ctx.searchModal) return; // Deduplicate by callsign: keep the most recently observed entry per callsign. diff --git a/src/components/AuthHeaderWidget.ts b/src/components/AuthHeaderWidget.ts new file mode 100644 index 000000000..5b6a1c0b1 --- /dev/null +++ b/src/components/AuthHeaderWidget.ts @@ -0,0 +1,57 @@ +import { subscribeAuthState, type AuthSession } from '@/services/auth-state'; +import { mountUserButton, openSignIn } from '@/services/clerk'; + +export class AuthHeaderWidget { + private container: HTMLElement; + private unsubscribeAuth: (() => void) | null = null; + private unmountUserButton: (() => void) | null = null; + + constructor(_onSignInClick?: () => void, _onSettingsClick?: () => void) { + this.container = document.createElement('div'); + this.container.className = 'auth-header-widget'; + + this.unsubscribeAuth = subscribeAuthState((state: AuthSession) => { + if (state.isPending) { + this.container.innerHTML = ''; + return; + } + this.render(state); + }); + } + + public getElement(): HTMLElement { + return this.container; + } + + public destroy(): void { + this.unmountUserButton?.(); + this.unmountUserButton = null; + if (this.unsubscribeAuth) { + this.unsubscribeAuth(); + this.unsubscribeAuth = null; + } + } + + private render(state: AuthSession): void { + // Cleanup previous Clerk mount + this.unmountUserButton?.(); + this.unmountUserButton = null; + this.container.innerHTML = ''; + + if (!state.user) { + // Signed out -- show Sign In button + const btn = document.createElement('button'); + btn.className = 'auth-signin-btn'; + btn.textContent = 'Sign In'; + btn.addEventListener('click', () => openSignIn()); + this.container.appendChild(btn); + return; + } + + // Signed in -- mount Clerk UserButton + const userBtnEl = document.createElement('div'); + userBtnEl.className = 'auth-clerk-user-button'; + this.container.appendChild(userBtnEl); + this.unmountUserButton = mountUserButton(userBtnEl); + } +} diff --git a/src/components/AuthLauncher.ts b/src/components/AuthLauncher.ts new file mode 100644 index 000000000..a4a84eb95 --- /dev/null +++ b/src/components/AuthLauncher.ts @@ -0,0 +1,19 @@ +import { openSignIn } from '@/services/clerk'; + +/** + * Minimal auth launcher -- wraps Clerk.openSignIn(). + * Replaces the custom OTP modal. Clerk handles all UI. + */ +export class AuthLauncher { + public open(): void { + openSignIn(); + } + + public close(): void { + // Clerk manages its own modal lifecycle + } + + public destroy(): void { + // Nothing to clean up -- Clerk manages its own resources + } +} diff --git a/src/components/CommunityWidget.ts b/src/components/CommunityWidget.ts index 06408d9a7..457f66c7e 100644 --- a/src/components/CommunityWidget.ts +++ b/src/components/CommunityWidget.ts @@ -12,25 +12,18 @@ export function mountCommunityWidget(): void { widget.className = 'community-widget'; widget.innerHTML = `
-
- ${t('components.community.joinDiscussion')} - ${t('components.community.openDiscussion')} + Join the Discord Community
- `; const dismiss = () => { + setDismissed(DISMISSED_KEY); widget.classList.add('cw-hiding'); setTimeout(() => widget.remove(), 300); }; widget.querySelector('.cw-close')!.addEventListener('click', dismiss); - widget.querySelector('.cw-dismiss')!.addEventListener('click', () => { - setDismissed(DISMISSED_KEY); - dismiss(); - }); - document.body.appendChild(widget); } diff --git a/src/components/Panel.ts b/src/components/Panel.ts index 24d33e3ce..ded2dc725 100644 --- a/src/components/Panel.ts +++ b/src/components/Panel.ts @@ -5,6 +5,7 @@ import { h, replaceChildren, safeHtml } from '../utils/dom-utils'; import { trackPanelResized } from '@/services/analytics'; import { getAiFlowSettings } from '@/services/ai-flow-settings'; import { getSecretState } from '@/services/runtime-config'; +import { PanelGateReason } from '@/services/panel-gating'; export interface PanelOptions { id: string; @@ -18,6 +19,10 @@ export interface PanelOptions { defaultRowSpan?: number; } +const lockSvg = ``; + +const upgradeSvg = ``; + const PANEL_SPANS_KEY = 'worldmonitor-panel-spans'; function loadPanelSpans(): Record { @@ -758,7 +763,6 @@ export class Panel { } this.element.classList.add('panel-is-locked'); - const lockSvg = ``; const iconEl = h('div', { className: 'panel-locked-icon' }); iconEl.innerHTML = lockSvg; @@ -786,6 +790,55 @@ export class Panel { replaceChildren(this.content, h('div', { className: 'panel-locked-state' }, ...lockedChildren)); } + public showGatedCta(reason: PanelGateReason, onAction: () => void): void { + this._locked = true; + this.clearRetryCountdown(); + + // Hide elements between header and content (same as showLocked) + for (let child = this.header.nextElementSibling; child && child !== this.content; child = child.nextElementSibling) { + (child as HTMLElement).style.display = 'none'; + } + this.element.classList.add('panel-is-locked'); + + const config: Record = { + [PanelGateReason.ANONYMOUS]: { + icon: lockSvg, + desc: t('premium.signInToUnlock'), + cta: t('premium.signIn'), + }, + [PanelGateReason.FREE_TIER]: { + icon: upgradeSvg, + desc: t('premium.upgradeDesc'), + cta: t('premium.upgradeToPro'), + }, + }; + + const entry = config[reason]; + if (!entry) return; // PanelGateReason.NONE should never reach here + + const iconEl = h('div', { className: 'panel-locked-icon' }); + iconEl.innerHTML = entry.icon; + + const descEl = h('div', { className: 'panel-locked-desc' }, entry.desc); + + const ctaBtn = h('button', { type: 'button', className: 'panel-locked-cta' }, entry.cta); + ctaBtn.addEventListener('click', onAction); + + replaceChildren(this.content, h('div', { className: 'panel-locked-state' }, iconEl, descEl, ctaBtn)); + } + + public unlockPanel(): void { + if (!this._locked) return; + this._locked = false; + this.element.classList.remove('panel-is-locked'); + // Re-show hidden elements + for (let child = this.header.nextElementSibling; child && child !== this.content; child = child.nextElementSibling) { + (child as HTMLElement).style.display = ''; + } + // Clear the locked state content + replaceChildren(this.content); + } + public showRetrying(message?: string, countdownSeconds?: number): void { if (this._locked) return; this.clearRetryCountdown(); diff --git a/src/components/ProBanner.ts b/src/components/ProBanner.ts index 484cee9ce..25a42e61e 100644 --- a/src/components/ProBanner.ts +++ b/src/components/ProBanner.ts @@ -1,3 +1,5 @@ +import { trackGateHit } from '@/services/analytics'; + let bannerEl: HTMLElement | null = null; /* TODO: re-enable dismiss after pro launch promotion period @@ -29,6 +31,8 @@ export function showProBanner(container: HTMLElement): void { if (bannerEl) return; if (window.self !== window.top) return; + trackGateHit('pro-banner'); + const banner = document.createElement('div'); banner.className = 'pro-banner'; banner.innerHTML = ` diff --git a/src/config/panels.ts b/src/config/panels.ts index 25a7072d0..550dc31e2 100644 --- a/src/config/panels.ts +++ b/src/config/panels.ts @@ -43,6 +43,9 @@ const FULL_PANELS: Record = { commodities: { name: 'Metals & Materials', enabled: true, priority: 1 }, 'energy-complex': { name: 'Energy Complex', enabled: true, priority: 1 }, markets: { name: 'Markets', enabled: true, priority: 1 }, + 'stock-analysis': { name: 'Stock Analysis', enabled: true, priority: 1, premium: 'locked' as const }, + 'stock-backtest': { name: 'Backtesting', enabled: true, priority: 1, premium: 'locked' as const }, + 'daily-market-brief': { name: 'Daily Market Brief', enabled: true, priority: 1, premium: 'locked' as const }, economic: { name: 'Macro Stress', enabled: true, priority: 1 }, 'trade-policy': { name: 'Trade Policy', enabled: true, priority: 1 }, 'supply-chain': { name: 'Supply Chain', enabled: true, priority: 1, ...(_desktop && { premium: 'enhanced' as const }) }, diff --git a/src/config/variants/finance.ts b/src/config/variants/finance.ts index 2ce61e85e..e2862e5b1 100644 --- a/src/config/variants/finance.ts +++ b/src/config/variants/finance.ts @@ -145,6 +145,9 @@ export const DEFAULT_PANELS: Record = { 'live-news': { name: 'Market Headlines', enabled: true, priority: 1 }, insights: { name: 'AI Market Insights', enabled: true, priority: 1 }, markets: { name: 'Live Markets', enabled: true, priority: 1 }, + 'stock-analysis': { name: 'Stock Analysis', enabled: true, priority: 1 }, + 'stock-backtest': { name: 'Backtesting', enabled: true, priority: 1 }, + 'daily-market-brief': { name: 'Daily Market Brief', enabled: true, priority: 1 }, 'markets-news': { name: 'Markets News', enabled: true, priority: 2 }, forex: { name: 'Forex & Currencies', enabled: true, priority: 1 }, bonds: { name: 'Fixed Income', enabled: true, priority: 1 }, diff --git a/src/config/variants/full.ts b/src/config/variants/full.ts index 2e4e11711..b77f9a9d4 100644 --- a/src/config/variants/full.ts +++ b/src/config/variants/full.ts @@ -37,6 +37,9 @@ export const DEFAULT_PANELS: Record = { polymarket: { name: 'Predictions', enabled: true, priority: 1 }, commodities: { name: 'Commodities', enabled: true, priority: 1 }, markets: { name: 'Markets', enabled: true, priority: 1 }, + 'stock-analysis': { name: 'Stock Analysis', enabled: true, priority: 1 }, + 'stock-backtest': { name: 'Backtesting', enabled: true, priority: 1 }, + 'daily-market-brief': { name: 'Daily Market Brief', enabled: true, priority: 1 }, economic: { name: 'Economic Indicators', enabled: true, priority: 1 }, finance: { name: 'Financial', enabled: true, priority: 1 }, tech: { name: 'Technology', enabled: true, priority: 2 }, diff --git a/src/locales/en.json b/src/locales/en.json index f4a3261af..9607d04b7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2723,6 +2723,12 @@ "pro": "PRO", "lockedDesc": "Requires a World Monitor license key", "joinWaitlist": "Join Waitlist", + "signInToUnlock": "Sign in to unlock premium features", + "signIn": "Sign In to Unlock", + "verifyEmailToUnlock": "Verify your email to access premium features", + "resendVerification": "Resend Verification", + "upgradeDesc": "Upgrade to Pro for full access to premium analytics", + "upgradeToPro": "Upgrade to Pro", "features": { "orefSirens1": "Real-time Israel missile & rocket alerts", "orefSirens2": "Siren zone mapping with threat classification", diff --git a/src/services/analytics.ts b/src/services/analytics.ts index a5932d006..4389b50d1 100644 --- a/src/services/analytics.ts +++ b/src/services/analytics.ts @@ -5,6 +5,8 @@ * even if the Umami script has not loaded yet (e.g. ad blockers, SSR). */ +import { subscribeAuthState } from './auth-state'; + // --------------------------------------------------------------------------- // Type-safe event catalog — every event name lives here. // Typo in an event string = compile error. @@ -69,58 +71,50 @@ export async function initAnalytics(): Promise { // by user/plan. Safe to call before Umami script loads. // --------------------------------------------------------------------------- -/** - * Attach user context to all subsequent Umami events for this session. - * Call this once after a successful sign-in or on app boot when the user - * is already authenticated. - * - * PR #1812: call from subscribeAuthState() when user is non-null. - * Pass user.id and the plan string from the session/subscription object. - */ export function identifyUser(userId: string, plan: string): void { window.umami?.identify({ userId, plan }); } -/** - * Clear user identity (call on sign-out so subsequent events are anonymous). - * - * PR #1812: call from subscribeAuthState() when user becomes null. - */ export function clearIdentity(): void { window.umami?.identify({}); } +let _unsubscribeAuthAnalytics: (() => void) | null = null; + /** - * Stub — wire this in PR #1812. - * - * Instructions for PR #1812: - * 1. Import { identifyUser, clearIdentity, track } from '@/services/analytics' - * 2. Replace this body with: - * - * subscribeAuthState((user) => { - * if (user) { - * identifyUser(user.id, user.plan ?? 'free'); - * } else { - * clearIdentity(); - * } - * }); - * - * 3. Call initAuthAnalytics() from main.ts after initAnalytics(). - * - * 4. At the sign-in callsite (success callback): - * track('sign-in', { method: 'email' }); // or 'google', 'github' - * - * 5. At the sign-up callsite (success callback): - * track('sign-up', { method: 'email' }); - * - * 6. At the sign-out callsite: - * track('sign-out'); - * - * 7. Wherever a feature is gated behind auth/pro and the user is blocked: - * track('gate-hit', { feature: 'pro-widget' }); // or 'mcp', 'pro-brief', etc. + * Call once after initAuthState() to keep Umami identity in sync with + * the authenticated user. Re-entrant safe: subsequent calls are no-ops. */ export function initAuthAnalytics(): void { - // No-op until PR #1812. + if (_unsubscribeAuthAnalytics) return; + + _unsubscribeAuthAnalytics = subscribeAuthState((state) => { + if (state.user) { + identifyUser(state.user.id, state.user.role); + } else { + clearIdentity(); + } + }); +} + +// --------------------------------------------------------------------------- +// Auth events +// --------------------------------------------------------------------------- + +export function trackSignIn(method: string): void { + track('sign-in', { method }); +} + +export function trackSignUp(method: string): void { + track('sign-up', { method }); +} + +export function trackSignOut(): void { + track('sign-out'); +} + +export function trackGateHit(feature: string): void { + track('gate-hit', { feature }); } // --------------------------------------------------------------------------- diff --git a/src/services/auth-state.ts b/src/services/auth-state.ts new file mode 100644 index 000000000..d50ab0e5d --- /dev/null +++ b/src/services/auth-state.ts @@ -0,0 +1,62 @@ +import { initClerk, getCurrentClerkUser, subscribeClerk } from './clerk'; + +/** Minimal user profile exposed to UI components. */ +export interface AuthUser { + id: string; + name: string; + email: string; + image?: string | null; + role: 'free' | 'pro'; +} + +/** Simplified auth session state for UI consumption. */ +export interface AuthSession { + user: AuthUser | null; + isPending: boolean; +} + +let _currentSession: AuthSession = { user: null, isPending: true }; + +function snapshotSession(): AuthSession { + const cu = getCurrentClerkUser(); + if (!cu) return { user: null, isPending: false }; + return { + user: { + id: cu.id, + name: cu.name, + email: cu.email, + image: cu.image, + role: cu.plan, + }, + isPending: false, + }; +} + +/** + * Initialize auth state. Call once at app startup before UI subscribes. + */ +export async function initAuthState(): Promise { + await initClerk(); + _currentSession = snapshotSession(); +} + +/** + * Subscribe to reactive auth state changes. + * @returns Unsubscribe function. + */ +export function subscribeAuthState(callback: (state: AuthSession) => void): () => void { + // Emit current state immediately + callback(_currentSession); + + return subscribeClerk(() => { + _currentSession = snapshotSession(); + callback(_currentSession); + }); +} + +/** + * Synchronous snapshot of current auth state. + */ +export function getAuthState(): AuthSession { + return _currentSession; +} diff --git a/src/services/clerk.ts b/src/services/clerk.ts new file mode 100644 index 000000000..84b85410d --- /dev/null +++ b/src/services/clerk.ts @@ -0,0 +1,190 @@ +/** + * Clerk JS initialization and thin wrapper. + * + * Uses dynamic import so the module is safe to import in Node.js test + * environments where @clerk/clerk-js (browser-only) is not available. + */ + +import type { Clerk } from '@clerk/clerk-js'; + +type ClerkInstance = Clerk; + +const PUBLISHABLE_KEY = (typeof import.meta !== 'undefined' && import.meta.env?.VITE_CLERK_PUBLISHABLE_KEY) as string | undefined; + +let clerkInstance: ClerkInstance | null = null; +let loadPromise: Promise | null = null; + +const MONO_FONT = "'SF Mono', Monaco, 'Cascadia Code', 'Fira Code', 'DejaVu Sans Mono', monospace"; + +function getAppearance() { + const isDark = typeof document !== 'undefined' + ? document.documentElement.dataset.theme !== 'light' + : true; + + return isDark + ? { + variables: { + colorBackground: '#0f0f0f', + colorInputBackground: '#141414', + colorInputText: '#e8e8e8', + colorText: '#e8e8e8', + colorTextSecondary: '#aaaaaa', + colorPrimary: '#44ff88', + colorNeutral: '#e8e8e8', + colorDanger: '#ff4444', + borderRadius: '4px', + fontFamily: MONO_FONT, + fontFamilyButtons: MONO_FONT, + }, + elements: { + card: { backgroundColor: '#111111', border: '1px solid #2a2a2a', boxShadow: '0 8px 32px rgba(0,0,0,0.6)' }, + headerTitle: { color: '#e8e8e8' }, + headerSubtitle: { color: '#aaaaaa' }, + dividerLine: { backgroundColor: '#2a2a2a' }, + dividerText: { color: '#666666' }, + formButtonPrimary: { color: '#000000', fontWeight: '600' }, + footerActionLink: { color: '#44ff88' }, + identityPreviewEditButton: { color: '#44ff88' }, + formFieldLabel: { color: '#cccccc' }, + formFieldInput: { borderColor: '#2a2a2a' }, + socialButtonsBlockButton: { borderColor: '#2a2a2a', color: '#e8e8e8', backgroundColor: '#141414' }, + socialButtonsBlockButtonText: { color: '#e8e8e8' }, + modalCloseButton: { color: '#888888' }, + }, + } + : { + variables: { + colorBackground: '#ffffff', + colorInputBackground: '#f8f9fa', + colorInputText: '#1a1a1a', + colorText: '#1a1a1a', + colorTextSecondary: '#555555', + colorPrimary: '#16a34a', + colorNeutral: '#1a1a1a', + colorDanger: '#dc2626', + borderRadius: '4px', + fontFamily: MONO_FONT, + fontFamilyButtons: MONO_FONT, + }, + elements: { + card: { backgroundColor: '#ffffff', border: '1px solid #d4d4d4', boxShadow: '0 4px 24px rgba(0,0,0,0.12)' }, + formButtonPrimary: { color: '#ffffff', fontWeight: '600' }, + footerActionLink: { color: '#16a34a' }, + identityPreviewEditButton: { color: '#16a34a' }, + socialButtonsBlockButton: { borderColor: '#d4d4d4' }, + }, + }; +} + +/** Initialize Clerk. Call once at app startup. */ +export async function initClerk(): Promise { + if (clerkInstance) return; + if (loadPromise) return loadPromise; + if (!PUBLISHABLE_KEY) { + console.warn('[clerk] VITE_CLERK_PUBLISHABLE_KEY not set, auth disabled'); + return; + } + loadPromise = (async () => { + try { + const { Clerk } = await import('@clerk/clerk-js'); + const clerk = new Clerk(PUBLISHABLE_KEY); + await clerk.load({ appearance: getAppearance() }); + clerkInstance = clerk; + } catch (e) { + loadPromise = null; // allow retry on next call + throw e; + } + })(); + return loadPromise; +} + +/** Get the initialized Clerk instance. Returns null if not loaded. */ +export function getClerk(): ClerkInstance | null { + return clerkInstance; +} + +/** Open the Clerk sign-in modal. */ +export function openSignIn(): void { + clerkInstance?.openSignIn({ appearance: getAppearance() }); +} + +/** Sign out the current user. */ +export async function signOut(): Promise { + _cachedToken = null; + _cachedTokenAt = 0; + await clerkInstance?.signOut(); +} + +/** + * Get a bearer token for premium API requests. + * Uses the 'convex' JWT template which includes the `plan` claim. + * Returns null if no active session. + * + * Tokens are cached for 50s (Clerk tokens expire at 60s) with in-flight + * deduplication to prevent concurrent panels from racing against Clerk. + */ +let _cachedToken: string | null = null; +let _cachedTokenAt = 0; +let _tokenInflight: Promise | null = null; +const TOKEN_CACHE_TTL_MS = 50_000; + +export async function getClerkToken(): Promise { + if (_cachedToken && Date.now() - _cachedTokenAt < TOKEN_CACHE_TTL_MS) { + return _cachedToken; + } + if (_tokenInflight) return _tokenInflight; + + _tokenInflight = (async () => { + const session = clerkInstance?.session; + if (!session) return null; + try { + const token = await session.getToken({ template: 'convex' }); + if (token) { + _cachedToken = token; + _cachedTokenAt = Date.now(); + } + return token; + } catch { + return null; + } finally { + _tokenInflight = null; + } + })(); + return _tokenInflight; +} + +/** Get current Clerk user metadata. Returns null if signed out. */ +export function getCurrentClerkUser(): { id: string; name: string; email: string; image: string | null; plan: 'free' | 'pro' } | null { + const user = clerkInstance?.user; + if (!user) return null; + const plan = (user.publicMetadata as Record)?.plan; + return { + id: user.id, + name: user.fullName ?? user.firstName ?? 'User', + email: user.primaryEmailAddress?.emailAddress ?? '', + image: user.imageUrl ?? null, + plan: plan === 'pro' ? 'pro' : 'free', + }; +} + +/** + * Subscribe to Clerk auth state changes. + * Returns unsubscribe function. + */ +export function subscribeClerk(callback: () => void): () => void { + if (!clerkInstance) return () => {}; + return clerkInstance.addListener(callback); +} + +/** + * Mount Clerk's UserButton component into a DOM element. + * Returns an unmount function. + */ +export function mountUserButton(el: HTMLDivElement): () => void { + if (!clerkInstance) return () => {}; + clerkInstance.mountUserButton(el, { + afterSignOutUrl: window.location.href, + appearance: getAppearance(), + }); + return () => clerkInstance?.unmountUserButton(el); +} diff --git a/src/services/panel-gating.ts b/src/services/panel-gating.ts new file mode 100644 index 000000000..b97793044 --- /dev/null +++ b/src/services/panel-gating.ts @@ -0,0 +1,32 @@ +import type { AuthSession } from './auth-state'; +import { isDesktopRuntime } from './runtime'; +import { getSecretState } from './runtime-config'; + +export enum PanelGateReason { + NONE = 'none', // show content (pro user, or desktop with API key, or non-premium panel) + ANONYMOUS = 'anonymous', // "Sign In to Unlock" + FREE_TIER = 'free_tier', // "Upgrade to Pro" +} + +/** + * Determine gating reason for a premium panel given current auth state. + * Desktop with valid API key always bypasses auth gating (backward compat). + * Non-premium panels always return NONE. + */ +export function getPanelGateReason( + authState: AuthSession, + isPremium: boolean, +): PanelGateReason { + // Non-premium panels are never gated + if (!isPremium) return PanelGateReason.NONE; + + // Desktop with API key: always unlocked (backward compat) + if (isDesktopRuntime() && getSecretState('WORLDMONITOR_API_KEY').present) { + return PanelGateReason.NONE; + } + + // Web gating based on auth state + if (!authState.user) return PanelGateReason.ANONYMOUS; + if (authState.user.role !== 'pro') return PanelGateReason.FREE_TIER; + return PanelGateReason.NONE; +} diff --git a/src/services/runtime.ts b/src/services/runtime.ts index 5346ff04e..63ed5cefa 100644 --- a/src/services/runtime.ts +++ b/src/services/runtime.ts @@ -1,4 +1,5 @@ import { SITE_VARIANT } from '@/config/variant'; +import { getClerkToken } from '@/services/clerk'; const ENV = (() => { try { @@ -732,6 +733,8 @@ export function installRuntimeFetchPatch(): void { (window as unknown as Record).__wmFetchPatched = true; } +import { PREMIUM_RPC_PATHS as WEB_PREMIUM_API_PATHS } from '@/shared/premium-paths'; + const ALLOWED_REDIRECT_HOSTS = /^https:\/\/([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)*worldmonitor\.app(:\d+)?$/; function isAllowedRedirectTarget(url: string): boolean { @@ -777,20 +780,42 @@ export function installWebApiRedirect(): void { } }; + /** + * For premium API paths, inject an Authorization: Bearer header when the + * user has an active session and no existing auth header is present. + * Returns the original init unchanged for non-premium paths (zero overhead). + */ + const enrichInitForPremium = async (pathWithQuery: string, init?: RequestInit): Promise => { + const path = pathWithQuery.split('?')[0] ?? pathWithQuery; + if (!WEB_PREMIUM_API_PATHS.has(path)) return init; + const token = await getClerkToken(); + if (!token) return init; + const headers = new Headers(init?.headers); + // Don't overwrite existing auth headers (API key users keep their flow) + if (headers.has('Authorization') || headers.has('X-WorldMonitor-Key')) return init; + headers.set('Authorization', `Bearer ${token}`); + return { ...init, headers }; + }; + window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise => { if (typeof input === 'string' && shouldRedirectPath(input)) { - return fetchWithRedirectFallback(`${API_BASE}${input}`, input, init); + const enriched = await enrichInitForPremium(input, init); + return fetchWithRedirectFallback(`${API_BASE}${input}`, input, enriched); } if (input instanceof URL && input.origin === window.location.origin && shouldRedirectPath(`${input.pathname}${input.search}`)) { - return fetchWithRedirectFallback(new URL(`${API_BASE}${input.pathname}${input.search}`), input, init); + const pathAndSearch = `${input.pathname}${input.search}`; + const enriched = await enrichInitForPremium(pathAndSearch, init); + return fetchWithRedirectFallback(new URL(`${API_BASE}${pathAndSearch}`), input, enriched); } if (input instanceof Request) { const u = new URL(input.url); if (u.origin === window.location.origin && shouldRedirectPath(`${u.pathname}${u.search}`)) { + const pathAndSearch = `${u.pathname}${u.search}`; + const enriched = await enrichInitForPremium(pathAndSearch, init); return fetchWithRedirectFallback( - new Request(`${API_BASE}${u.pathname}${u.search}`, input), + new Request(`${API_BASE}${pathAndSearch}`, input), input.clone(), - init, + enriched, ); } } diff --git a/src/services/widget-store.ts b/src/services/widget-store.ts index d59fb2ea4..5c75f9abb 100644 --- a/src/services/widget-store.ts +++ b/src/services/widget-store.ts @@ -1,5 +1,6 @@ import { loadFromStorage, saveToStorage } from '@/utils'; import { sanitizeWidgetHtml } from '@/utils/widget-sanitizer'; +import { getAuthState } from '@/services/auth-state'; const STORAGE_KEY = 'wm-custom-widgets'; const PANEL_SPANS_KEY = 'worldmonitor-panel-spans'; @@ -150,7 +151,7 @@ export function isProWidgetEnabled(): boolean { } export function isProUser(): boolean { - return isWidgetFeatureEnabled() || isProWidgetEnabled(); + return isWidgetFeatureEnabled() || isProWidgetEnabled() || getAuthState().user?.role === 'pro'; } export function getProWidgetKey(): string { diff --git a/src/shared/premium-paths.ts b/src/shared/premium-paths.ts new file mode 100644 index 000000000..6434ea28a --- /dev/null +++ b/src/shared/premium-paths.ts @@ -0,0 +1,12 @@ +/** + * Premium RPC paths that require either an API key or a Pro session. + * + * Single source of truth consumed by both the server gateway (auth enforcement) + * and the web client runtime (token injection). + */ +export const PREMIUM_RPC_PATHS = new Set([ + '/api/market/v1/analyze-stock', + '/api/market/v1/get-stock-analysis-history', + '/api/market/v1/backtest-stock', + '/api/market/v1/list-stored-stock-backtests', +]); diff --git a/src/styles/main.css b/src/styles/main.css index 6e718adfe..443c06a91 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -20182,6 +20182,341 @@ body.has-breaking-alert .panels-grid { 100% { background-position: -200% 0; } } +/* ── Auth Modal ── */ +.auth-modal-content { + max-width: 420px; + width: 90%; + padding: 32px; + text-align: center; + position: relative; +} + +.auth-modal-close { + position: absolute; + top: 12px; + right: 12px; + background: none; + border: none; + color: var(--text-secondary); + cursor: pointer; + padding: 4px; + font-size: 18px; +} +.auth-modal-close:hover { color: var(--text); } + +.auth-form { + display: flex; + flex-direction: column; + gap: 12px; + text-align: left; +} + +.auth-form label { + font-size: 13px; + color: var(--text-secondary); + font-weight: 500; +} + +.auth-form input { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--bg); + color: var(--text); + font-size: 14px; + outline: none; + transition: border-color 0.15s; + box-sizing: border-box; +} +.auth-form input:focus { + border-color: var(--accent); +} + +.auth-submit-btn { + width: 100%; + padding: 10px; + border: none; + border-radius: 6px; + background: #3b82f6; + color: #fff; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: opacity 0.15s; + margin-top: 4px; +} +.auth-submit-btn:hover { opacity: 0.9; } +.auth-submit-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.auth-error { + color: #e74c3c; + font-size: 13px; + text-align: center; + min-height: 18px; +} + +.auth-otp-actions { + display: flex; + justify-content: space-between; + margin-top: 12px; +} + +.auth-link-btn { + background: none; + border: none; + color: var(--text-secondary); + font-size: 13px; + cursor: pointer; + padding: 0; +} +.auth-link-btn:hover { color: var(--text); } +.auth-link-btn:disabled { opacity: 0.5; cursor: default; } + +.auth-footer { + margin-top: 16px; + font-size: 12px; + color: var(--text-secondary); +} + +/* ── Auth Header Widget ── */ +.auth-header-widget { + display: flex; + align-items: center; + position: relative; +} + +.auth-signin-btn { + padding: 6px 14px; + border: 1px solid var(--border); + border-radius: 6px; + background: transparent; + color: var(--text); + font-size: 13px; + font-weight: 500; + cursor: pointer; + white-space: nowrap; + transition: opacity 0.15s; +} +.auth-signin-btn:hover { opacity: 0.85; } + +.auth-avatar-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: 2px solid var(--border); + background: var(--accent); + color: #fff; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + overflow: hidden; + font-size: 13px; + font-weight: 600; +} + +.auth-avatar-img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.auth-avatar-initials { + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + color: #fff; + font-weight: 600; + line-height: 1; + flex-shrink: 0; +} + +.auth-dropdown { + position: absolute; + top: calc(100% + 8px); + right: 0; + min-width: 240px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 8px; + box-shadow: 0 4px 16px rgba(0,0,0,0.18); + padding: 12px; + display: none; + z-index: 1000; +} +.auth-dropdown.open { display: block; } + +.auth-dropdown-header { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + margin-bottom: 8px; +} + +.auth-dropdown-avatar-wrap { + width: 44px; + height: 44px; + border-radius: 50%; + overflow: hidden; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.auth-dropdown-info { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + flex: 1; +} + +.auth-dropdown-name { + font-size: 14px; + font-weight: 600; + color: var(--text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.auth-dropdown-email { + font-size: 12px; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.auth-tier-badge { + display: inline-block; + padding: 2px 8px; + border-radius: 4px; + background: var(--border); + color: var(--text-secondary); + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + margin-top: 4px; + width: fit-content; +} + +.auth-tier-badge-pro { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: #fff; +} + +.auth-dropdown-divider { + height: 1px; + background: var(--border); + margin: 8px 0; +} + +.auth-dropdown-item { + width: 100%; + padding: 7px 8px; + border: none; + border-radius: 5px; + background: none; + color: var(--text); + font-size: 13px; + cursor: pointer; + text-align: left; + display: flex; + align-items: center; + gap: 8px; + transition: background 0.12s; +} +.auth-dropdown-item:hover { background: var(--bg); } +.auth-dropdown-item svg { flex-shrink: 0; opacity: 0.7; } + +.auth-signout-item { + color: var(--text); +} +.auth-signout-item:hover { + background: var(--bg); + color: #e85b5b; +} +.auth-signout-item:hover svg { opacity: 1; } + +/* Profile edit inline form */ +.auth-profile-edit { + padding: 8px 0 4px; +} + +.auth-profile-edit-field { + display: flex; + flex-direction: column; + gap: 5px; + margin-bottom: 10px; +} + +.auth-profile-label { + font-size: 11px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.auth-profile-input { + width: 100%; + padding: 7px 10px; + border: 1px solid var(--border); + border-radius: 5px; + background: var(--bg); + color: var(--text); + font-size: 13px; + outline: none; + box-sizing: border-box; + transition: border-color 0.15s; +} +.auth-profile-input:focus { border-color: var(--accent); } + +.auth-profile-edit-actions { + display: flex; + gap: 8px; +} + +.auth-profile-save-btn, +.auth-profile-cancel-btn { + flex: 1; + padding: 6px 10px; + border-radius: 5px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: opacity 0.12s; + border: none; +} +.auth-profile-save-btn { + background: var(--accent); + color: #fff; +} +.auth-profile-save-btn:disabled { opacity: 0.5; cursor: default; } +.auth-profile-cancel-btn { + background: var(--bg); + color: var(--text); + border: 1px solid var(--border); +} + +.auth-profile-msg { + font-size: 12px; + min-height: 16px; + margin-top: 6px; +} +.auth-profile-msg-ok { color: #4caf7d; } +.auth-profile-msg-err { color: #e85b5b; } + @media (max-width: 900px) { .widget-chat-modal { width: min(96vw, 720px); diff --git a/tests/auth-session.test.mts b/tests/auth-session.test.mts new file mode 100644 index 000000000..4f2efdd76 --- /dev/null +++ b/tests/auth-session.test.mts @@ -0,0 +1,291 @@ +/** + * Tests for server/auth-session.ts (Clerk JWT verification with jose) + * + * Covers the full validation matrix: + * - Returns invalid when CLERK_JWT_ISSUER_DOMAIN is not set (fail-closed) + * - Valid Pro token → { valid: true, role: 'pro' } + * - Valid Free token → { valid: true, role: 'free' } + * - Missing plan claim → defaults to 'free' + * - Expired token → { valid: false } + * - Invalid signature → { valid: false } + * - Wrong audience → { valid: false } + * - JWKS resolver is reused across calls (module-scoped, not per-request) + */ + +import assert from 'node:assert/strict'; +import { createServer, type Server } from 'node:http'; +import { describe, it, before, after } from 'node:test'; +import { generateKeyPair, exportJWK, SignJWT } from 'jose'; + +// --------------------------------------------------------------------------- +// Suite 1: fail-closed when CLERK_JWT_ISSUER_DOMAIN is NOT set +// --------------------------------------------------------------------------- + +// Clear env BEFORE dynamic import so the module captures an empty domain +delete process.env.CLERK_JWT_ISSUER_DOMAIN; + +let validateBearerTokenNoEnv: (token: string) => Promise<{ valid: boolean; userId?: string; role?: string }>; + +before(async () => { + const mod = await import('../server/auth-session.ts'); + validateBearerTokenNoEnv = mod.validateBearerToken; +}); + +describe('validateBearerToken (no CLERK_JWT_ISSUER_DOMAIN)', () => { + it('returns invalid when CLERK_JWT_ISSUER_DOMAIN is not set', async () => { + const result = await validateBearerTokenNoEnv('some-random-token'); + assert.equal(result.valid, false); + assert.equal(result.userId, undefined); + assert.equal(result.role, undefined); + }); + + it('returns invalid for empty token', async () => { + const result = await validateBearerTokenNoEnv(''); + assert.equal(result.valid, false); + }); + + it('returns SessionResult shape with expected fields', async () => { + const result = await validateBearerTokenNoEnv('test'); + assert.equal(typeof result.valid, 'boolean'); + if (!result.valid) { + assert.equal(result.userId, undefined); + assert.equal(result.role, undefined); + } + }); +}); + +// --------------------------------------------------------------------------- +// Suite 2: full JWT validation with self-signed keys + local JWKS server +// --------------------------------------------------------------------------- + +describe('validateBearerToken (with JWKS)', () => { + let privateKey: CryptoKey; + let jwksServer: Server; + let jwksPort: number; + let validateBearerToken: (token: string) => Promise<{ valid: boolean; userId?: string; role?: string }>; + + // Separate key pair for "wrong key" tests + let wrongPrivateKey: CryptoKey; + + before(async () => { + // Generate an RSA key pair for signing JWTs + const { publicKey, privateKey: pk } = await generateKeyPair('RS256'); + privateKey = pk; + + const { privateKey: wpk } = await generateKeyPair('RS256'); + wrongPrivateKey = wpk; + + // Export public key as JWK for the JWKS endpoint + const publicJwk = await exportJWK(publicKey); + publicJwk.kid = 'test-key-1'; + publicJwk.alg = 'RS256'; + publicJwk.use = 'sig'; + const jwks = { keys: [publicJwk] }; + + // Start a local HTTP server serving the JWKS + jwksServer = createServer((req, res) => { + if (req.url === '/.well-known/jwks.json') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(jwks)); + } else { + res.writeHead(404); + res.end(); + } + }); + + await new Promise((resolve) => { + jwksServer.listen(0, '127.0.0.1', () => resolve()); + }); + const addr = jwksServer.address(); + jwksPort = typeof addr === 'object' && addr ? addr.port : 0; + + // Set the issuer domain to the local JWKS server and re-import the module + // (fresh import since the module caches JWKS at first use) + process.env.CLERK_JWT_ISSUER_DOMAIN = `http://127.0.0.1:${jwksPort}`; + + // Dynamic import with cache-busting query param to get a fresh module instance + const mod = await import(`../server/auth-session.ts?t=${Date.now()}`); + validateBearerToken = mod.validateBearerToken; + }); + + after(async () => { + jwksServer?.close(); + delete process.env.CLERK_JWT_ISSUER_DOMAIN; + }); + + /** Helper to sign a JWT with the test private key */ + function signToken(claims: Record, opts?: { expiresIn?: string; key?: CryptoKey }) { + const builder = new SignJWT(claims) + .setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' }) + .setIssuer(`http://127.0.0.1:${jwksPort}`) + .setAudience('convex') + .setSubject(claims.sub as string ?? 'user_test123') + .setIssuedAt(); + + if (opts?.expiresIn) { + builder.setExpirationTime(opts.expiresIn); + } else { + builder.setExpirationTime('1h'); + } + + return builder.sign(opts?.key ?? privateKey); + } + + it('accepts a valid Pro token', async () => { + const token = await signToken({ sub: 'user_pro1', plan: 'pro' }); + const result = await validateBearerToken(token); + assert.equal(result.valid, true); + assert.equal(result.userId, 'user_pro1'); + assert.equal(result.role, 'pro'); + }); + + it('accepts a valid Free token and normalizes role to free', async () => { + const token = await signToken({ sub: 'user_free1', plan: 'free' }); + const result = await validateBearerToken(token); + assert.equal(result.valid, true); + assert.equal(result.userId, 'user_free1'); + assert.equal(result.role, 'free'); + }); + + it('treats missing plan claim as free', async () => { + const token = await signToken({ sub: 'user_noplan' }); + const result = await validateBearerToken(token); + assert.equal(result.valid, true); + assert.equal(result.userId, 'user_noplan'); + assert.equal(result.role, 'free'); + }); + + it('treats unknown plan value as free', async () => { + const token = await signToken({ sub: 'user_weird', plan: 'enterprise' }); + const result = await validateBearerToken(token); + assert.equal(result.valid, true); + assert.equal(result.userId, 'user_weird'); + assert.equal(result.role, 'free'); + }); + + it('rejects an expired token', async () => { + const token = await new SignJWT({ sub: 'user_expired', plan: 'pro' }) + .setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' }) + .setIssuer(`http://127.0.0.1:${jwksPort}`) + .setAudience('convex') + .setSubject('user_expired') + .setIssuedAt(Math.floor(Date.now() / 1000) - 7200) // 2h ago + .setExpirationTime(Math.floor(Date.now() / 1000) - 3600) // expired 1h ago + .sign(privateKey); + + const result = await validateBearerToken(token); + assert.equal(result.valid, false); + }); + + it('rejects a token signed with wrong key', async () => { + const token = await signToken({ sub: 'user_wrongkey', plan: 'pro' }, { key: wrongPrivateKey }); + const result = await validateBearerToken(token); + assert.equal(result.valid, false); + }); + + it('rejects a token with wrong audience', async () => { + const token = await new SignJWT({ sub: 'user_wrongaud', plan: 'pro' }) + .setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' }) + .setIssuer(`http://127.0.0.1:${jwksPort}`) + .setAudience('wrong-audience') + .setSubject('user_wrongaud') + .setIssuedAt() + .setExpirationTime('1h') + .sign(privateKey); + + const result = await validateBearerToken(token); + assert.equal(result.valid, false); + }); + + it('rejects a token with wrong issuer', async () => { + const token = await new SignJWT({ sub: 'user_wrongiss', plan: 'pro' }) + .setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' }) + .setIssuer('https://wrong-issuer.example.com') + .setAudience('convex') + .setSubject('user_wrongiss') + .setIssuedAt() + .setExpirationTime('1h') + .sign(privateKey); + + const result = await validateBearerToken(token); + assert.equal(result.valid, false); + }); + + it('rejects a token with no sub claim', async () => { + const token = await new SignJWT({ plan: 'pro' }) + .setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' }) + .setIssuer(`http://127.0.0.1:${jwksPort}`) + .setAudience('convex') + .setIssuedAt() + .setExpirationTime('1h') + .sign(privateKey); + + const result = await validateBearerToken(token); + assert.equal(result.valid, false); + }); + + it('reuses the JWKS resolver across calls (not per-request)', async () => { + // Make two calls — both should succeed using the same cached JWKS + const token1 = await signToken({ sub: 'user_a', plan: 'pro' }); + const token2 = await signToken({ sub: 'user_b', plan: 'free' }); + + const [r1, r2] = await Promise.all([ + validateBearerToken(token1), + validateBearerToken(token2), + ]); + + assert.equal(r1.valid, true); + assert.equal(r1.role, 'pro'); + assert.equal(r2.valid, true); + assert.equal(r2.role, 'free'); + }); +}); + +// --------------------------------------------------------------------------- +// Suite 3: CORS origin matching -- pure logic (independent of auth provider) +// --------------------------------------------------------------------------- + +describe('CORS origin matching (convex/http.ts)', () => { + function matchOrigin(origin: string, pattern: string): boolean { + if (pattern.startsWith('*.')) { + return origin.endsWith(pattern.slice(1)); + } + return origin === pattern; + } + + function allowedOrigin(origin: string | null, trusted: string[]): string | null { + if (!origin) return null; + return trusted.some((p) => matchOrigin(origin, p)) ? origin : null; + } + + const TRUSTED = [ + 'https://worldmonitor.app', + '*.worldmonitor.app', + 'http://localhost:3000', + ]; + + it('allows exact match', () => { + assert.equal(allowedOrigin('https://worldmonitor.app', TRUSTED), 'https://worldmonitor.app'); + }); + + it('allows wildcard subdomain', () => { + const origin = 'https://preview-xyz.worldmonitor.app'; + assert.equal(allowedOrigin(origin, TRUSTED), origin); + }); + + it('allows localhost', () => { + assert.equal(allowedOrigin('http://localhost:3000', TRUSTED), 'http://localhost:3000'); + }); + + it('blocks unknown origin', () => { + assert.equal(allowedOrigin('https://evil.com', TRUSTED), null); + }); + + it('blocks partial domain match', () => { + assert.equal(allowedOrigin('https://attackerworldmonitor.app', TRUSTED), null); + }); + + it('returns null for null origin -- no ACAO header emitted', () => { + assert.equal(allowedOrigin(null, TRUSTED), null); + }); +}); diff --git a/tests/deploy-config.test.mjs b/tests/deploy-config.test.mjs index 832344319..8832fb347 100644 --- a/tests/deploy-config.test.mjs +++ b/tests/deploy-config.test.mjs @@ -155,6 +155,24 @@ describe('security header guardrails', () => { assert.ok(scriptSrc.includes("'self'"), 'CSP script-src must include self'); }); + it('CSP script-src includes Clerk origin for auth UI', () => { + const csp = getHeaderValue('Content-Security-Policy'); + const scriptSrc = csp.match(/script-src\s+([^;]+)/)?.[1] ?? ''; + assert.ok( + scriptSrc.includes('clerk.accounts.dev') || scriptSrc.includes('clerk.worldmonitor.app'), + 'CSP script-src must include Clerk origin for auth UI to load' + ); + }); + + it('CSP frame-src includes Clerk origin for auth modals', () => { + const csp = getHeaderValue('Content-Security-Policy'); + const frameSrc = csp.match(/frame-src\s+([^;]+)/)?.[1] ?? ''; + assert.ok( + frameSrc.includes('clerk.accounts.dev') || frameSrc.includes('clerk.worldmonitor.app'), + 'CSP frame-src must include Clerk origin for sign-in modal' + ); + }); + it('security.txt exists in public/.well-known/', () => { const secTxt = readFileSync(resolve(__dirname, '../public/.well-known/security.txt'), 'utf-8'); assert.match(secTxt, /^Contact:/m, 'security.txt must have a Contact field'); diff --git a/tests/helpers/runtime-config-panel-harness.mjs b/tests/helpers/runtime-config-panel-harness.mjs index 046825934..5cf7a6ddf 100644 --- a/tests/helpers/runtime-config-panel-harness.mjs +++ b/tests/helpers/runtime-config-panel-harness.mjs @@ -484,6 +484,10 @@ async function loadRuntimeConfigPanel() { export const PLAINTEXT_KEYS = new Set(); export const MASKED_SENTINEL = '***'; `], + ['panel-gating-stub', ` + export const PanelGateReason = { NONE: 'none', ANONYMOUS: 'anonymous', UNVERIFIED: 'unverified', FREE_TIER: 'free_tier' }; + export function getPanelGateReason() { return PanelGateReason.NONE; } + `], ]); const aliasMap = new Map([ @@ -500,6 +504,7 @@ async function loadRuntimeConfigPanel() { ['@/utils/sanitize', 'sanitize-stub'], ['@/services/ollama-models', 'ollama-models-stub'], ['@/services/settings-constants', 'settings-constants-stub'], + ['@/services/panel-gating', 'panel-gating-stub'], ]); const plugin = { diff --git a/tests/premium-stock-gateway.test.mts b/tests/premium-stock-gateway.test.mts index b765af4f9..c234a6b37 100644 --- a/tests/premium-stock-gateway.test.mts +++ b/tests/premium-stock-gateway.test.mts @@ -1,5 +1,7 @@ import assert from 'node:assert/strict'; -import { afterEach, describe, it } from 'node:test'; +import { createServer, type Server } from 'node:http'; +import { afterEach, describe, it, before, after } from 'node:test'; +import { generateKeyPair, exportJWK, SignJWT } from 'jose'; import { createDomainGateway } from '../server/gateway.ts'; @@ -11,7 +13,7 @@ afterEach(() => { }); describe('premium stock gateway enforcement', () => { - it('allows trusted browser origins without a key (client-side isProUser gate controls access)', async () => { + it('requires credentials for premium endpoints regardless of origin', async () => { const handler = createDomainGateway([ { method: 'GET', @@ -27,11 +29,11 @@ describe('premium stock gateway enforcement', () => { process.env.WORLDMONITOR_VALID_KEYS = 'real-key-123'; - // Trusted browser origin (worldmonitor.app) — no key needed; isProUser() is the client-side gate + // Trusted browser origin without credentials — 401 (Origin is spoofable, not a security boundary) const browserNoKey = await handler(new Request('https://worldmonitor.app/api/market/v1/analyze-stock?symbol=AAPL', { headers: { Origin: 'https://worldmonitor.app' }, })); - assert.equal(browserNoKey.status, 200); + assert.equal(browserNoKey.status, 401); // Trusted browser origin with a valid key — also allowed const browserWithKey = await handler(new Request('https://worldmonitor.app/api/market/v1/analyze-stock?symbol=AAPL', { @@ -55,3 +57,119 @@ describe('premium stock gateway enforcement', () => { assert.equal(publicAllowed.status, 200); }); }); + +// --------------------------------------------------------------------------- +// Bearer token auth path for premium endpoints +// --------------------------------------------------------------------------- + +describe('premium stock gateway bearer token auth', () => { + let privateKey: CryptoKey; + let wrongPrivateKey: CryptoKey; + let jwksServer: Server; + let jwksPort: number; + let handler: (req: Request) => Promise; + + before(async () => { + const { publicKey, privateKey: pk } = await generateKeyPair('RS256'); + privateKey = pk; + + const { privateKey: wpk } = await generateKeyPair('RS256'); + wrongPrivateKey = wpk; + + const publicJwk = await exportJWK(publicKey); + publicJwk.kid = 'test-key-1'; + publicJwk.alg = 'RS256'; + publicJwk.use = 'sig'; + const jwks = { keys: [publicJwk] }; + + jwksServer = createServer((req, res) => { + if (req.url === '/.well-known/jwks.json') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(jwks)); + } else { + res.writeHead(404); + res.end(); + } + }); + + await new Promise((resolve) => { + jwksServer.listen(0, '127.0.0.1', () => resolve()); + }); + const addr = jwksServer.address(); + jwksPort = typeof addr === 'object' && addr ? addr.port : 0; + + process.env.CLERK_JWT_ISSUER_DOMAIN = `http://127.0.0.1:${jwksPort}`; + process.env.WORLDMONITOR_VALID_KEYS = 'real-key-123'; + + handler = createDomainGateway([ + { + method: 'GET', + path: '/api/market/v1/analyze-stock', + handler: async () => new Response(JSON.stringify({ ok: true }), { status: 200 }), + }, + { + method: 'GET', + path: '/api/market/v1/list-market-quotes', + handler: async () => new Response(JSON.stringify({ ok: true }), { status: 200 }), + }, + ]); + }); + + after(async () => { + jwksServer?.close(); + delete process.env.CLERK_JWT_ISSUER_DOMAIN; + }); + + function signToken(claims: Record, opts?: { key?: CryptoKey; audience?: string }) { + return new SignJWT(claims) + .setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' }) + .setIssuer(`http://127.0.0.1:${jwksPort}`) + .setAudience(opts?.audience ?? 'convex') + .setSubject(claims.sub as string ?? 'user_test') + .setIssuedAt() + .setExpirationTime('1h') + .sign(opts?.key ?? privateKey); + } + + it('accepts valid Pro bearer token on premium endpoint → 200', async () => { + const token = await signToken({ sub: 'user_pro', plan: 'pro' }); + const res = await handler(new Request('https://worldmonitor.app/api/market/v1/analyze-stock?symbol=AAPL', { + headers: { + Origin: 'https://worldmonitor.app', + Authorization: `Bearer ${token}`, + }, + })); + assert.equal(res.status, 200); + }); + + it('rejects Free bearer token on premium endpoint → 403', async () => { + const token = await signToken({ sub: 'user_free', plan: 'free' }); + const res = await handler(new Request('https://worldmonitor.app/api/market/v1/analyze-stock?symbol=AAPL', { + headers: { + Origin: 'https://worldmonitor.app', + Authorization: `Bearer ${token}`, + }, + })); + assert.equal(res.status, 403); + const body = await res.json() as { error: string }; + assert.match(body.error, /[Pp]ro/); + }); + + it('rejects invalid/expired bearer token on premium endpoint → 401', async () => { + const token = await signToken({ sub: 'user_bad', plan: 'pro' }, { key: wrongPrivateKey }); + const res = await handler(new Request('https://worldmonitor.app/api/market/v1/analyze-stock?symbol=AAPL', { + headers: { + Origin: 'https://worldmonitor.app', + Authorization: `Bearer ${token}`, + }, + })); + assert.equal(res.status, 401); + }); + + it('public routes are unaffected by absence of auth header', async () => { + const res = await handler(new Request('https://worldmonitor.app/api/market/v1/list-market-quotes?symbols=AAPL', { + headers: { Origin: 'https://worldmonitor.app' }, + })); + assert.equal(res.status, 200); + }); +}); diff --git a/vercel.json b/vercel.json index 7c1bf94da..788e80429 100644 --- a/vercel.json +++ b/vercel.json @@ -34,7 +34,7 @@ { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" }, { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }, { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=(self), accelerometer=(), autoplay=(self \"https://www.youtube.com\" \"https://www.youtube-nocookie.com\"), bluetooth=(), display-capture=(), encrypted-media=(self \"https://www.youtube.com\" \"https://www.youtube-nocookie.com\"), gyroscope=(), hid=(), idle-detection=(), magnetometer=(), midi=(), payment=(), picture-in-picture=(self \"https://www.youtube.com\" \"https://www.youtube-nocookie.com\"), screen-wake-lock=(), serial=(), usb=(), xr-spatial-tracking=()" }, - { "key": "Content-Security-Policy", "value": "default-src 'self'; connect-src 'self' https: wss: blob: data:; img-src 'self' data: blob: https:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' 'sha256-LnMFPWZxTgVOr2VYwIh9mhQ3l/l3+a3SfNOLERnuHfY=' 'sha256-4Z2xtr1B9QQugoojE/nbpOViG+8l2B7CZVlKgC78AeQ=' 'sha256-903UI9my1I7mqHoiVeZSc56yd50YoRJTB2269QqL76w=' 'wasm-unsafe-eval' https://www.youtube.com https://static.cloudflareinsights.com https://vercel.live https://challenges.cloudflare.com https://abacus.worldmonitor.app; worker-src 'self' blob:; font-src 'self' data: https:; media-src 'self' data: blob: https:; frame-src 'self' https://worldmonitor.app https://tech.worldmonitor.app https://finance.worldmonitor.app https://commodity.worldmonitor.app https://happy.worldmonitor.app https://www.youtube.com https://www.youtube-nocookie.com https://webcams.windy.com https://challenges.cloudflare.com https://vercel.live https://*.vercel.app; frame-ancestors 'self' https://www.worldmonitor.app https://tech.worldmonitor.app https://finance.worldmonitor.app https://commodity.worldmonitor.app https://happy.worldmonitor.app https://worldmonitor.app https://vercel.live https://*.vercel.app; base-uri 'self'; object-src 'none'; form-action 'self'" } + { "key": "Content-Security-Policy", "value": "default-src 'self'; connect-src 'self' https: wss: blob: data:; img-src 'self' data: blob: https:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' 'sha256-LnMFPWZxTgVOr2VYwIh9mhQ3l/l3+a3SfNOLERnuHfY=' 'sha256-4Z2xtr1B9QQugoojE/nbpOViG+8l2B7CZVlKgC78AeQ=' 'sha256-903UI9my1I7mqHoiVeZSc56yd50YoRJTB2269QqL76w=' 'wasm-unsafe-eval' https://www.youtube.com https://static.cloudflareinsights.com https://vercel.live https://challenges.cloudflare.com https://*.clerk.accounts.dev https://abacus.worldmonitor.app; worker-src 'self' blob:; font-src 'self' data: https:; media-src 'self' data: blob: https:; frame-src 'self' https://worldmonitor.app https://tech.worldmonitor.app https://finance.worldmonitor.app https://commodity.worldmonitor.app https://happy.worldmonitor.app https://www.youtube.com https://www.youtube-nocookie.com https://webcams.windy.com https://challenges.cloudflare.com https://*.clerk.accounts.dev https://vercel.live https://*.vercel.app; frame-ancestors 'self' https://www.worldmonitor.app https://tech.worldmonitor.app https://finance.worldmonitor.app https://commodity.worldmonitor.app https://happy.worldmonitor.app https://worldmonitor.app https://vercel.live https://*.vercel.app; base-uri 'self'; object-src 'none'; form-action 'self'" } ] }, {