diff --git a/LICENSE b/LICENSE index 9e69d0f..4732fe2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,8 @@ -MIT License +MintPass is a mixed-license monorepo for the public, open-source MintPass packages. -Copyright (c) 2025 Bitsocial +The controlling license depends on the package you are using: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +- `challenge` (`@bitsocial/mintpass-challenge`): `GPL-3.0-or-later`. See `challenge/LICENSE`. +- `contracts` (`@bitsocial/mintpass-contracts`): `MIT`. See `contracts/LICENSE`. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +The hosted MintPass web app (the SMS-OTP and minting service for mintpass.org) is proprietary and maintained separately in a private repository; it is not included here. Unless a subdirectory says otherwise, root workspace files are provided for monorepo development only and are not offered as a separate redistributable package. diff --git a/README.md b/README.md index 56b147b..9737202 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ MintPass integrates as a challenge so Bitsocial communities can distinguish real mintpass/ ├── contracts/ # MintPassV1 smart contract and tooling ├── challenge/ # Bitsocial challenge implementation (“mintpass”) -├── web/ # Next.js website + API (mintpass.org) ├── docs/ # Documentation and specifications ├── tests/ # Cross‑component integration tests └── scripts/ # Deployment and utilities @@ -42,7 +41,7 @@ mintpass/ - `contracts/`: Solidity contracts (MintPassV1). Versioned, role‑based minting, token types per NFT (type 0 = SMS). See `contracts/README.md`. - `challenge/`: The Bitsocial challenge that checks for a MintPass NFT and applies additional rules (e.g., transfer cooldowns) to resist sybils. -- `web/`: The user‑facing site and serverless backend. Sends SMS codes, verifies OTP, and mints or records successful verification. See `web/README.md`. +- The hosted MintPass web app (the `mintpass.org` site + SMS-OTP / minting API) is **proprietary** and maintained in a separate private repository; it is not part of this public monorepo. ## Privacy and anti‑sybil design (high level) @@ -59,7 +58,6 @@ mintpass/ - Contracts: `cd contracts && yarn install && yarn test` - Challenge: `cd challenge && yarn install && yarn test` -- Web: `cd web && yarn install && yarn dev` then open `https://mintpass.localhost/request` ## Using MintPass in your community @@ -195,6 +193,9 @@ These items are exploratory; concrete work will land incrementally and stay conf ## License -MIT License — see [LICENSE](LICENSE). +MintPass is a mixed-license monorepo — see [LICENSE](LICENSE): -Open source and commercial‑friendly. A hosted version is available at [mintpass.org](https://mintpass.org). +- `challenge/` (`@bitsocial/mintpass-challenge`): GPL-3.0-or-later +- `contracts/` (`@bitsocial/mintpass-contracts`): MIT + +The hosted MintPass web app is proprietary and maintained in a separate private repository. A hosted version is available at [mintpass.org](https://mintpass.org). diff --git a/package.json b/package.json index 4d6e349..e3e3df3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "description": "NFT-based authentication system for PKC communities", "author": "Bitsocial", - "license": "MIT", + "license": "UNLICENSED", "exports": { "./challenge": { "types": "./dist/challenge/index.d.ts", diff --git a/scripts/llms-config.json b/scripts/llms-config.json index 1626857..7da0b4a 100644 --- a/scripts/llms-config.json +++ b/scripts/llms-config.json @@ -4,8 +4,8 @@ "repoUrl": "https://github.com/bitsocialnet/mintpass", "llmsUrl": "https://mintpass.org/llms.txt", "llmsFullUrl": "https://mintpass.org/llms-full.txt", - "shortOutputs": ["llms.txt", "web/public/llms.txt"], - "fullOutputs": ["llms-full.txt", "web/public/llms-full.txt"], + "shortOutputs": ["llms.txt"], + "fullOutputs": ["llms-full.txt"], "links": [ ["Website", "https://mintpass.org"], ["Repository", "https://github.com/bitsocialnet/mintpass"], @@ -13,14 +13,13 @@ ["Bitsocial protocol", "https://bitsocial.net"] ], "notes": [ - "This is a multi-project repo: web, contracts, challenge, docs, and generated challenge artifacts.", + "This is a multi-project repo: contracts, challenge, docs, and generated challenge artifacts.", "Secrets and private deployment values belong in environment variables or private operator files, never in committed docs.", "Generated llms files are compiled context for AI contributors and deployed site readers; verify behavior against source." ], "docs": [ "README.md", "AGENTS.md", - "web/README.md", "challenge/README.md", "contracts/README.md", "docs/pkc-info.md", diff --git a/web/.env.example b/web/.env.example deleted file mode 100644 index fc50575..0000000 --- a/web/.env.example +++ /dev/null @@ -1,36 +0,0 @@ -# Vercel KV / Upstash Redis -KV_REST_API_URL="" -KV_REST_API_TOKEN="" - -# Server-held minter private key (DO NOT COMMIT REAL KEY) -MINTER_PRIVATE_KEY="" - -# Twilio Verify (Verify-first OTP; required in production for Verify mode) -TWILIO_ACCOUNT_SID="" -TWILIO_AUTH_TOKEN="" -TWILIO_VERIFY_SERVICE_SID="" - -# Legacy SMS sender envs (deprecated for OTP path) -# TWILIO_MESSAGING_SERVICE_SID="" -# SMS_SENDER_ID="MintPass" -SMS_PROVIDER_API_KEY="" - -# Preview/local smoke fallback token -SMOKE_TEST_TOKEN="" - -# Optional security policy -BLOCKED_COUNTRIES="" - -# Rate limiting -RATE_LIMIT_WINDOW_SECONDS="60" -RATE_LIMIT_MAX_REQUESTS="10" - -# Cooldowns -SMS_SEND_COOLDOWN_SECONDS="120" -MINT_IP_COOLDOWN_SECONDS="604800" # 7 days - -# Optional external intelligence providers -# IPQS for VPN/proxy/cloud detection -IPQS_API_KEY="" -# AbstractAPI (or similar) for phone risk and disposable detection -ABSTRACTAPI_PHONE_KEY="" \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore deleted file mode 100644 index 7b8da95..0000000 --- a/web/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* -!.env.example - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/web/.yarnrc.yml b/web/.yarnrc.yml deleted file mode 100644 index 3186f3f..0000000 --- a/web/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/web/README.md b/web/README.md deleted file mode 100644 index f8b613a..0000000 --- a/web/README.md +++ /dev/null @@ -1,178 +0,0 @@ -## MintPass Web - -Serverless website and API that power the SMS verification flow and NFT minting at `mintpass.org`. - -### How it works (code map) -- UI: `src/pages/request/` — phone + OTP form, error handling, and success state -- API routes: - - `POST /api/sms/send` → rate-limit, optional IP/phone risk checks, then start Twilio Verify SMS (or non-prod smoke `kv-fallback`); returns `mode` (`verify` | `kv-fallback`) - - `POST /api/sms/verify` → verify OTP, mark phone as verified - - `POST /api/pre-check-eligibility` → quick check before sending code - - `POST /api/check-eligibility` → final eligibility check before mint - - `POST /api/mint` → on‑chain mint (when envs set) or record as minted -- Libraries and helpers: `lib/` — KV (Upstash), rate‑limits, cooldowns, hashing, SMS provider adapter, IP/phone intelligence, and admin auth/session utilities - -### Anti-Sybil and Security Requirements -- **Phone number database**: Track used numbers; prevent reuse for minting. -- **IP address tracking**: Rate limit per IP; detect abuse patterns. -- **SMS verification state**: Verify with Twilio Verify; keep short-lived fallback codes only for smoke flow. -- **Minter key security**: Private key only in Vercel env vars; never in code or logs. -- **Rate limiting**: Global and per-route limits to mitigate spam. -- **Geolocation filtering**: Optional country blocking via `middleware.ts`. -- **Audit trail (optional)**: Log key events to a separate store with redaction. - - **VPN/Proxy detection (optional)**: If `IPQS_API_KEY` is set, block VPNs/proxies/cloud provider IPs. - - **Disposable/VOIP phone detection (optional)**: If `ABSTRACTAPI_PHONE_KEY` is set, block disposable/VOIP/high-risk numbers. - - **Cooldowns**: Per-IP mint cooldown and per-IP/phone SMS send cooldowns configurable via env. - - **Keyed hashing (pepper)**: If `HASH_PEPPER` is set, phone numbers and IPs are stored as HMAC-SHA256 digests (domain-separated) in KV keys to reduce offline brute-force risk if a DB-only leak occurs. - -#### Preview-only testing behavior -- In Vercel Preview deployments (`VERCEL_ENV=preview`), the disposable/VOIP/high-risk phone check is bypassed in `POST /api/sms/send` to allow testing with temporary numbers. Production (`VERCEL_ENV=production`) remains strict and continues to block such numbers. - -### Vercel Setup (exact steps) -1. Create a new Vercel project and select this repo. Set root directory to `web`. -2. Add storage via Marketplace: Upstash → Redis. Create separate databases per environment (recommended): - - `mintpass-kv-prod` (Production) - - `mintpass-kv-preview` (Preview) - - `mintpass-kv-dev` (optional local) - Settings for each DB: Plan = Pay as You Go, Primary Region = `iad1` (US‑East), Read Regions = none, Eviction = off, Auto‑upgrade = on. -3. In Project Settings → Environment Variables, add: - - `MINTER_PRIVATE_KEY` (server only) - - `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_VERIFY_SERVICE_SID` (Verify mode; recommended for OTP) - - `BLOCKED_COUNTRIES` (comma-separated ISO codes if needed) - - `RATE_LIMIT_WINDOW_SECONDS`, `RATE_LIMIT_MAX_REQUESTS` (optional) - - `IPQS_API_KEY` (optional) to enable IP reputation checks - - `ABSTRACTAPI_PHONE_KEY` (optional) to enable disposable/VOIP phone checks - - `SMS_SEND_COOLDOWN_SECONDS` (optional) default 120 - - `MINT_IP_COOLDOWN_SECONDS` (optional) default 604800 (7 days) - - Map Upstash credentials to app envs per environment: - - Production: `KV_REST_API_URL` = Upstash prod REST URL; `KV_REST_API_TOKEN` = Upstash prod REST token - - Preview: `KV_REST_API_URL` = Upstash preview REST URL; `KV_REST_API_TOKEN` = Upstash preview REST token - - Local dev (optional): set the same in `.env.local` pointing to `mintpass-kv-dev` -4. Deploy. After first deploy, add the domain `mintpass.org` in Domains, set as primary. -5. Ensure the KV database is scoped to the production environment and not shared with preview. - -### Runtime regions -- Vercel Project → Settings → Functions → Region: set to `iad1` to co‑locate with Redis and reduce latency. - -### Storage notes -- Why Redis/KV: fast TTL keys for OTPs and cooldowns, atomic counters, and simple idempotency. -- Cost model (Pay as You Go): ~$0.20 per 100k commands. Typical flow is ~20 commands per successful mint. - -### Environments and KV mapping (prod/preview/local) -- **Databases to create**: - - `mintpass-kv-prod` → used by Vercel Production - - `mintpass-kv-preview` → used by Vercel Preview (and local dev by default) - - Optional later: `mintpass-kv-dev` → used only for local dev - -- **Vercel → Project → Settings → Environment Variables**: - - Production: set `KV_REST_API_URL` and `KV_REST_API_TOKEN` from `mintpass-kv-prod` - - Preview: set `KV_REST_API_URL` and `KV_REST_API_TOKEN` from `mintpass-kv-preview` - - Do not point local or preview to prod. - -- **Local development**: - - Copy `.env.example` to `.env.local` - - Set `KV_REST_API_URL` and `KV_REST_API_TOKEN` to the preview DB (or to `mintpass-kv-dev` if you created it) - - Run `yarn dev` - -### API Routes -- `POST /api/sms/send` → request SMS code (rate-limited; Verify-first with non-prod smoke fallback) -- `POST /api/sms/verify` → verify code and mark phone as verified -- `POST /api/check-eligibility` → confirm address + phone can mint -- `POST /api/mint` → on-chain mint on Base Sepolia when envs are set; otherwise records local minted state. Requires `authorAddress` string; country is derived from `x-vercel-ip-country` header. - -### Local Development - -Requires [Portless](https://github.com/vercel-labs/portless), installed with project dependencies via `corepack yarn install` - -```bash -corepack enable -corepack yarn install -corepack yarn dev # https://mintpass.localhost -``` - -The dev server runs at https://mintpass.localhost via [Portless](https://github.com/vercel-labs/portless), which gives each Bitsocial project a stable, named URL instead of a random port. To bypass Portless: `PORTLESS=0 yarn dev` - -To test API calls locally, use `curl` or your preferred REST client. - -### Smoke test (prod/preview) -Create local env files (not committed) with the target base URL and test identifiers. Include Upstash REST URL/Token when you need `kv-fallback` full E2E: - -- `.env.smoke.prod` (for Production) -``` -BASE_URL=https://mintpass.org -PHONE=+15555550123 -ADDR=0x1111111111111111111111111111111111111111 -``` - -- `.env.smoke.preview` (for Preview) -``` -KV_REST_API_URL= -KV_REST_API_TOKEN= -PREVIEW_BASE_URL=https://.vercel.app -PHONE=+15555550123 -ADDR=0x1111111111111111111111111111111111111111 -BYPASS_TOKEN=... -SMOKE_TEST_TOKEN=... -``` - -Run the script: -```bash -yarn smoke:prod -# or -yarn smoke:preview -``` - -Notes: -- The script inspects `POST /api/sms/send` response JSON for `mode` (`verify` | `kv-fallback`). -- **mode=kv-fallback**: runs full E2E (fetch code from KV/debug → verify → eligibility → mint). -- **mode=verify + prod**: send-health (send ok) + eligibility check only; OTP verify/mint skipped by design. -- **mode=verify + preview**: full E2E only when fallback is used; otherwise OTP verify/mint skipped with clear message. -- `KV_REST_API_URL` and `KV_REST_API_TOKEN` are required only when `mode=kv-fallback`. -- `BYPASS_TOKEN` and `SMOKE_TEST_TOKEN` supported. -- The script loads ENVFILE (if provided), then `.env.smoke.{prod|preview}`, then `.env.local`, then `.env`. -- No SMS provider needed for kv-fallback; OTP is read from KV via REST for testing. -- Cooldowns and rate limits apply; you may need to wait 120s for repeated runs. - - If your Preview custom domain is protected via Vercel Auth and proxied by Cloudflare, prefer the vercel.app URL for smoke tests. - -### Contributor quickstart: Preview smoke test -- Create/connect a Preview Upstash KV to this project and ensure the Preview env has `KV_REST_API_URL` and `KV_REST_API_TOKEN` (use the plain names; avoid double prefixes). Optionally set `SMOKE_TEST_TOKEN` to enable a debug echo of the OTP. -- If Deployment Protection is enabled, create a Protection Bypass token and use it as `BYPASS_TOKEN` in the `.env.smoke.preview` file. The script will set the bypass cookie and header automatically. -- Trigger a new Preview deployment and copy its per-deployment vercel.app URL from Vercel → Deployments. Use that URL as `PREVIEW_BASE_URL`. -- Create `web/.env.smoke.preview` with: - - `KV_REST_API_URL`, `KV_REST_API_TOKEN` - - `PREVIEW_BASE_URL`, `PHONE`, `ADDR` - - `BYPASS_TOKEN` (only if protection is enabled) - - `SMOKE_TEST_TOKEN` (optional; returns `debugCode` during send) -- Run `corepack yarn install` then `corepack yarn smoke:preview`. -- Expected: send → verify → eligibility=true → mint ok (stubbed unless on-chain envs are set). - -### Environment Variables -Copy `.env.example` to `.env.local` and fill in values. Do not commit `.env.local`. - -Required for runtime: -- `KV_REST_API_URL`, `KV_REST_API_TOKEN` -- `HASH_PEPPER` (optional; HMAC key for hashing identifiers used in KV keys) - -Optional for SMS provider (Twilio Verify preferred): -- `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_VERIFY_SERVICE_SID` — required in production for Verify mode. Create a Verify Service in Twilio Console and use its SID. -- Legacy (kv-fallback only; deprecated for OTP): `TWILIO_MESSAGING_SERVICE_SID` or `SMS_SENDER_ID` - -Optional for on-chain mint (Base Sepolia): -- `MINTER_PRIVATE_KEY` -- `MINTPASSV1_ADDRESS_BASE_SEPOLIA` -- `BASE_SEPOLIA_RPC_URL` - -Optional for smoke testing (Preview only): -- `SMOKE_TEST_TOKEN` (debug-only echo of OTP when `x-smoke-test-token` header is present) -- `HASH_PEPPER` (optional; if set, `scripts/smoke-test.sh` derives the hashed OTP KV key via OpenSSL when available) - -### Next Steps -- Add additional authentication methods beyond SMS (e.g., pay‑to‑mint, others). -- Add abuse heuristics (velocity checks per phone/IP, lightweight device signals if needed). -- Continue UI polish and success animations; broaden admin tooling and observability. - -This project includes both backend APIs and frontend UI components built with shadcn/ui and mobile-first responsive design. - -### Operational recommendations -- Put Cloudflare in front of Vercel for additional DDoS protection and WAF/challenge. Set `CF-Connecting-IP` pass-through so backend uses real client IP. -- Monitor rate-limit headers (`X-RateLimit-*`) and adjust envs based on traffic. diff --git a/web/components.json b/web/components.json deleted file mode 100644 index 81e9184..0000000 --- a/web/components.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "tailwind.config.ts", - "css": "src/styles/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "iconLibrary": "lucide", - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "registries": { - "@alpine": "https://alpine-registry.vercel.app/r/{name}.json", - "@tailark": "https://tailark.com/r/{name}.json", - "@magicui": "https://magicui.dev/r/{name}.json", - "@shadcn-form": "https://www.shadcn-form.com/r/{name}.json", - "@kokonutui": "https://kokonutui.com/r/{name}.json", - "@diceui": "https://diceui.com/r/{name}.json", - "@basecn": "https://basecn.dev/r/{name}.json", - "@animateui": "https://animate-ui.com/r/{name}.json", - "@fancycomponents": "https://fancycomponents.dev/r/{name}.json" - } -} diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs deleted file mode 100644 index fcbac3c..0000000 --- a/web/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import nextConfig from "eslint-config-next"; - -export default nextConfig; diff --git a/web/lib/abi.ts b/web/lib/abi.ts deleted file mode 100644 index 10d6f79..0000000 --- a/web/lib/abi.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Minimal ABIs used by the backend -export const MintPassV1Abi = [ - 'function mint(address to, uint16 tokenTypeValue) external', -]; - diff --git a/web/lib/admin-auth-edge.ts b/web/lib/admin-auth-edge.ts deleted file mode 100644 index dcea47a..0000000 --- a/web/lib/admin-auth-edge.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Edge-safe admin token verification using Web Crypto API - -function toHex(buffer: ArrayBuffer): string { - const bytes = new Uint8Array(buffer); - let out = ''; - for (let i = 0; i < bytes.length; i++) { - const h = bytes[i].toString(16).padStart(2, '0'); - out += h; - } - return out; -} - -async function hmacSha256Hex(message: string, secret: string): Promise { - const enc = new TextEncoder(); - const key = await crypto.subtle.importKey( - 'raw', - enc.encode(secret), - { name: 'HMAC', hash: 'SHA-256' }, - false, - ['sign'] - ); - const sig = await crypto.subtle.sign('HMAC', key, enc.encode(message)); - return toHex(sig); -} - -export async function verifyAdminTokenEdge(token: string | undefined, secret: string | undefined): Promise { - if (!token || !secret) return false; - const parts = token.split('.'); - if (parts.length !== 2) return false; - const [payloadB64, sig] = parts; - try { - // Validate formats up-front - const isBase64Url = /^[A-Za-z0-9_-]+$/.test(payloadB64); - // sha256 HMAC hex digest should be 64 lowercase hex chars - const isHexSig = /^[0-9a-f]{64}$/.test(sig); - if (!isBase64Url || !isHexSig) return false; - - const expected = await hmacSha256Hex(payloadB64, secret); - if (expected.length !== sig.length) return false; - // timing-safe equal polyfill - let mismatch = 0; - for (let i = 0; i < expected.length; i++) { - mismatch |= expected.charCodeAt(i) ^ sig.charCodeAt(i); - } - if (mismatch !== 0) return false; - - // Verify payload with robust validation - try { - // Normalize base64url and add required padding for robust decoding - const normalized = payloadB64.replace(/-/g, '+').replace(/_/g, '/'); - const paddingLen = (4 - (normalized.length % 4)) % 4; - const padded = normalized + '='.repeat(paddingLen); - const json = atob(padded); - const payload = JSON.parse(json); - - // Validate payload structure - if (!payload || typeof payload !== 'object') return false; - const { v, iat, exp } = payload as { v: unknown; iat: unknown; exp: unknown }; - if (!Number.isFinite(v as number) || (v as number) !== 1) return false; - if (!Number.isFinite(iat as number) || !Number.isFinite(exp as number)) return false; - - const now = Math.floor(Date.now() / 1000); - const iatNum = Math.floor(iat as number); - const expNum = Math.floor(exp as number); - - // iat must be in the past (<= now) but not older than 1 day - const maxIatSkewSeconds = 24 * 60 * 60; // 1 day - if (iatNum > now) return false; - if (iatNum < now - maxIatSkewSeconds) return false; - - // exp must be in the future and not farther than 30 days from now - const maxTtlSeconds = 30 * 24 * 60 * 60; // 30 days - if (expNum <= now) return false; - if (expNum > now + maxTtlSeconds) return false; - - return true; - } catch { - return false; - } - } catch { - return false; - } -} - - diff --git a/web/lib/admin-auth.ts b/web/lib/admin-auth.ts deleted file mode 100644 index f5a90cd..0000000 --- a/web/lib/admin-auth.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { createHmac, timingSafeEqual } from 'crypto'; -import type { NextApiRequest, NextApiResponse } from 'next'; -import { getAdminSessionSecret, getAdminSessionMaxLifetimeSeconds } from './env'; - -const ADMIN_COOKIE_NAME = 'admin_session'; -const DEFAULT_SESSION_TTL_SECONDS = getAdminSessionMaxLifetimeSeconds(); - -type AdminTokenPayload = { - v: 1; - iat: number; // seconds since epoch - exp: number; // seconds since epoch -}; - -function base64urlEncode(input: string): string { - return Buffer.from(input) - .toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); -} - -function sign(data: string, secret: string): string { - return createHmac('sha256', secret).update(data).digest('hex'); -} - -export function createAdminToken(expiresInSeconds = DEFAULT_SESSION_TTL_SECONDS): string { - const secret = getAdminSessionSecret(); - if (!secret) throw new Error('ADMIN_SESSION_SECRET not configured'); - const now = Math.floor(Date.now() / 1000); - const payload: AdminTokenPayload = { v: 1, iat: now, exp: now + expiresInSeconds }; - const payloadB64 = base64urlEncode(JSON.stringify(payload)); - const sig = sign(payloadB64, secret); - return `${payloadB64}.${sig}`; -} - -export function verifyAdminToken(token: string | undefined): boolean { - if (!token) return false; - const secret = getAdminSessionSecret(); - if (!secret) return false; - const parts = token.split('.'); - if (parts.length !== 2) return false; - const [payloadB64, sig] = parts; - // Pre-validate signature format to fixed-length hex (64 chars for sha256) - if (!/^[0-9a-fA-F]{64}$/.test(sig)) return false; - // Validate payload base64url characters to avoid decode work on malformed input - if (!/^[A-Za-z0-9_-]+$/.test(payloadB64)) return false; - const expected = sign(payloadB64, secret); - const a = Buffer.from(sig, 'hex'); - const b = Buffer.from(expected, 'hex'); - // Use timingSafeEqual on same-length buffers - if (!timingSafeEqual(a, b)) return false; - try { - // Decode base64url with padding - const base64 = payloadB64.replace(/-/g, '+').replace(/_/g, '/'); - const pad = (4 - (base64.length % 4)) % 4; - const padded = base64 + '='.repeat(pad); - const json = Buffer.from(padded, 'base64').toString('utf8'); - const payload = JSON.parse(json) as AdminTokenPayload; - if (payload.v !== 1) return false; - const now = Math.floor(Date.now() / 1000); - return now < payload.exp; - } catch { - return false; - } -} - -function appendSetCookie(res: NextApiResponse, newCookie: string) { - const existing = res.getHeader('Set-Cookie'); - const existingCookies = existing - ? (Array.isArray(existing) ? existing.map(String) : [String(existing)]) - : []; - res.setHeader('Set-Cookie', [...existingCookies, newCookie]); -} - -export function setAdminSessionCookie(res: NextApiResponse, token: string, maxAgeSeconds = DEFAULT_SESSION_TTL_SECONDS) { - const isProd = process.env.NODE_ENV === 'production'; - const cookie = `${ADMIN_COOKIE_NAME}=${token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${maxAgeSeconds};${isProd ? ' Secure;' : ''}`; - appendSetCookie(res, cookie); -} - -export function clearAdminSessionCookie(res: NextApiResponse) { - const isProd = process.env.NODE_ENV === 'production'; - const cookie = `${ADMIN_COOKIE_NAME}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0;${isProd ? ' Secure;' : ''}`; - appendSetCookie(res, cookie); -} - -export function isAdminRequest(req: NextApiRequest): boolean { - const token = req.cookies?.[ADMIN_COOKIE_NAME]; - return verifyAdminToken(token); -} - -export function requireAdmin(req: NextApiRequest, res: NextApiResponse): boolean { - if (isAdminRequest(req)) return true; - res.status(401).json({ error: 'Unauthorized' }); - return false; -} - -export const AdminAuth = { - ADMIN_COOKIE_NAME, - DEFAULT_SESSION_TTL_SECONDS, - createAdminToken, - verifyAdminToken, - setAdminSessionCookie, - clearAdminSessionCookie, - isAdminRequest, - requireAdmin, -}; - - diff --git a/web/lib/cooldowns.ts b/web/lib/cooldowns.ts deleted file mode 100644 index 6453ca6..0000000 --- a/web/lib/cooldowns.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { kv } from '@vercel/kv'; -import { policy } from './policy'; -import { hashIdentifier } from './hash'; - -function smsPhoneCooldownKey(phoneE164: string) { - const h = hashIdentifier('phone', phoneE164); - return `cd:sms:phone:${h}`; -} - -function smsIpCooldownKey(ip: string) { - const h = hashIdentifier('ip', ip); - return `cd:sms:ip:${h}`; -} - -function mintIpCooldownKey(ip: string) { - const h = hashIdentifier('ip', ip); - return `cd:mint:ip:${h}`; -} - -export async function isSmsSendInCooldown(ip: string, phoneE164: string) { - const [p, i] = await Promise.all([ - kv.get(smsPhoneCooldownKey(phoneE164)), - kv.get(smsIpCooldownKey(ip)), - ]); - if (p || i) return true; - // Legacy plaintext keys fallback - const legacyP = await kv.get(`cd:sms:phone:${phoneE164}`); - const legacyI = await kv.get(`cd:sms:ip:${ip}`); - return Boolean(legacyP || legacyI); -} - -export async function getSmsSendCooldownRemaining(ip: string, phoneE164: string): Promise { - // Check TTL for both phone and IP cooldown keys - const [phoneTtl, ipTtl] = await Promise.all([ - kv.ttl(smsPhoneCooldownKey(phoneE164)), - kv.ttl(smsIpCooldownKey(ip)), - ]); - - // Also check legacy plaintext keys - const [legacyPhoneTtl, legacyIpTtl] = await Promise.all([ - kv.ttl(`cd:sms:phone:${phoneE164}`), - kv.ttl(`cd:sms:ip:${ip}`), - ]); - - // Return the maximum TTL (whichever cooldown has more time remaining) - // TTL returns -1 if key doesn't exist, -2 if key exists but has no expiry - const validTtls = [phoneTtl, ipTtl, legacyPhoneTtl, legacyIpTtl].filter(ttl => ttl > 0); - - return validTtls.length > 0 ? Math.max(...validTtls) : 0; -} - -export async function setSmsSendCooldown(ip: string, phoneE164: string) { - const ttl = Number.isFinite(policy.SMS_SEND_COOLDOWN_SECONDS) && policy.SMS_SEND_COOLDOWN_SECONDS > 0 - ? policy.SMS_SEND_COOLDOWN_SECONDS - : 120; - await Promise.all([ - kv.set(smsPhoneCooldownKey(phoneE164), '1', { ex: ttl }), - kv.set(smsIpCooldownKey(ip), '1', { ex: ttl }), - ]); -} - -export async function isMintIpInCooldown(ip: string) { - const v = await kv.get(mintIpCooldownKey(ip)); - if (v) return true; - const legacy = await kv.get(`cd:mint:ip:${ip}`); - return Boolean(legacy); -} - -export async function setMintIpCooldown(ip: string) { - const ttl = Number.isFinite(policy.MINT_IP_COOLDOWN_SECONDS) && policy.MINT_IP_COOLDOWN_SECONDS > 0 - ? policy.MINT_IP_COOLDOWN_SECONDS - : 7 * 24 * 60 * 60; // default 7 days - await kv.set(mintIpCooldownKey(ip), '1', { ex: ttl }); -} - - diff --git a/web/lib/env.ts b/web/lib/env.ts deleted file mode 100644 index f02dbf9..0000000 --- a/web/lib/env.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { z } from "zod"; - -// Guard against accidental client bundling of this module -if (typeof window !== "undefined") { - throw new Error("env.ts must not be imported on the client"); -} - -const ethAddress = z - .string() - .regex(/^0x[a-fA-F0-9]{40}$/, "Must be a valid 0x-prefixed Ethereum address"); - -const envSchema = z.object({ - // Vercel KV / Upstash Redis - KV_REST_API_URL: z.string().url(), - KV_REST_API_TOKEN: z.string().min(1), - - // Secrets and provider keys (server-only) - MINTER_PRIVATE_KEY: z.string().min(1).optional(), - SMS_PROVIDER_API_KEY: z.string().optional(), // legacy/generic - // Legacy sender ID env (deprecated for OTP Verify flow): 1-11 chars, - // must have at least one letter, only letters/digits/spaces allowed. - SMS_SENDER_ID: z - .string() - .refine((val) => { - if (!val) return true; // optional - // If it starts with + it's a phone number, skip alphanumeric validation - if (val.startsWith("+")) return true; - // Alphanumeric: max 11 chars, at least one letter, only [A-Za-z0-9 ] - return val.length <= 11 && /[A-Za-z]/.test(val) && /^[A-Za-z0-9 ]+$/.test(val); - }, "Alphanumeric sender ID must be 1-11 characters, contain at least one letter, and only use letters, digits, or spaces") - .optional(), // generic sender id/from - - // Twilio Verify (preferred OTP path) - TWILIO_ACCOUNT_SID: z.string().optional(), - TWILIO_AUTH_TOKEN: z.string().optional(), - TWILIO_VERIFY_SERVICE_SID: z.string().optional(), - // Legacy Twilio Messaging env (deprecated for OTP Verify flow) - TWILIO_MESSAGING_SERVICE_SID: z.string().optional(), - - // On-chain Mint (Base Sepolia) - MINTPASSV1_ADDRESS_BASE_SEPOLIA: ethAddress.optional(), - BASE_SEPOLIA_RPC_URL: z.string().url().optional(), - - // Preview-only smoke test helper - SMOKE_TEST_TOKEN: z.string().optional(), - // Keyed hashing pepper for identifiers (HMAC key) - HASH_PEPPER: z.string().optional(), - - // Admin credentials (server-only) - ADMIN_PASSWORD: z.string().min(12, "Admin password must be at least 12 characters").optional(), - ADMIN_SESSION_SECRET: z - .string() - .min(32, "Admin session secret must be at least 32 characters") - .optional(), - ADMIN_SESSION_MAX_LIFETIME_SECONDS: z.string().optional(), -}); - -const parsed = envSchema.safeParse(process.env as Record); - -if (!parsed.success) { - // Do not crash builds in early scaffolding if envs are missing; warn instead - // Runtime handlers will validate critical vars per-route. - console.warn( - "[env] Missing or invalid environment variables:", - parsed.error.flatten().fieldErrors, - ); -} - -export const env = { - KV_REST_API_URL: process.env.KV_REST_API_URL, - KV_REST_API_TOKEN: process.env.KV_REST_API_TOKEN, - MINTER_PRIVATE_KEY: process.env.MINTER_PRIVATE_KEY, - SMS_PROVIDER_API_KEY: process.env.SMS_PROVIDER_API_KEY, - SMS_SENDER_ID: process.env.SMS_SENDER_ID, - TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, - TWILIO_AUTH_TOKEN: process.env.TWILIO_AUTH_TOKEN, - TWILIO_VERIFY_SERVICE_SID: process.env.TWILIO_VERIFY_SERVICE_SID, - TWILIO_MESSAGING_SERVICE_SID: process.env.TWILIO_MESSAGING_SERVICE_SID, - MINTPASSV1_ADDRESS_BASE_SEPOLIA: process.env.MINTPASSV1_ADDRESS_BASE_SEPOLIA, - BASE_SEPOLIA_RPC_URL: process.env.BASE_SEPOLIA_RPC_URL, - SMOKE_TEST_TOKEN: process.env.SMOKE_TEST_TOKEN, - HASH_PEPPER: process.env.HASH_PEPPER, - ADMIN_PASSWORD: process.env.ADMIN_PASSWORD, - ADMIN_SESSION_SECRET: process.env.ADMIN_SESSION_SECRET, - ADMIN_SESSION_MAX_LIFETIME_SECONDS: process.env.ADMIN_SESSION_MAX_LIFETIME_SECONDS, -}; - -// Server-only accessors for secrets - use validated schema values -export function getAdminPassword(): string | undefined { - return env.ADMIN_PASSWORD; -} - -export function getAdminSessionSecret(): string | undefined { - return env.ADMIN_SESSION_SECRET; -} - -export function getAdminSessionMaxLifetimeSeconds(): number { - const raw = env.ADMIN_SESSION_MAX_LIFETIME_SECONDS; - const n = raw ? Number(raw) : NaN; - // Default to 8 hours if unset or invalid - if (!Number.isFinite(n) || n <= 0) return 8 * 60 * 60; - // Cap to 24 hours - return Math.min(n, 24 * 60 * 60); -} - -export function requireEnv(key: K): NonNullable<(typeof env)[K]> { - const value = env[key]; - if (value === undefined || value === null || (typeof value === "string" && value.length === 0)) { - throw new Error(`Missing required env: ${String(key)}`); - } - return value as NonNullable<(typeof env)[K]>; -} diff --git a/web/lib/hash.ts b/web/lib/hash.ts deleted file mode 100644 index 93807a5..0000000 --- a/web/lib/hash.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createHmac } from 'crypto'; -import { env } from './env'; - -// Deterministic keyed hashing for identifiers with domain separation. -// If HASH_PEPPER is not set, returns the original value for backward compatibility. -export function hashIdentifier(kind: 'phone' | 'ip' | 'addr' | 'generic', value: string): string { - const pepper = env.HASH_PEPPER; - if (pepper && pepper.length > 0) { - return createHmac('sha256', pepper).update(`${kind}:${value}`).digest('hex'); - } - return value; -} - - diff --git a/web/lib/ip-reputation.ts b/web/lib/ip-reputation.ts deleted file mode 100644 index b26fb28..0000000 --- a/web/lib/ip-reputation.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { NextApiRequest } from 'next'; - -export type IpReputation = { - ip: string; - isVpnOrProxy: boolean; - isCloudProvider: boolean; - riskScore?: number; - provider?: 'ipqs' | 'none'; -}; - -function getClientIp(req: NextApiRequest): string { - const xf = (req.headers['x-forwarded-for'] as string) || ''; - const first = xf.split(',')[0]?.trim(); - return first || req.socket.remoteAddress || 'unknown'; -} - -export async function assessIpReputation(req: NextApiRequest): Promise { - const ip = getClientIp(req); - - // If IPQS is configured, use it. Otherwise, return neutral assessment. - const ipqsKey = process.env.IPQS_API_KEY; - if (ipqsKey && ip && ip !== 'unknown') { - try { - const url = `https://ipqualityscore.com/api/json/ip/${encodeURIComponent(ipqsKey)}/${encodeURIComponent(ip)}?strictness=1&allow_public_access_points=false`; - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), 3000); - const res = await fetch(url, { method: 'GET', signal: controller.signal }); - clearTimeout(timer); - const data = (await res.json()) as { - vpn?: boolean; - proxy?: boolean; - tor?: boolean; - relay?: boolean; - active_vpn?: boolean; - active_tor?: boolean; - cloud_provider?: boolean; - fraud_score?: number; - }; - const isVpnOrProxy = Boolean(data.vpn || data.proxy || data.tor || data.relay || data.active_vpn || data.active_tor); - const isCloudProvider = Boolean(data.cloud_provider); - const riskScore = typeof data.fraud_score === 'number' ? data.fraud_score : undefined; - return { ip, isVpnOrProxy, isCloudProvider, riskScore, provider: 'ipqs' }; - } catch { - return { ip, isVpnOrProxy: false, isCloudProvider: false, provider: 'ipqs' }; - } - } - - return { ip, isVpnOrProxy: false, isCloudProvider: false, provider: 'none' }; -} - - diff --git a/web/lib/kv.ts b/web/lib/kv.ts deleted file mode 100644 index 53bf021..0000000 --- a/web/lib/kv.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { kv } from "@vercel/kv"; -import { hashIdentifier } from "./hash"; - -const CODE_TTL_SECONDS = 5 * 60; // 5 minutes - -function codeKey(phoneE164: string) { - const h = hashIdentifier("phone", phoneE164); - return `sms:code:${h}`; -} - -function verifiedKey(phoneE164: string) { - const h = hashIdentifier("phone", phoneE164); - return `sms:verified:${h}`; -} - -function mintedKey(address: string) { - const lower = address.toLowerCase(); - const h = hashIdentifier("addr", lower); - return `mint:address:${h}`; -} - -function phoneMintedKey(phoneE164: string) { - const h = hashIdentifier("phone", phoneE164); - return `mint:phone:${h}`; -} - -export async function saveSmsCode(phoneE164: string, code: string) { - await kv.set(codeKey(phoneE164), code, { ex: CODE_TTL_SECONDS }); -} - -export async function readSmsCode(phoneE164: string) { - // Try hashed key; if missing, fall back to legacy plaintext key for backwards compatibility - const primary = await kv.get(codeKey(phoneE164)); - if (primary !== null && primary !== undefined) return primary; - const legacyKey = `sms:code:${phoneE164}`; - return kv.get(legacyKey); -} - -export async function clearSmsCode(phoneE164: string) { - await kv.del(codeKey(phoneE164)); - // Also remove legacy key if present - await kv.del(`sms:code:${phoneE164}`); -} - -export async function markPhoneVerified(phoneE164: string) { - await kv.set(verifiedKey(phoneE164), "1", { ex: CODE_TTL_SECONDS }); -} - -export async function isPhoneVerified(phoneE164: string) { - let v = await kv.get(verifiedKey(phoneE164)); - if (v === null || v === undefined) { - v = await kv.get(`sms:verified:${phoneE164}`); - } - // Normalize potential numeric deserialization from KV - return String(v) === "1"; -} - -export async function markMinted(address: string, phoneE164: string) { - // Persist indefinitely to prevent reuse; can add TTL policy later if needed - await kv.set(mintedKey(address), phoneE164); - await kv.set(phoneMintedKey(phoneE164), address.toLowerCase()); - // Optionally, we could migrate legacy keys here, but we avoid writing plaintext keys. -} - -export async function hasMinted(address: string) { - let v = await kv.get(mintedKey(address)); - if (!(typeof v === "string" && v.length > 0)) { - v = await kv.get(`mint:address:${address.toLowerCase()}`); - } - return typeof v === "string" && v.length > 0; -} - -export async function hasPhoneMinted(phoneE164: string) { - let v = await kv.get(phoneMintedKey(phoneE164)); - if (!(typeof v === "string" && v.length > 0)) { - v = await kv.get(`mint:phone:${phoneE164}`); - } - return typeof v === "string" && v.length > 0; -} - -// --- Associations: phone/address -> hashed IPs --- -function phoneIpsKey(phoneE164: string) { - const h = hashIdentifier("phone", phoneE164); - return `assoc:phone:ips:${h}`; -} - -function addressIpsKey(address: string) { - const lower = address.toLowerCase(); - const h = hashIdentifier("addr", lower); - return `assoc:addr:ips:${h}`; -} - -/** - * Record the hashed IP used with a given phone number. - * Stores only the hashed IP to avoid retaining plaintext IPs. - */ -export async function addIpAssociationForPhone(phoneE164: string, ip: string) { - const ipHash = hashIdentifier("ip", ip); - try { - await kv.sadd(phoneIpsKey(phoneE164), ipHash); - } catch {} -} - -/** - * Record the hashed IP used with a given wallet address. - * Stores only the hashed IP to avoid retaining plaintext IPs. - */ -export async function addIpAssociationForAddress(address: string, ip: string) { - const ipHash = hashIdentifier("ip", ip); - try { - await kv.sadd(addressIpsKey(address), ipHash); - } catch {} -} - -/** Get all hashed IPs associated with a given phone number. */ -export async function getHashedIpsForPhone(phoneE164: string): Promise { - try { - const ips = await kv.smembers(phoneIpsKey(phoneE164)); - return Array.isArray(ips) - ? ips.filter((v): v is string => typeof v === "string" && v.length > 0) - : []; - } catch { - return []; - } -} - -/** Get all hashed IPs associated with a given wallet address. */ -export async function getHashedIpsForAddress(address: string): Promise { - try { - const ips = await kv.smembers(addressIpsKey(address)); - return Array.isArray(ips) - ? ips.filter((v): v is string => typeof v === "string" && v.length > 0) - : []; - } catch { - return []; - } -} diff --git a/web/lib/phone-intel.ts b/web/lib/phone-intel.ts deleted file mode 100644 index 3ac279c..0000000 --- a/web/lib/phone-intel.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { z } from 'zod'; - -export type PhoneCheck = { - phoneE164: string; - isDisposable: boolean; - isVoip: boolean; - isHighRisk: boolean; - carrierType?: string; - provider?: 'abstractapi' | 'none'; -}; - -const AbstractPhoneResp = z.object({ - valid: z.boolean().optional(), - number: z.string().optional(), - disposable: z.boolean().optional(), - type: z.string().optional(), // e.g., 'mobile', 'voip' - risk: z.number().optional(), -}); - -export async function analyzePhone(phoneE164: string): Promise { - const key = process.env.ABSTRACTAPI_PHONE_KEY; - if (key) { - try { - const url = `https://phonevalidation.abstractapi.com/v1/?api_key=${encodeURIComponent(key)}&phone=${encodeURIComponent(phoneE164)}`; - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), 3000); - const res = await fetch(url, { signal: controller.signal }); - clearTimeout(timer); - const data = await res.json(); - const parsed = AbstractPhoneResp.safeParse(data); - if (parsed.success) { - const t = (parsed.data.type || '').toLowerCase(); - const isVoip = t.includes('voip'); - const isDisposable = Boolean(parsed.data.disposable); - const risk = parsed.data.risk ?? 0; - const isHighRisk = isVoip || isDisposable || risk >= 70; - return { - phoneE164, - isDisposable, - isVoip, - isHighRisk, - carrierType: parsed.data.type, - provider: 'abstractapi', - }; - } - } catch { - // fall through to default - } - } - return { phoneE164, isDisposable: false, isVoip: false, isHighRisk: false, provider: 'none' }; -} - - diff --git a/web/lib/policy.ts b/web/lib/policy.ts deleted file mode 100644 index 2b7c387..0000000 --- a/web/lib/policy.ts +++ /dev/null @@ -1,24 +0,0 @@ -export type Policy = { - // Country codes (ISO alpha-2) that are blocked at the edge middleware - BLOCKED_COUNTRIES: string[]; - - // Global rate limit window and allowance per IP - RATE_LIMIT_WINDOW_SECONDS: number; - RATE_LIMIT_MAX_REQUESTS: number; - - // Cooldowns - SMS_SEND_COOLDOWN_SECONDS: number; // per-IP and per-phone cooldown for requesting SMS - MINT_IP_COOLDOWN_SECONDS: number; // per-IP cooldown after a successful mint -}; - -// Non-secret anti-abuse policy (committed defaults). -// Adjust these values via code review; secrets remain in env variables. -export const policy: Policy = { - BLOCKED_COUNTRIES: [], - RATE_LIMIT_WINDOW_SECONDS: 60, - RATE_LIMIT_MAX_REQUESTS: 10, - SMS_SEND_COOLDOWN_SECONDS: 120, - MINT_IP_COOLDOWN_SECONDS: 7 * 24 * 60 * 60, // 7 days -}; - - diff --git a/web/lib/rate-limit.ts b/web/lib/rate-limit.ts deleted file mode 100644 index a862144..0000000 --- a/web/lib/rate-limit.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Ratelimit } from '@upstash/ratelimit'; -import { Redis } from '@upstash/redis'; -import { requireEnv } from './env'; -import { policy } from './policy'; -import { hashIdentifier } from './hash'; - -// Single Redis instance for the app using Vercel KV credentials -const redis = new Redis({ - url: requireEnv('KV_REST_API_URL'), - token: requireEnv('KV_REST_API_TOKEN'), -}); - -// Global IP rate limiter (used as default) -export const globalIpRatelimit = new Ratelimit({ - redis, - limiter: Ratelimit.slidingWindow(policy.RATE_LIMIT_MAX_REQUESTS, `${policy.RATE_LIMIT_WINDOW_SECONDS} s`), - analytics: true, - prefix: 'rl:ip', -}); - -export function createRatelimit(prefix: string, maxRequests: number, windowSeconds: number) { - return new Ratelimit({ - redis, - limiter: Ratelimit.slidingWindow(maxRequests, `${windowSeconds} s`), - analytics: true, - prefix, - }); -} - -// Helper to normalize IP key before rate limiting if callers forget to hash. -export function ratelimitKeyForIp(rawIp: string): string { - return hashIdentifier('ip', rawIp); -} - - diff --git a/web/lib/request-ip.ts b/web/lib/request-ip.ts deleted file mode 100644 index eb3043d..0000000 --- a/web/lib/request-ip.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { NextApiRequest } from 'next'; - -// Prefer Cloudflare/Vercel headers if present -export function getClientIp(req: NextApiRequest): string { - const cf = (req.headers['cf-connecting-ip'] as string) || ''; - if (cf) return cf; - const vercel = (req.headers['x-real-ip'] as string) || ''; - if (vercel) return vercel; - const xf = (req.headers['x-forwarded-for'] as string) || ''; - const first = xf.split(',')[0]?.trim(); - return first || req.socket.remoteAddress || 'unknown'; -} - - diff --git a/web/lib/session.ts b/web/lib/session.ts deleted file mode 100644 index a19abad..0000000 --- a/web/lib/session.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { randomBytes } from 'crypto'; - -// Generate a unique session ID for grouping related database entries -export function generateSessionId(): string { - return randomBytes(16).toString('hex'); -} - -// Session-aware key generation for better data management -export function sessionKey(sessionId: string, type: string, identifier: string): string { - const values = { sessionId, type, identifier } as const; - for (const [k, v] of Object.entries(values)) { - if (typeof v !== 'string') throw new TypeError(`sessionKey: ${k} must be a string`); - if (v.trim().length === 0) throw new TypeError(`sessionKey: ${k} must be non-empty`); - if (!/^[^\s:]+$/.test(v)) throw new TypeError(`sessionKey: ${k} contains invalid characters`); - } - return `session:${sessionId}:${type}:${identifier}`; -} - -// Extract session ID from a session key with DoS protection -export function extractSessionId(sessionKey: string): string | null { - // Validate input - if (typeof sessionKey !== 'string') return null; - if (sessionKey.length > 1024) return null; // Prevent extremely long inputs - - // Use simple string operations instead of regex - if (!sessionKey.startsWith('session:')) return null; - - const parts = sessionKey.split(':'); - if (parts.length < 3) return null; // Should have at least "session", sessionId, and type - - const sessionId = parts[1]; - - // Validate session ID format (alphanumeric, reasonable length) - if (!sessionId || sessionId.length === 0 || sessionId.length > 64) return null; - if (!/^[a-zA-Z0-9]+$/.test(sessionId)) return null; - - return sessionId; -} diff --git a/web/lib/sms.ts b/web/lib/sms.ts deleted file mode 100644 index 97c4886..0000000 --- a/web/lib/sms.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { env } from "./env"; - -const VERIFY_PROVIDER = "twilio-verify" as const; - -export type SmsProviderError = { - provider: typeof VERIFY_PROVIDER; - status?: number; - errorCode?: number | string; - errorMessage?: string; -}; - -type VerifyRequestOptions = { - timeoutMs?: number; - maxRetries?: number; - baseDelayMs?: number; - deviceIp?: string; -}; - -export type StartSmsVerificationResult = { - ok: boolean; - sid?: string; - status?: string; - providerError?: SmsProviderError; -}; - -export type SmsVerificationFailureReason = "invalid_code" | "expired_or_invalid" | "provider_error"; - -export type CheckSmsVerificationResult = { - ok: boolean; - sid?: string; - status?: string; - valid?: boolean; - reason?: SmsVerificationFailureReason; - providerError?: SmsProviderError; -}; - -type TwilioVerifyResponse = { - sid?: unknown; - status?: unknown; - valid?: unknown; -}; - -type VerifyPostResult = - | { ok: true; status: number; data: TwilioVerifyResponse } - | { ok: false; providerError: SmsProviderError }; - -const EXPIRED_OR_INVALID_ERROR_CODES = new Set([20404, 60202, 60203, 60212, 60213, 60224]); -const INVALID_CODE_ERROR_CODES = new Set([60200]); -const EXPIRED_OR_INVALID_STATUS = new Set([ - "canceled", - "expired", - "max_attempts_reached", - "denied", -]); - -function toUrlEncoded(params: Record): string { - return Object.entries(params) - .filter(([, value]) => typeof value === "string" && value.length > 0) - .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v as string)}`) - .join("&"); -} - -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -function asString(value: unknown): string | undefined { - return typeof value === "string" && value.length > 0 ? value : undefined; -} - -function asBoolean(value: unknown): boolean | undefined { - return typeof value === "boolean" ? value : undefined; -} - -function getNumericCode(value: number | string | undefined): number | undefined { - if (typeof value === "number" && Number.isFinite(value)) return value; - if (typeof value === "string" && value.length > 0) { - const parsed = Number(value); - return Number.isFinite(parsed) ? parsed : undefined; - } - return undefined; -} - -function normalizeProviderError( - status: number | undefined, - details: { errorCode?: number | string; errorMessage?: string } = {}, -): SmsProviderError { - return { - provider: VERIFY_PROVIDER, - status, - errorCode: details.errorCode, - errorMessage: details.errorMessage, - }; -} - -export function isProductionRuntime(): boolean { - return (process.env.VERCEL_ENV || "").toLowerCase() === "production"; -} - -export function isVerifyConfigured(): boolean { - return Boolean(env.TWILIO_ACCOUNT_SID && env.TWILIO_AUTH_TOKEN && env.TWILIO_VERIFY_SERVICE_SID); -} - -export function shouldUseKvFallback(smokeTokenHeader: string | undefined): boolean { - if (isProductionRuntime()) return false; - if (!env.SMOKE_TEST_TOKEN) return false; - const incoming = (smokeTokenHeader || "").trim(); - return incoming.length > 0 && incoming === env.SMOKE_TEST_TOKEN; -} - -async function parseProviderErrorDetails( - res: Response, -): Promise<{ errorCode?: number | string; errorMessage?: string }> { - try { - const contentType = res.headers.get("content-type") || ""; - if (contentType.includes("application/json")) { - const json = (await res.json()) as Record; - const rawCode = json.code; - const rawMessage = json.message; - const errorCode = - typeof rawCode === "number" || typeof rawCode === "string" - ? (rawCode as number | string) - : undefined; - const errorMessage = typeof rawMessage === "string" ? rawMessage : undefined; - return { errorCode, errorMessage }; - } - - const text = await res.text(); - return { - errorMessage: typeof text === "string" && text.length > 0 ? text.slice(0, 300) : undefined, - }; - } catch { - return {}; - } -} - -async function twilioVerifyPost( - resource: "Verifications" | "VerificationCheck", - bodyParams: Record, - options: VerifyRequestOptions = {}, -): Promise { - if (!isVerifyConfigured()) { - return { - ok: false, - providerError: normalizeProviderError(undefined, { - errorMessage: "Twilio Verify is not configured", - }), - }; - } - - const accountSid = env.TWILIO_ACCOUNT_SID as string; - const authToken = env.TWILIO_AUTH_TOKEN as string; - const verifyServiceSid = env.TWILIO_VERIFY_SERVICE_SID as string; - const url = `https://verify.twilio.com/v2/Services/${encodeURIComponent(verifyServiceSid)}/${resource}`; - const authorization = Buffer.from(`${accountSid}:${authToken}`).toString("base64"); - - const timeoutMs = - Number.isFinite(options.timeoutMs) && (options.timeoutMs as number) > 0 - ? (options.timeoutMs as number) - : 6000; - const maxRetries = - Number.isFinite(options.maxRetries) && (options.maxRetries as number) >= 0 - ? (options.maxRetries as number) - : 2; - const baseDelayMs = - Number.isFinite(options.baseDelayMs) && (options.baseDelayMs as number) >= 0 - ? (options.baseDelayMs as number) - : 300; - - let attempt = 0; - let lastProviderError: SmsProviderError | undefined; - - while (attempt <= maxRetries) { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); - try { - const res = await fetch(url, { - method: "POST", - headers: { - Authorization: `Basic ${authorization}`, - "Content-Type": "application/x-www-form-urlencoded", - }, - body: toUrlEncoded(bodyParams), - signal: controller.signal, - }); - - if (res.ok) { - let data: TwilioVerifyResponse = {}; - try { - const contentType = res.headers.get("content-type") || ""; - if (contentType.includes("application/json")) { - data = (await res.json()) as TwilioVerifyResponse; - } - } catch { - data = {}; - } - return { ok: true, status: res.status, data }; - } - - const details = await parseProviderErrorDetails(res); - const providerError = normalizeProviderError(res.status, details); - if (res.status >= 400 && res.status < 500) { - return { ok: false, providerError }; - } - - lastProviderError = providerError; - } catch { - lastProviderError = normalizeProviderError(undefined, { - errorMessage: "Twilio Verify request failed", - }); - } finally { - clearTimeout(timer); - } - - attempt += 1; - if (attempt > maxRetries) break; - - const backoff = baseDelayMs * 2 ** (attempt - 1); - const jitter = Math.floor(Math.random() * (baseDelayMs / 2)); - await sleep(backoff + jitter); - } - - return { - ok: false, - providerError: - lastProviderError || - normalizeProviderError(undefined, { - errorMessage: "Twilio Verify request failed after retries", - }), - }; -} - -function classifyProviderFailure(providerError: SmsProviderError): SmsVerificationFailureReason { - const code = getNumericCode(providerError.errorCode); - const message = (providerError.errorMessage || "").toLowerCase(); - - if (providerError.status === 404) return "expired_or_invalid"; - if (code !== undefined && EXPIRED_OR_INVALID_ERROR_CODES.has(code)) return "expired_or_invalid"; - if (code !== undefined && INVALID_CODE_ERROR_CODES.has(code)) return "invalid_code"; - - if ( - message.includes("expired") || - message.includes("not found") || - message.includes("max check attempts") || - message.includes("maximum check attempts") || - message.includes("too many check") || - message.includes("already canceled") || - message.includes("already cancelled") - ) { - return "expired_or_invalid"; - } - - if ( - message.includes("invalid code") || - message.includes("incorrect") || - message.includes("does not match") || - message.includes("mismatch") || - message.includes("wrong code") - ) { - return "invalid_code"; - } - - return "provider_error"; -} - -export async function startSmsVerification( - phoneE164: string, - options: VerifyRequestOptions = {}, -): Promise { - const result = await twilioVerifyPost( - "Verifications", - { - To: phoneE164, - Channel: "sms", - DeviceIp: options.deviceIp, - }, - options, - ); - - if (!result.ok) { - return { - ok: false, - providerError: result.providerError, - }; - } - - return { - ok: true, - sid: asString(result.data.sid), - status: asString(result.data.status), - }; -} - -export async function checkSmsVerification( - phoneE164: string, - code: string, - options: VerifyRequestOptions = {}, -): Promise { - const result = await twilioVerifyPost( - "VerificationCheck", - { - To: phoneE164, - Code: code, - DeviceIp: options.deviceIp, - }, - options, - ); - - if (!result.ok) { - return { - ok: false, - reason: classifyProviderFailure(result.providerError), - providerError: result.providerError, - }; - } - - const status = asString(result.data.status); - const valid = asBoolean(result.data.valid); - const normalizedStatus = (status || "").toLowerCase(); - const isApproved = normalizedStatus === "approved" || valid === true; - if (isApproved) { - return { - ok: true, - sid: asString(result.data.sid), - status, - valid, - }; - } - - const reason = EXPIRED_OR_INVALID_STATUS.has(normalizedStatus) - ? "expired_or_invalid" - : "invalid_code"; - return { - ok: false, - sid: asString(result.data.sid), - status, - valid, - reason, - }; -} diff --git a/web/middleware.ts b/web/middleware.ts deleted file mode 100644 index 1d9a203..0000000 --- a/web/middleware.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { policy } from './lib/policy'; -import { verifyAdminTokenEdge } from './lib/admin-auth-edge'; - -// Helper functions to reduce duplication -function sendUnauthorizedApi(): NextResponse { - return new NextResponse(JSON.stringify({ error: 'Unauthorized' }), { - status: 401, - headers: { 'content-type': 'application/json' }, - }); -} - -function redirectToHome(req: NextRequest): NextResponse { - const url = req.nextUrl.clone(); - url.pathname = '/'; - return NextResponse.redirect(url); -} - -export async function middleware(req: NextRequest) { - // Block by country if configured (prefer Vercel header at edge) - const headerCountry = req.headers.get('x-vercel-ip-country') || ''; - const country = headerCountry.toUpperCase(); - if (country && policy.BLOCKED_COUNTRIES.includes(country)) { - return new NextResponse(JSON.stringify({ error: 'Access blocked in your region' }), { - status: 451, - headers: { 'content-type': 'application/json' }, - }); - } - - // Protect admin routes both API and page access at the edge where possible - const { pathname } = req.nextUrl; - const isAdminApi = pathname.startsWith('/api/admin'); - const isAdminPage = pathname === '/admin' || pathname.startsWith('/admin/'); - if (isAdminApi || isAdminPage) { - const token = req.cookies.get('admin_session')?.value; - const secret = process.env.ADMIN_SESSION_SECRET; // Use direct access since requireEnv is not edge-compatible - try { - // Fail fast on server misconfiguration (missing secret) - if (!secret) { - console.error('[middleware] ADMIN_SESSION_SECRET is not set. Blocking admin route access.'); - return isAdminApi - ? new NextResponse(JSON.stringify({ error: 'Server error' }), { - status: 500, - headers: { 'content-type': 'application/json' }, - }) - : redirectToHome(req); - } - - if (!token) { - return isAdminApi ? sendUnauthorizedApi() : redirectToHome(req); - } - // Edge-safe verification - const ok = await verifyAdminTokenEdge(token, secret); - if (!ok) { - return isAdminApi ? sendUnauthorizedApi() : redirectToHome(req); - } - } catch (err) { - const path = req.nextUrl.pathname; - console.warn(`[middleware] Admin auth failure on ${path} (isAdminApi=${isAdminApi})`, err); - return isAdminApi ? sendUnauthorizedApi() : redirectToHome(req); - } - } - - return NextResponse.next(); -} - -export const config = { - matcher: ['/api/:path*', '/admin', '/admin/:path*'], -}; - - diff --git a/web/next.config.ts b/web/next.config.ts deleted file mode 100644 index b1ec640..0000000 --- a/web/next.config.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ - reactStrictMode: true, - images: { - // Enable static image optimization for better caching - unoptimized: false, - // Set cache time for optimized images (1 year) - minimumCacheTTL: 31536000, - }, - // Add cache headers for static assets - async headers() { - return [ - { - // Cache all static image assets under `public/`. Use a long max-age without `immutable` - // since filenames are not fingerprinted with hashes. - source: '/:all*\\.(jpg|jpeg|png|svg|ico|webp)', - headers: [ - { - key: 'Cache-Control', - value: 'public, max-age=31536000', - }, - ], - }, - ]; - }, -}; - -export default nextConfig; diff --git a/web/package.json b/web/package.json deleted file mode 100644 index 3571be4..0000000 --- a/web/package.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "web", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "node scripts/start-dev.mjs", - "build": "next build", - "start": "next start", - "lint": "eslint .", - "smoke:prod": "bash scripts/smoke-test.sh prod", - "smoke:preview": "bash scripts/smoke-test.sh preview", - "doctor": "react-doctor . -y", - "doctor:score": "react-doctor . --score -y", - "doctor:verbose": "react-doctor . --verbose -y" - }, - "dependencies": { - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.7", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-toast": "^1.2.15", - "@types/canvas-confetti": "^1.9.0", - "@upstash/ratelimit": "2.0.6", - "@upstash/redis": "1.35.3", - "@vercel/analytics": "^1.5.0", - "@vercel/kv": "3.0.0", - "canvas-confetti": "^1.9.3", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.1", - "ethers": "6.13.4", - "input-otp": "^1.4.2", - "lucide-react": "^0.543.0", - "next": "16.2.3", - "next-themes": "^0.4.6", - "react": "19.2.3", - "react-dom": "19.2.3", - "react-phone-number-input": "^3.4.12", - "tailwind-merge": "^3.3.1", - "zod": "4.0.15" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@types/minimatch": "^6.0.0", - "@types/node": "^20", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "autoprefixer": "^10.4.21", - "eslint": "^9.39.2", - "eslint-config-next": "16.2.3", - "portless": "0.11.1", - "postcss": "^8.5.6", - "react-doctor": "0.2.6", - "tailwindcss": "^3.4.19", - "typescript": "^6.0.2" - }, - "packageManager": "yarn@4.13.0", - "resolutions": { - "brace-expansion@npm:^1.1.7": "1.1.13", - "flatted@npm:^3.2.9": "3.4.2", - "js-yaml": ">=4.1.1", - "minimatch@npm:^3.1.2": "3.1.4", - "minimatch@npm:^9.0.4": "9.0.7", - "minimatch@npm:*": "10.2.4", - "picomatch@npm:^2.0.4": "2.3.2", - "picomatch@npm:^2.2.1": "2.3.2", - "picomatch@npm:^2.3.1": "2.3.2", - "picomatch@npm:^4.0.1": "4.0.4", - "picomatch@npm:^4.0.2": "4.0.4", - "picomatch@npm:^4.0.3": "4.0.4" - } -} diff --git a/web/postcss.config.mjs b/web/postcss.config.mjs deleted file mode 100644 index a982c64..0000000 --- a/web/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -const config = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; - -export default config; diff --git a/web/public/android-chrome-192x192.png b/web/public/android-chrome-192x192.png deleted file mode 100644 index 028c25a..0000000 Binary files a/web/public/android-chrome-192x192.png and /dev/null differ diff --git a/web/public/android-chrome-512x512.png b/web/public/android-chrome-512x512.png deleted file mode 100644 index 63f6189..0000000 Binary files a/web/public/android-chrome-512x512.png and /dev/null differ diff --git a/web/public/apple-touch-icon.png b/web/public/apple-touch-icon.png deleted file mode 100644 index d536291..0000000 Binary files a/web/public/apple-touch-icon.png and /dev/null differ diff --git a/web/public/favicon-16x16.png b/web/public/favicon-16x16.png deleted file mode 100644 index 29aa400..0000000 Binary files a/web/public/favicon-16x16.png and /dev/null differ diff --git a/web/public/favicon-32x32.png b/web/public/favicon-32x32.png deleted file mode 100644 index 57b202d..0000000 Binary files a/web/public/favicon-32x32.png and /dev/null differ diff --git a/web/public/favicon.ico b/web/public/favicon.ico deleted file mode 100644 index f68fa67..0000000 Binary files a/web/public/favicon.ico and /dev/null differ diff --git a/web/public/favicon.png b/web/public/favicon.png deleted file mode 100644 index 2e7ee8b..0000000 Binary files a/web/public/favicon.png and /dev/null differ diff --git a/web/public/llms-full.txt b/web/public/llms-full.txt deleted file mode 100644 index 8364f36..0000000 --- a/web/public/llms-full.txt +++ /dev/null @@ -1,3251 +0,0 @@ -# MintPass Full LLM Context - -> MintPass is an NFT-based authentication and anti-sybil system for Bitsocial communities and other decentralized apps. - -This file is generated by `scripts/generate-llms-files.mjs`. Do not hand-edit it; update the source docs or generator config, then run `yarn llms:generate`. - -Use this as compiled context only. Source files, tests, manifests, and live/runtime evidence remain authoritative. - -## Index - -- [README.md](#readmemd) -- [AGENTS.md](#agentsmd) -- [web/README.md](#webreadmemd) -- [challenge/README.md](#challengereadmemd) -- [contracts/README.md](#contractsreadmemd) -- [docs/pkc-info.md](#docspkc-infomd) -- [docs/challenges/README.md](#docschallengesreadmemd) -- [docs/challenges/question.md](#docschallengesquestionmd) -- [docs/challenges/evm-contract-call.md](#docschallengesevm-contract-callmd) -- [docs/challenges/publication-match.md](#docschallengespublication-matchmd) -- [challenge/TESTING.md](#challengetestingmd) -- [challenge/AUTOMATED_TESTING.md](#challengeautomatedtestingmd) -- [contracts/DEPLOYMENT.md](#contractsdeploymentmd) -- [contracts/security-checklist.md](#contractssecurity-checklistmd) -- [docs/challenges/blacklist.md](#docschallengesblacklistmd) -- [docs/challenges/captcha-canvas.md](#docschallengescaptcha-canvasmd) -- [docs/challenges/fail.md](#docschallengesfailmd) -- [docs/challenges/text-math.md](#docschallengestext-mathmd) - ---- - -## README.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/README.md - -```markdown -[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) - -# MintPass - NFT Authentication Middleware for Bitsocial - -MintPass Logo - -MintPass is an NFT-based authentication system that provides verified identity proofs for decentralized communities. It began as an anti‑spam challenge for Bitsocial communities, and it works equally well for other protocols and social applications. Users mint a non‑transferable verification NFT (e.g., after SMS OTP) that communities can check to reduce sybil attacks, such as fake upvotes/downvotes, fake conversations, and users evading bans. - -
- -## How people use MintPass - -1) Visit `mintpass.org/request`, enter a phone number, and complete SMS OTP. -2) MintPass mints an NFT (on testnet in this reference deployment) to your wallet or records an equivalent “verified” state when on‑chain minting is disabled. -3) Communities (e.g., Bitsocial communities) check ownership of the NFT to treat you as authenticated for anti‑spam. - -The request form looks like this: - -

- MintPass request form screenshot -

- -## What is Bitsocial? - -Bitsocial is p2p and decentralized social media protocol built completely with IPFS/IPNS/pubsub. It doesn't use any central server, central database, public HTTP endpoint or DNS, it is pure peer to peer (except for the web client that can't join a P2P swarm directly, web clients use interchangeable HTTP providers). It allows community owners to retain full ownership over their community. Whitepaper [here](https://github.com/pkc/whitepaper/discussions/2). - -MintPass integrates as a challenge so Bitsocial communities can distinguish real users and limit abuse without central servers. Because the artifact is an NFT, other decentralized apps can use the same credential to authenticate users. - -## Project Structure - -``` -mintpass/ -├── contracts/ # MintPassV1 smart contract and tooling -├── challenge/ # Bitsocial challenge implementation (“mintpass”) -├── web/ # Next.js website + API (mintpass.org) -├── docs/ # Documentation and specifications -├── tests/ # Cross‑component integration tests -└── scripts/ # Deployment and utilities -``` - -### Subprojects - -- `contracts/`: Solidity contracts (MintPassV1). Versioned, role‑based minting, token types per NFT (type 0 = SMS). See `contracts/README.md`. -- `challenge/`: The Bitsocial challenge that checks for a MintPass NFT and applies additional rules (e.g., transfer cooldowns) to resist sybils. -- `web/`: The user‑facing site and serverless backend. Sends SMS codes, verifies OTP, and mints or records successful verification. See `web/README.md`. - -## Privacy and anti‑sybil design (high level) - -- Short‑lived operational data (OTP codes, verification markers, rate‑limit state) stored in Redis with TTLs. -- Persistent “mint association” between wallet and phone to prevent duplicate mints. -- Optional IP reputation (VPN/proxy) and phone‑risk checks, optional geoblocking, and per‑IP cooldowns. -- Secrets live only in environment variables; logs avoid PII and never include OTPs or private keys. - -## Getting started - -1. Run `nvm install && nvm use` -2. Run `corepack enable` once per machine so `yarn` resolves to the pinned Yarn 4 release -3. Use plain `yarn install`, `yarn build`, and `yarn test` - -- Contracts: `cd contracts && yarn install && yarn test` -- Challenge: `cd challenge && yarn install && yarn test` -- Web: `cd web && yarn install && yarn dev` then open `https://mintpass.localhost/request` - -## Using MintPass in your community - -Community owners add the MintPass challenge to their community settings. When enabled, every publication (post, reply, vote) requires the author to hold a valid MintPass NFT. The challenge is published as [`@bitsocial/mintpass-challenge`](https://www.npmjs.com/package/@bitsocial/mintpass-challenge) on npm. - -### With pkc-js over RPC - -If your RPC server is already running, first install the challenge on the server: - -```bash -bitsocial challenge install @bitsocial/mintpass-challenge -``` - -Then from your RPC client, connect and set the challenge on your community by name — no npm install or challenge registration needed on the client side: - -```ts -import PKC from "@pkcprotocol/pkc-js"; - -const pkc = await PKC({ - pkcRpcClientsOptions: ["ws://localhost:9138"] -}); - -const community = await pkc.createCommunity({ address: "your-community-address.bso" }); - -await community.edit({ - settings: { - challenges: [ - { - name: "@bitsocial/mintpass-challenge", - options: { - chainTicker: "base", - contractAddress: "0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9", - requiredTokenType: "0", - transferCooldownSeconds: "604800" - } - } - ] - } -}); -``` - -### With pkc-js (TypeScript) - -Install the challenge package: - -```bash -npm install @bitsocial/mintpass-challenge -``` - -Register the challenge and configure your community: - -```typescript -import PKC from '@pkcprotocol/pkc-js' -import mintpassChallenge from '@bitsocial/mintpass-challenge' - -// Register the challenge so it can be referenced by name -PKC.challenges['@bitsocial/mintpass-challenge'] = mintpassChallenge - -const pkc = await PKC({ /* your pkc options */ }) -const community = await pkc.createCommunity({ address: 'your-community.bso' }) - -await community.edit({ - settings: { - challenges: [{ - name: '@bitsocial/mintpass-challenge', - options: { - chainTicker: 'base', - contractAddress: '0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9', - requiredTokenType: '0', - bindToFirstAuthor: 'true', - transferCooldownSeconds: '604800', - } - }] - } -}) -``` - -#### Challenge options - -All option values must be strings (pkc-js challenge convention). - -| Option | Default | Description | -|--------|---------|-------------| -| `chainTicker` | `"base"` | Chain where MintPass is deployed | -| `contractAddress` | Base Sepolia default | Contract address (auto-detected for supported chains) | -| `requiredTokenType` | `"0"` | Token type (0 = SMS, 1 = Email) | -| `bindToFirstAuthor` | `"true"` | Bind NFT to first author per community | -| `transferCooldownSeconds` | `"604800"` | Cooldown after NFT transfer (1 week) | -| `error` | Default message | Custom error (`{authorAddress}` placeholder supported) | -| `rpcUrl` | Chain default | Optional custom RPC URL | - -### With bitsocial-cli - -Install the challenge package: - -```bash -bitsocial challenge install @bitsocial/mintpass-challenge -``` - -Edit your community to use the challenge: - -```bash -bitsocial community edit your-community.bso \ - '--settings.challenges[0].name' @bitsocial/mintpass-challenge \ - '--settings.challenges[0].options.chainTicker' base \ - '--settings.challenges[0].options.contractAddress' '0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9' \ - '--settings.challenges[0].options.requiredTokenType' '0' \ - '--settings.challenges[0].options.bindToFirstAuthor' 'true' \ - '--settings.challenges[0].options.transferCooldownSeconds' '604800' -``` - -See the [bitsocial-cli documentation](https://github.com/bitsocial/bitsocial-cli) for full CLI reference. - -## Where MintPass is useful - -While designed for Bitsocial, any decentralized or serverless social app can use MintPass NFTs as a lightweight proof‑of‑personhood. Apps only need to check ownership of a token type (e.g., type 0 for SMS) to gate actions or increase trust in votes and reports. - -## Roadmap and considerations - -We plan to support multiple authentication methods alongside SMS OTP to fit different threat models and UX constraints: -- Add a “pay‑to‑mint” option with a small fee that is high enough to deter bulk purchases but low enough for regular users. -- Add additional human‑verification signals (e.g., email, government‑backed KYC providers, or proofs such as biometrics/world‑ID systems) when they can be integrated without compromising decentralization goals. -- Expand admin tooling, heuristics, and optional device signals to further reduce abuse. - -These items are exploratory; concrete work will land incrementally and stay configurable so communities can choose what they trust. - -## Technology Stack - -- **Smart Contracts**: Solidity, Hardhat/Foundry -- **Website**: Next.js, React, Ethereum (ethers) -- **Challenges**: TypeScript, pkc-js integration -- **Deployment**: Base network (L2) - -## License - -MIT License — see [LICENSE](LICENSE). - -Open source and commercial‑friendly. A hosted version is available at [mintpass.org](https://mintpass.org). -``` - ---- - -## AGENTS.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/AGENTS.md - -```markdown -# AGENTS.md - -## Project Overview - -MintPass is a multi-part authentication system for Bitsocial and other decentralized apps: -- `web/`: Next.js site + API for SMS verification and mint flow -- `contracts/`: Solidity contracts for MintPass NFTs -- `challenge/`: PKC challenge module that verifies NFT ownership - -This is not a single app repo. Treat it as a coordinated multi-project codebase. - -## Agent Operating Principles - -- Before editing, state important assumptions when the task is ambiguous. Ask instead of silently choosing between materially different interpretations. -- Prefer the smallest implementation that solves the requested problem. Do not add speculative abstractions, configurability, or features. -- Keep diffs surgical. Do not refactor, reformat, rename, or "improve" adjacent code unless it is necessary for the task. -- Clean up only artifacts created by the current change, such as newly unused imports or dead helper code. -- For non-trivial work, define success criteria and verify them with the narrowest reliable checks before marking the task complete. - -## LLM Knowledge Base Policy - -Use compiled context for orientation, not as source of truth. - -Source of truth: - -- Code, tests, package manifests, docs, and runtime/live evidence when relevant. - -Compiled context: - -- `AGENTS.md`, directory-specific `AGENTS.md` files, `CLAUDE.md`, and repo-managed `.codex/`, `.cursor/`, and `.claude/` workflow files. -- `docs/**`, tracked task notes, and tracked `llms.txt` / `llms-full.txt` files when present. - -Agents may use compiled context to navigate quickly, but must verify against source files before making behavioral claims or edits. External code graph, RAG, MCP, or wiki tools are optional local accelerators unless the developer explicitly asks to make one part of the committed workflow. - -## Repository Shape (Critical) - -```text -mintpass/ -├── web/ # Next.js 15 app (pages router) + API routes -├── contracts/ # Hardhat + Solidity contracts and deployment scripts -├── challenge/ # TypeScript challenge package for pkc-js -├── dist/challenge/ # Published challenge artifact copied from challenge/dist -└── docs/ # Specs and challenge docs -``` - -Important: -- Root workspaces include `contracts` and `challenge` only. -- `web` has its own `package.json` and `yarn.lock`; install/run it separately. -- `challenge/dist` and root `dist/challenge` are generated artifacts. Do not hand-edit them. - -## Stack - -- Node.js `22.12.0` via `.nvmrc` -- Yarn 4 via Corepack (run `corepack enable` once per machine, then use plain `yarn`) -- TypeScript (strict mode in `web`, `contracts`, `challenge`) -- Web: Next.js 15, React 19, Tailwind, Radix/shadcn-style UI, Zod, Upstash/Vercel KV -- Contracts: Solidity `0.8.24`, Hardhat, OpenZeppelin, ethers v6 -- Challenge: TypeScript ESM + `viem` + `keyv`, integrated with pkc-js challenge interface - -## Commands - -### Root - -```bash -yarn install # install root + contracts + challenge deps -yarn build # build contracts + challenge, then publish challenge artifact to dist/challenge -yarn test # contracts test suite -yarn test:contracts -yarn test:challenge -yarn test:challenge:local -yarn clean -``` - -### Web (`web/`) - -```bash -cd web -yarn install -yarn dev # local dev server -yarn build # production build -yarn start # run built app -yarn lint # Next.js lint rules -yarn smoke:preview # preview smoke flow (requires env) -yarn smoke:prod # production smoke flow (requires env) -``` - -### Contracts (`contracts/`) - -```bash -cd contracts -yarn install -yarn compile -yarn test -yarn coverage -yarn deploy:testnet -yarn deploy:mainnet -``` - -### Challenge (`challenge/`) - -```bash -cd challenge -yarn install -yarn build -yarn test # automated local integration (hardhat + kubo) -yarn test:manual -yarn clean -``` - -## React Doctor (Advisory) - -React Doctor is advisory quality tooling for React architecture/perf/correctness checks. **Scope: `web/` only** (the Next.js app). - -**Standard commands** (run from `web/`): -- `cd web && yarn doctor`, `cd web && yarn doctor:score`, `cd web && yarn doctor:verbose` - -**Trigger rules:** -- Run after touching React UI logic (`components`, `hooks`, route/page/view files, state/store code used by UI). -- Run before opening PRs that include React behavior changes. - -**Interpretation:** -- Treat diagnostics as actionable recommendations. -- Prioritize `error` diagnostics first, then `warning`. -- Score is informative only; no merge blocking based on score yet. - -## Scope-Driven Validation (Required) - -Run checks based on what you changed: - -- If you change `web/**`: - - `cd web && yarn doctor:score` - - `cd web && yarn lint` - - `cd web && yarn build` - - If API flow changed and envs are available: run smoke test (`yarn smoke:preview` or `yarn smoke:prod`) - -- If you change `contracts/**`: - - `cd contracts && yarn test` - - `cd contracts && yarn compile` - -- If you change `challenge/**`: - - `cd challenge && yarn test` - - `cd challenge && yarn build` - - If challenge runtime/API changed, sync artifacts with root: - - `yarn build:challenge` - - `yarn publish:challenge` - -- If you change public docs or AI context (`README.md`, `AGENTS.md`, `docs/**`, `web/README.md`, `challenge/README.md`, `contracts/README.md`, or `scripts/generate-llms-files.mjs`): - - `yarn llms:generate` - - Inspect and commit any resulting changes to `llms*.txt` and `web/public/llms*.txt` - -- If you change multiple areas: - - Run each affected package checks - - Then run root `yarn build` and root `yarn test` - -## Code Style and Architecture - -- Keep TypeScript strict-safe; avoid `any` unless unavoidable and documented. -- Prefer small focused functions and modules over large route handlers. -- Preserve existing naming and folder conventions in each package. -- Add brief comments only where logic is non-obvious. - -### Web-Specific Rules - -- API handlers live in `web/src/pages/api/**`; shared backend logic belongs in `web/lib/**`. -- Validate request bodies with `zod` before business logic. -- `web/lib/env.ts` is server-only; never import it into client-rendered code. -- Do not leak secrets, OTPs, raw phone numbers, or private keys in logs/responses. -- Keep anti-abuse behavior intact unless explicitly requested: - - rate limits - - cooldowns - - IP/phone risk checks - - hashed identifier storage via `hashIdentifier` - -### Contracts-Specific Rules - -- Preserve role boundaries (`ADMIN_ROLE`, `MINTER_ROLE`) and access controls. -- Keep deterministic deployment and network config behavior stable unless requested. -- Treat contract interface changes as breaking unless migration/versioning is planned. -- Prefer explicit tests for any change affecting minting, roles, token types, or metadata. - -### Challenge-Specific Rules - -- Source of truth is `challenge/src/**`, primarily `challenge/src/mintpass.ts`. -- Maintain compatibility with pkc-js challenge contract (factory + option handling). -- Be careful with chain/wallet fallback behavior and transfer cooldown logic. -- Rebuild generated outputs when source/public types change. - -## Security and Privacy Boundaries (Critical) - -- Never commit `.env` files, secrets, API keys, private keys, bypass tokens, or OTPs. -- Never log raw PII (phone/IP) when hashed storage/logging is available. -- Keep Preview-only shortcuts gated to Preview (`VERCEL_ENV=preview`) only. -- Do not weaken cooldown/rate-limit defaults without explicit user approval. -- For web changes touching auth/mint flow, favor fail-closed behavior. - -## Generated Artifacts - -- Do not manually edit: - - `challenge/dist/**` - - `dist/challenge/**` -- When challenge source changes, regenerate artifacts and commit them when relevant to the change. - -## Recommended Skills - -Use these when applicable (they are available in this repo setup): - -- `context7`: fetch current library/framework docs before API-sensitive changes -- `vercel-react-best-practices`: performance and React/Next.js patterns for `web/` -- `you-might-not-need-an-effect`: audit/refactor unnecessary `useEffect` usage -- `playwright-cli`: browser automation and UI validation -- `commit-format`: standardize Conventional Commit message output -- `issue-format`: standardize GitHub issue title/description output -- `impeccable`: design, redesign, critique, audit, polish, layout, typography, color, motion, or visual hierarchy work for `web/` (one `/impeccable` entry point with 23 design subcommands) - -## AI Tooling Rules - -- Treat `.codex/`, `.cursor/`, and `.claude/` as repo-managed contributor tooling, not private scratch space. -- Keep equivalent workflow files aligned across all toolchains when their directories contain the same skill, hook, or agent. -- When changing shared agent behavior, update the relevant files in `.codex/skills/`, `.cursor/skills/`, `.claude/skills/`, `.codex/agents/`, `.cursor/agents/`, `.claude/agents/`, `.codex/hooks/`, `.cursor/hooks/`, `.claude/hooks/`, and their `hooks.json` or `config.toml` entry points as needed. -- If `AGENTS.md` references a skill, agent, or hook, prefer a tracked file under `.codex/`, `.cursor/`, or `.claude/` rather than an untracked local-only instruction. - -## AI Agent Hooks - -This repo already includes hooks: -- `.codex/hooks.json` -- `.cursor/hooks.json` - -Configured scripts: -- `afterFileEdit`: `.cursor/hooks/format.sh` -- `afterFileEdit`: `.cursor/hooks/yarn-install.sh` -- `stop`: `.cursor/hooks/verify.sh` - -Use these as defaults when your agent supports lifecycle hooks. - -## Git Worktrees - -- Always give a new worktree a descriptive name that reflects the task (e.g. `fix-mint-cooldown`, not `wt1`, `tmp`, `feature`, or a numbered slug), so it can be identified at a glance in a long list of worktrees. - -## GitHub Workflow - -### Commits - -When proposing or implementing code changes, suggest a commit message: -- Title in Conventional Commits format, wrapped in backticks -- Use `perf:` for performance optimizations -- Optional short description: 1-3 technical sentences about the solution - -Example: - -> **Commit title:** `fix: enforce mint cooldown check before writing mint state` -> -> Moved the cooldown guard earlier in `web/src/pages/api/mint.ts` so failed requests do not write mint markers. - -### Issues - -When proposing or implementing code changes, suggest a trackable issue: -- Short title wrapped in backticks -- Description focuses on the problem (as-if not fixed yet), 2-3 concise sentences - -## Bug Investigation (Mandatory First Step) - -If the user reports a bug in a specific file/line/area, check git history first before editing. - -1. Inspect recent commit titles scoped to the file: - ```bash - git log --oneline -10 -- path/to/file - ``` -2. If needed, inspect line ownership: - ```bash - git blame -L , path/to/file - ``` -3. Read only relevant commits in detail: - ```bash - git show -- path/to/file - ``` - -Then proceed with code changes. - -## Dependency Management - -- Use exact versions when adding/updating packages (`pkg@x.y.z`, no bare installs). -- Do not do broad dependency rewrites unless explicitly requested. -- Keep lockfiles in sync for the package you touched: - - root `yarn.lock` - - `web/yarn.lock` - -## Tooling Preferences - -- Use `gh` CLI for GitHub operations (issues, PRs, actions, project ops). -- Prefer CLI + scripts over heavy MCP servers when both are available. -- Use `rg` for fast code search. - -## MCP Servers (Avoid by Default) - -- Do not use GitHub MCP for routine GitHub tasks; use `gh` CLI. -- Do not use browser MCPs for UI testing; use `playwright-cli`. -- Keep active MCP set small. Large MCP tool surfaces waste context and reduce agent quality. - -## Troubleshooting - -- If a bug is unclear after local debugging and git-history review, search the web for recent framework/library issues and validated fixes. - -## Practical Boundaries - -- Keep diffs minimal and scoped to the request. -- Do not silently refactor unrelated areas. -- Preserve backwards compatibility unless the user asks for a breaking change. -- For UI changes, verify desktop and mobile behavior. -``` - ---- - -## web/README.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/web/README.md - -```markdown -## MintPass Web - -Serverless website and API that power the SMS verification flow and NFT minting at `mintpass.org`. - -### How it works (code map) -- UI: `src/pages/request/` — phone + OTP form, error handling, and success state -- API routes: - - `POST /api/sms/send` → rate-limit, optional IP/phone risk checks, then start Twilio Verify SMS (or non-prod smoke `kv-fallback`); returns `mode` (`verify` | `kv-fallback`) - - `POST /api/sms/verify` → verify OTP, mark phone as verified - - `POST /api/pre-check-eligibility` → quick check before sending code - - `POST /api/check-eligibility` → final eligibility check before mint - - `POST /api/mint` → on‑chain mint (when envs set) or record as minted -- Libraries and helpers: `lib/` — KV (Upstash), rate‑limits, cooldowns, hashing, SMS provider adapter, IP/phone intelligence, and admin auth/session utilities - -### Anti-Sybil and Security Requirements -- **Phone number database**: Track used numbers; prevent reuse for minting. -- **IP address tracking**: Rate limit per IP; detect abuse patterns. -- **SMS verification state**: Verify with Twilio Verify; keep short-lived fallback codes only for smoke flow. -- **Minter key security**: Private key only in Vercel env vars; never in code or logs. -- **Rate limiting**: Global and per-route limits to mitigate spam. -- **Geolocation filtering**: Optional country blocking via `middleware.ts`. -- **Audit trail (optional)**: Log key events to a separate store with redaction. - - **VPN/Proxy detection (optional)**: If `IPQS_API_KEY` is set, block VPNs/proxies/cloud provider IPs. - - **Disposable/VOIP phone detection (optional)**: If `ABSTRACTAPI_PHONE_KEY` is set, block disposable/VOIP/high-risk numbers. - - **Cooldowns**: Per-IP mint cooldown and per-IP/phone SMS send cooldowns configurable via env. - - **Keyed hashing (pepper)**: If `HASH_PEPPER` is set, phone numbers and IPs are stored as HMAC-SHA256 digests (domain-separated) in KV keys to reduce offline brute-force risk if a DB-only leak occurs. - -#### Preview-only testing behavior -- In Vercel Preview deployments (`VERCEL_ENV=preview`), the disposable/VOIP/high-risk phone check is bypassed in `POST /api/sms/send` to allow testing with temporary numbers. Production (`VERCEL_ENV=production`) remains strict and continues to block such numbers. - -### Vercel Setup (exact steps) -1. Create a new Vercel project and select this repo. Set root directory to `web`. -2. Add storage via Marketplace: Upstash → Redis. Create separate databases per environment (recommended): - - `mintpass-kv-prod` (Production) - - `mintpass-kv-preview` (Preview) - - `mintpass-kv-dev` (optional local) - Settings for each DB: Plan = Pay as You Go, Primary Region = `iad1` (US‑East), Read Regions = none, Eviction = off, Auto‑upgrade = on. -3. In Project Settings → Environment Variables, add: - - `MINTER_PRIVATE_KEY` (server only) - - `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_VERIFY_SERVICE_SID` (Verify mode; recommended for OTP) - - `BLOCKED_COUNTRIES` (comma-separated ISO codes if needed) - - `RATE_LIMIT_WINDOW_SECONDS`, `RATE_LIMIT_MAX_REQUESTS` (optional) - - `IPQS_API_KEY` (optional) to enable IP reputation checks - - `ABSTRACTAPI_PHONE_KEY` (optional) to enable disposable/VOIP phone checks - - `SMS_SEND_COOLDOWN_SECONDS` (optional) default 120 - - `MINT_IP_COOLDOWN_SECONDS` (optional) default 604800 (7 days) - - Map Upstash credentials to app envs per environment: - - Production: `KV_REST_API_URL` = Upstash prod REST URL; `KV_REST_API_TOKEN` = Upstash prod REST token - - Preview: `KV_REST_API_URL` = Upstash preview REST URL; `KV_REST_API_TOKEN` = Upstash preview REST token - - Local dev (optional): set the same in `.env.local` pointing to `mintpass-kv-dev` -4. Deploy. After first deploy, add the domain `mintpass.org` in Domains, set as primary. -5. Ensure the KV database is scoped to the production environment and not shared with preview. - -### Runtime regions -- Vercel Project → Settings → Functions → Region: set to `iad1` to co‑locate with Redis and reduce latency. - -### Storage notes -- Why Redis/KV: fast TTL keys for OTPs and cooldowns, atomic counters, and simple idempotency. -- Cost model (Pay as You Go): ~$0.20 per 100k commands. Typical flow is ~20 commands per successful mint. - -### Environments and KV mapping (prod/preview/local) -- **Databases to create**: - - `mintpass-kv-prod` → used by Vercel Production - - `mintpass-kv-preview` → used by Vercel Preview (and local dev by default) - - Optional later: `mintpass-kv-dev` → used only for local dev - -- **Vercel → Project → Settings → Environment Variables**: - - Production: set `KV_REST_API_URL` and `KV_REST_API_TOKEN` from `mintpass-kv-prod` - - Preview: set `KV_REST_API_URL` and `KV_REST_API_TOKEN` from `mintpass-kv-preview` - - Do not point local or preview to prod. - -- **Local development**: - - Copy `.env.example` to `.env.local` - - Set `KV_REST_API_URL` and `KV_REST_API_TOKEN` to the preview DB (or to `mintpass-kv-dev` if you created it) - - Run `yarn dev` - -### API Routes -- `POST /api/sms/send` → request SMS code (rate-limited; Verify-first with non-prod smoke fallback) -- `POST /api/sms/verify` → verify code and mark phone as verified -- `POST /api/check-eligibility` → confirm address + phone can mint -- `POST /api/mint` → on-chain mint on Base Sepolia when envs are set; otherwise records local minted state. Requires `authorAddress` string; country is derived from `x-vercel-ip-country` header. - -### Local Development - -Requires [Portless](https://github.com/vercel-labs/portless), installed with project dependencies via `corepack yarn install` - -```bash -corepack enable -corepack yarn install -corepack yarn dev # https://mintpass.localhost -``` - -The dev server runs at https://mintpass.localhost via [Portless](https://github.com/vercel-labs/portless), which gives each Bitsocial project a stable, named URL instead of a random port. To bypass Portless: `PORTLESS=0 yarn dev` - -To test API calls locally, use `curl` or your preferred REST client. - -### Smoke test (prod/preview) -Create local env files (not committed) with the target base URL and test identifiers. Include Upstash REST URL/Token when you need `kv-fallback` full E2E: - -- `.env.smoke.prod` (for Production) -``` -BASE_URL=https://mintpass.org -PHONE=+15555550123 -ADDR=0x1111111111111111111111111111111111111111 -``` - -- `.env.smoke.preview` (for Preview) -``` -KV_REST_API_URL= -KV_REST_API_TOKEN= -PREVIEW_BASE_URL=https://.vercel.app -PHONE=+15555550123 -ADDR=0x1111111111111111111111111111111111111111 -BYPASS_TOKEN=... -SMOKE_TEST_TOKEN=... -``` - -Run the script: -```bash -yarn smoke:prod -# or -yarn smoke:preview -``` - -Notes: -- The script inspects `POST /api/sms/send` response JSON for `mode` (`verify` | `kv-fallback`). -- **mode=kv-fallback**: runs full E2E (fetch code from KV/debug → verify → eligibility → mint). -- **mode=verify + prod**: send-health (send ok) + eligibility check only; OTP verify/mint skipped by design. -- **mode=verify + preview**: full E2E only when fallback is used; otherwise OTP verify/mint skipped with clear message. -- `KV_REST_API_URL` and `KV_REST_API_TOKEN` are required only when `mode=kv-fallback`. -- `BYPASS_TOKEN` and `SMOKE_TEST_TOKEN` supported. -- The script loads ENVFILE (if provided), then `.env.smoke.{prod|preview}`, then `.env.local`, then `.env`. -- No SMS provider needed for kv-fallback; OTP is read from KV via REST for testing. -- Cooldowns and rate limits apply; you may need to wait 120s for repeated runs. - - If your Preview custom domain is protected via Vercel Auth and proxied by Cloudflare, prefer the vercel.app URL for smoke tests. - -### Contributor quickstart: Preview smoke test -- Create/connect a Preview Upstash KV to this project and ensure the Preview env has `KV_REST_API_URL` and `KV_REST_API_TOKEN` (use the plain names; avoid double prefixes). Optionally set `SMOKE_TEST_TOKEN` to enable a debug echo of the OTP. -- If Deployment Protection is enabled, create a Protection Bypass token and use it as `BYPASS_TOKEN` in the `.env.smoke.preview` file. The script will set the bypass cookie and header automatically. -- Trigger a new Preview deployment and copy its per-deployment vercel.app URL from Vercel → Deployments. Use that URL as `PREVIEW_BASE_URL`. -- Create `web/.env.smoke.preview` with: - - `KV_REST_API_URL`, `KV_REST_API_TOKEN` - - `PREVIEW_BASE_URL`, `PHONE`, `ADDR` - - `BYPASS_TOKEN` (only if protection is enabled) - - `SMOKE_TEST_TOKEN` (optional; returns `debugCode` during send) -- Run `corepack yarn install` then `corepack yarn smoke:preview`. -- Expected: send → verify → eligibility=true → mint ok (stubbed unless on-chain envs are set). - -### Environment Variables -Copy `.env.example` to `.env.local` and fill in values. Do not commit `.env.local`. - -Required for runtime: -- `KV_REST_API_URL`, `KV_REST_API_TOKEN` -- `HASH_PEPPER` (optional; HMAC key for hashing identifiers used in KV keys) - -Optional for SMS provider (Twilio Verify preferred): -- `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_VERIFY_SERVICE_SID` — required in production for Verify mode. Create a Verify Service in Twilio Console and use its SID. -- Legacy (kv-fallback only; deprecated for OTP): `TWILIO_MESSAGING_SERVICE_SID` or `SMS_SENDER_ID` - -Optional for on-chain mint (Base Sepolia): -- `MINTER_PRIVATE_KEY` -- `MINTPASSV1_ADDRESS_BASE_SEPOLIA` -- `BASE_SEPOLIA_RPC_URL` - -Optional for smoke testing (Preview only): -- `SMOKE_TEST_TOKEN` (debug-only echo of OTP when `x-smoke-test-token` header is present) -- `HASH_PEPPER` (optional; if set, `scripts/smoke-test.sh` derives the hashed OTP KV key via OpenSSL when available) - -### Next Steps -- Add additional authentication methods beyond SMS (e.g., pay‑to‑mint, others). -- Add abuse heuristics (velocity checks per phone/IP, lightweight device signals if needed). -- Continue UI polish and success animations; broaden admin tooling and observability. - -This project includes both backend APIs and frontend UI components built with shadcn/ui and mobile-first responsive design. - -### Operational recommendations -- Put Cloudflare in front of Vercel for additional DDoS protection and WAF/challenge. Set `CF-Connecting-IP` pass-through so backend uses real client IP. -- Monitor rate-limit headers (`X-RateLimit-*`) and adjust envs based on traffic. -``` - ---- - -## challenge/README.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/challenge/README.md - -```markdown -# @bitsocial/mintpass-challenge - -MintPass challenge for pkc-js that verifies users own a MintPass NFT before publishing. - -## Requirements - -- Node.js `>=22` -- ESM-only environment - -## Using mintpass in your community - -Community owners add the mintpass challenge to their community settings. When enabled, every publication (post, reply, vote) requires the author to own a MintPass NFT. The challenge is published as [`@bitsocial/mintpass-challenge`](https://www.npmjs.com/package/@bitsocial/mintpass-challenge) on npm. - -### With pkc-js over RPC - -If your RPC server is already running, first install the challenge on the server: - -```bash -bitsocial challenge install @bitsocial/mintpass-challenge -``` - -Then from your RPC client, connect and set the challenge on your community by name — no npm install or challenge registration needed on the client side: - -```ts -import PKC from "@pkcprotocol/pkc-js"; - -const pkc = await PKC({ - pkcRpcClientsOptions: ["ws://localhost:9138"] -}); - -const community = await pkc.createCommunity({ address: "your-community-address.bso" }); - -await community.edit({ - settings: { - challenges: [ - { - name: "@bitsocial/mintpass-challenge", - options: { - chainTicker: "base", - contractAddress: "0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9", - requiredTokenType: "0", - transferCooldownSeconds: "604800" - } - } - ] - } -}); -``` - -### With pkc-js (TypeScript) - -Install the challenge package: - -```bash -npm install @bitsocial/mintpass-challenge -``` - -Register the challenge and configure your community: - -```typescript -import PKC from '@pkcprotocol/pkc-js' -import { mintpass } from '@bitsocial/mintpass-challenge' - -// Register the challenge so it can be referenced by name -PKC.challenges['@bitsocial/mintpass-challenge'] = mintpass - -const pkc = await PKC({ /* your pkc options */ }) -const community = await pkc.createCommunity({ address: 'your-community.bso' }) - -await community.edit({ - settings: { - challenges: [{ - name: '@bitsocial/mintpass-challenge', - options: { - chainTicker: 'base', - contractAddress: '0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9', - requiredTokenType: '0', - transferCooldownSeconds: '604800', - } - }] - } -}) -``` - -#### Challenge options - -All option values must be strings (pkc-js challenge convention). - -| Option | Default | Description | -|--------|---------|-------------| -| `chainTicker` | `"base"` | Chain where MintPass contract is deployed | -| `contractAddress` | Known deployment per chain | If omitted and `chainTicker` is supported, defaults to the known MintPass deployment for that chain | -| `requiredTokenType` | `"0"` | Required token type (0=SMS, 1=Email, 2+=future methods) | -| `transferCooldownSeconds` | `"604800"` | Cooldown period after NFT transfer (1 week) | -| `error` | Default message | Custom error message for users without NFT. Use `{authorAddress}` as a placeholder | - -### With bitsocial-cli - -Install the challenge package: - -```bash -bitsocial challenge install @bitsocial/mintpass-challenge -``` - -Edit your community to use the challenge: - -```bash -bitsocial community edit your-community.bso \ - '--settings.challenges[0].name' @bitsocial/mintpass-challenge \ - '--settings.challenges[0].options.chainTicker' 'base' \ - '--settings.challenges[0].options.contractAddress' '0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9' \ - '--settings.challenges[0].options.requiredTokenType' '0' \ - '--settings.challenges[0].options.transferCooldownSeconds' '604800' -``` - -See the [bitsocial-cli documentation](https://github.com/bitsocial/bitsocial-cli) for full CLI reference. - -## Scripts - -```bash -yarn build -yarn test -yarn clean -``` -``` - ---- - -## contracts/README.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/contracts/README.md - -```markdown -# MintPassV1 Smart Contract - -This directory contains the MintPassV1 NFT smart contract for the MintPass authentication system. - -## Features - -- **ERC721 + ERC721Enumerable**: Standard NFT functionality with enumeration support -- **Access Control**: Admin and Minter roles for secure operations -- **Token Types**: Each NFT has a type (e.g., 0 = SMS verification, 1 = Email verification) -- **Batch Operations**: Gas-efficient batch minting -- **Utility Functions**: Comprehensive ownership and type checking functions -- **Upgradeable Metadata**: Admin can update base URI for metadata - -## Contract Specification - -- **Name**: MintPassV1 -- **Symbol**: MPSS -- **Base URI**: `https://mintpass.org/mint1/` -- **Network**: Base (Layer 2) -- **Token Types**: uint16 (65,536 possible types) - - Type 0: SMS verification - - Type 1+: Future verification methods - -## Quick Start - -Contributor setup: -1. Run `nvm install && nvm use` -2. Run `corepack enable` once per machine -3. Use plain `yarn install`, `yarn compile`, and `yarn test` - -1. **Install dependencies**: - ```bash - yarn install - ``` - -2. **Compile contracts**: - ```bash - yarn compile - ``` - -3. **Run tests**: - ```bash - yarn test - ``` - -4. **Deploy to Base Sepolia (testnet)**: - ```bash - # Copy .env.example to .env and fill in your values - cp .env.example .env - - # Deploy - yarn deploy:base-sepolia - ``` - -5. **Deploy to Base Mainnet**: - ```bash - yarn deploy:base - ``` - -## Environment Setup - -Create a `.env` file with the following variables: - -```env -# Private key for deployment (without 0x prefix) -PRIVATE_KEY=your_private_key_here - -# Base network explorer API key for contract verification -BASESCAN_API_KEY=your_basescan_api_key_here - -# Gas reporting (optional) -REPORT_GAS=true -``` - -## Core Functions - -### Minting -- `mint(address to, uint16 tokenType)`: Mint single NFT -- `mintBatch(address[] recipients, uint16[] tokenTypes)`: Batch mint - -### Token Information -- `tokenType(uint256 tokenId)`: Get token type for a token ID -- `tokensOfOwner(address owner)`: Get all tokens and types owned by address -- `tokensOfOwners(address[] owners)`: Batch version of above - -### Ownership Checks -- `ownsTokenType(address owner, uint16 tokenType)`: Check if owns specific type -- `ownsTokenTypes(address owner, uint16[] tokenTypes)`: Check if owns all types -- `ownsOneOfTokenTypes(address owner, uint16[] tokenTypes)`: Check if owns any type - -### Admin Functions -- `setBaseURI(string newBaseURI)`: Update metadata base URI (admin only) -- Role management via AccessControl - -## Testing - -The test suite covers: -- ✅ Deployment and initialization -- ✅ Minting (single and batch) -- ✅ Token type tracking -- ✅ All utility functions -- ✅ Access control -- ✅ ERC721 compatibility -- ✅ Admin functions - -Run tests with: -```bash -yarn test -``` - -For coverage report: -```bash -yarn coverage -``` - -## Gas Optimization - -The contract includes several gas optimizations: -- Batch minting for multiple NFTs -- Efficient token type storage -- Optimized enumeration functions -- No unnecessary storage reads - -## Security Features - -- **Role-based access control**: Admin and Minter roles -- **No proxy pattern**: Immutable core logic (versioned approach) -- **Transfer detection**: External systems can implement cooldowns -- **Admin functions limited**: Only cosmetic changes allowed - -## Deployment Networks - -- **Base Sepolia** (Testnet): Chain ID 84532 -- **Base Mainnet**: Chain ID 8453 - -## Next Steps - -1. Deploy contract to Base Sepolia for testing -2. Verify contract on BaseScan -3. Test minting functionality -4. Integrate with the PKC challenge system -5. Iterate on web integration and tooling -``` - ---- - -## docs/pkc-info.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/pkc-info.md - -```markdown -# Info about PKC - -PKC-js is a decentralized Reddit-like protocol built on top of IPFS and libp2p PubSub. It enables censorship-resistant communities called communities, each with its own moderation rules, where moderation is enforced through challenge-response games between peers rather than through centralized control. - -### 1. IPFS + PubSub Backbone -PKC-js uses libp2p's PubSub (gossipsub) to propagate posts and comments. Each community is essentially a pubsub topic. Peers subscribe to the topics they care about (communities) and exchange content via the decentralized mesh. -### 2. Community Moderation via P2P Challenge-Response -Instead of centralized mods, PKC lets community owners define arbitrary logic (expressed in WASM or interpreted JavaScript) for moderation challenges. Any peer can challenge a post or comment according to the rules defined by the community. -### 3. Challenges -A challenge is a message sent directly (peer-to-peer) to the post/comment author. The author must reply with a valid response (or the post is hidden locally). This creates an adversarial moderation environment that still honors local control and censorship-resistance. -### 4 No Global Consensus -The system is subjective. Every peer maintains their own view of the network, deciding whether to accept or reject content based on challenges they observe and responses received. -### 5. Example: -A community might define a rule like: -"You must prove that your account is older than 7 days to post." -This challenge would be enforced by sending a P2P challenge to posters and expecting a valid proof. - -Existing Challenge API on pkc-js library: - -```ts -import { EventEmitter } from "events"; -import { pkcJsChallenges } from "../../../dist/node/runtime/node/community/challenges/index.js"; - -// define mock Author instances -const highKarmaAuthor = { - address: "high-karma.eth", - wallets: { eth: { address: "0x...", signature: "0x..." } } -}; -const lowKarmaAuthor = { address: "low-karma.eth" }; -const authors = [highKarmaAuthor, lowKarmaAuthor]; - -// mock comment instance -function Comment(cid) { - const split = cid.replace("Qm...", "").split(","); - const communityAddress = split[0]; - const karma = split[1]; - const age = split[2]; - this.communityAddress = communityAddress; - this.updatedAt = undefined; - - // define author - this.author = { address: "Qm..." }; - if (karma === "high") { - this.author.address = highKarmaAuthor.address; - } else if (karma === "low") { - this.author.address = lowKarmaAuthor.address; - } - - // use this value to mock giving 'high' or 'low' karma to the author - this.karma = karma; - this.age = age; -} -Object.setPrototypeOf(Comment.prototype, EventEmitter.prototype); - -Comment.prototype.update = function () { - setTimeout(() => { - this.updatedAt = 123456; - if (this.karma === "high") { - this.author.community = { - postScore: 1000, - replyScore: 1000 - }; - } else if (this.karma === "low") { - this.author.community = { - postScore: 1, - replyScore: 1 - }; - } - if (this.age === "old") { - this.author.community.firstCommentTimestamp = Math.round(Date.now() / 1000) - 60 * 60 * 24 * 999; // 999 days ago - } else if (this.age === "new") { - this.author.community.firstCommentTimestamp = Math.round(Date.now() / 1000) - 60 * 60 * 24 * 1; // 1 day ago - } - this.emit("update", this); - }, 5).unref?.(); -}; - -Comment.prototype.stop = function () { - this.removeAllListeners(); -}; - -// mock pkc sync -const createPKC = () => { - return { - getComment: (cid) => new Comment(cid), - createComment: (cid) => new Comment(cid) - }; -}; -// mock PKC async -const PKC = () => createPKC(); - -// define mock challenges included with pkc-js -PKC.challenges = pkcJsChallenges; - -// define mock Community instances -const textMathChallengeCommunity = { - title: "text-math challenge community", - settings: { - challenges: [ - { - name: "text-math", - options: { difficulty: "3" }, - description: "Complete a math challenge." - } - ] - } -}; -// comment out because don't know how to make the captcha node code work in the browser -// const captchaAndMathChallengeCommunity = { -// title: 'captcha and math challenge community', -// settings: { -// challenges: [ -// { -// name: 'captcha-canvas-v3', -// options: { -// width: '600', -// height: '400', -// characters: '10', -// color: '#000000' -// }, -// description: 'Complete a captcha challenge.' -// }, -// { -// name: 'text-math', -// options: {difficulty: '2'}, -// description: 'Complete a math challenge.' -// } -// ] -// } -// } -const excludeHighKarmaChallengeCommunity = { - title: "exclude high karma challenge community", - settings: { - challenges: [ - { - name: "text-math", - options: { difficulty: "3" }, - // exclude if the author match any one item in the array - exclude: [ - { postScore: 100, replyScore: 100 }, // exclude author that has more than 100 post score AND 100 reply score - // exclude author with account age older than 100 days (Math.round(Date.now() / 1000)- 60*60*24*100) - { firstCommentTimestamp: 60 * 60 * 24 * 100 } - ] - } - ] - } -}; -const excludeAccountAgeChallengeCommunity = { - title: "exclude account age challenge community", - settings: { - challenges: [ - { - name: "fail", - // exclude if the author match any one item in the array - exclude: [ - // exclude author with account age older than 100 days (Math.round(Date.now() / 1000)- 60*60*24*100) - { firstCommentTimestamp: 60 * 60 * 24 * 100 } - ] - } - ] - } -}; -const whitelistChallengeCommunity = { - title: "whitelist challenge community", - settings: { - challenges: [ - { - // the fail challenge always fails - name: "fail", - options: { - error: `You're not whitelisted.` - }, - // challenge should never be triggered if the author address is excluded - exclude: [{ address: ["high-karma.eth"] }] - } - ] - } -}; -const blacklistChallengeCommunity = { - title: "blacklist challenge community", - settings: { - challenges: [ - { - name: "blacklist", - options: { - blacklist: "low-karma.eth,some-author.eth" - } - } - ] - } -}; -// comment out because don't know how to require external challenge in the browser tests -// const erc20PaymentChallengeCommunity = { -// title: 'erc20 payment challenge community', -// settings: { -// challenges: [ -// { -// path: path.join(__dirname, 'challenges', 'erc20-payment'), -// options: { -// chainTicker: 'eth', -// contractAddress: '0x...', -// recipientAddress: '0x...', -// symbol: 'PLEB', -// decimals: '18', -// postPrice: '1000', -// replyPrice: '100', -// votePrice: '10' -// }, -// }, -// ] -// } -// } -const evmContractCallChallengeCommunity = { - title: "evm contract call challenge community", - settings: { - challenges: [ - { - name: "evm-contract-call", - options: { - chainTicker: "eth", - // contract address - address: "0x...", - // abi of the contract method - abi: '{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}', - condition: ">1000", - // error to display to the user if condition fails - error: "PLEB token balance must be greater than 1000." - } - } - ] - } -}; -const passwordChallengeCommunity = { - title: "password challenge community", - settings: { - challenges: [ - { - name: "question", - options: { - question: "What is the password?", - answer: "password" - } - } - ] - } -}; -const excludeFriendlySubKarmaChallengeCommunity = { - title: "exclude friendly community karma challenge community", - settings: { - challenges: [ - { - name: "fail", - exclude: [ - // exclude author with karma in those subs using publication.challengeCommentCids - { - community: { - addresses: ["friendly-sub.eth", "friendly-sub2.eth"], - postScore: 100, - replyScore: 100, - maxCommentCids: 3 - } - } - ] - } - ] - } -}; -const twoOutOf4SuccessChallengeCommunity = { - title: "2 out of 4 success challenge community", - settings: { - // challenge 0, 1 fail, but excluded if 2, 3 succeed, which makes challengeVerification.challengeSuccess = true - challenges: [ - { - name: "fail", - exclude: [{ challenges: [2, 3] }] - }, - { - name: "fail", - exclude: [{ challenges: [2, 3] }] - }, - { - name: "blacklist", - options: { blacklist: "low-karma.eth,some-author.eth" } - }, - { - name: "blacklist", - options: { blacklist: "low-karma.eth,some-author.eth" } - } - ] - } -}; -const twoOutOf4SuccessInverseChallengeCommunity = { - title: "2 out of 4 success inverse challenge community", - settings: { - // challenge 0, 1 fail, but excluded if 2, 3 succeed, which makes challengeVerification.challengeSuccess = true - challenges: [ - { - name: "blacklist", - options: { blacklist: "low-karma.eth,some-author.eth" } - }, - { - name: "blacklist", - options: { blacklist: "low-karma.eth,some-author.eth" } - }, - { - name: "fail", - exclude: [{ challenges: [0, 1] }] - }, - { - name: "fail", - exclude: [{ challenges: [0, 1] }] - } - ] - } -}; -const rateLimitChallengeCommunity = { - title: "rate limit challenge community", - settings: { - challenges: [ - { - name: "fail", - options: { - error: `You're doing this too much, rate limit: 0 post/h, 10 replies/h, 100 votes/h.` - }, - exclude: [ - // different rate limit per publication type - { publicationType: { post: true }, rateLimit: 0 }, // 0 per hour - { publicationType: { reply: true }, rateLimit: 10 }, // 10 per hour - { publicationType: { vote: true }, rateLimit: 100 } // 100 per hour - ] - } - ] - } -}; -const rateLimitChallengeSuccessChallengeCommunity = { - title: "rate limit challenge success challenge community", - settings: { - challenges: [ - { - name: "fail", - options: { - error: `You're doing this too much.` - }, - exclude: [ - // only 1 successful publication per hour - { rateLimit: 1, rateLimitChallengeSuccess: true }, - // only 100 failed challenge request per hour - { rateLimit: 100, rateLimitChallengeSuccess: false } - ] - } - ] - } -}; -const excludeModsChallengeCommunity = { - title: "exclude mods challenge community", - roles: { - "high-karma.eth": { - role: "moderator" - } - }, - settings: { - challenges: [ - { - name: "fail", - options: { - error: `You're not a mod.` - }, - exclude: [{ role: ["moderator", "admin", "owner"] }] - } - ] - } -}; - -// define mock author karma scores and account age -const communityAuthors = {}; -communityAuthors[highKarmaAuthor.address] = {}; -communityAuthors[highKarmaAuthor.address][excludeHighKarmaChallengeCommunity.title] = { - postScore: 1000, - replyScore: 1000, - firstCommentTimestamp: 1 -}; -communityAuthors[highKarmaAuthor.address][excludeAccountAgeChallengeCommunity.title] = { - postScore: 1, - replyScore: 1, - firstCommentTimestamp: 1 -}; -communityAuthors[lowKarmaAuthor.address] = {}; -communityAuthors[lowKarmaAuthor.address][excludeHighKarmaChallengeCommunity.title] = { postScore: 1, replyScore: 1000 }; -communityAuthors[lowKarmaAuthor.address][excludeAccountAgeChallengeCommunity.title] = { postScore: 1000, replyScore: 1000 }; - -// define mock friendly community comment cids -const challengeCommentCids = {}; -challengeCommentCids[highKarmaAuthor.address] = ["Qm...friendly-sub.eth,high,old", "Qm...friendly-sub.eth,high,old"]; - -const challengeAnswers = {}; -challengeAnswers[highKarmaAuthor.address] = {}; -challengeAnswers[highKarmaAuthor.address][passwordChallengeCommunity.title] = ["password"]; -challengeAnswers[lowKarmaAuthor.address] = {}; -challengeAnswers[lowKarmaAuthor.address][passwordChallengeCommunity.title] = ["wrong"]; - -const communities = [ - textMathChallengeCommunity, - // captchaAndMathChallengeCommunity, - excludeHighKarmaChallengeCommunity, - excludeAccountAgeChallengeCommunity, - whitelistChallengeCommunity, - blacklistChallengeCommunity, - // erc20PaymentChallengeCommunity, - // evmContractCallChallengeCommunity, - passwordChallengeCommunity, - excludeFriendlySubKarmaChallengeCommunity, - twoOutOf4SuccessChallengeCommunity, - twoOutOf4SuccessInverseChallengeCommunity, - rateLimitChallengeCommunity, - rateLimitChallengeSuccessChallengeCommunity, - excludeModsChallengeCommunity -]; - -const results = {}; -results[textMathChallengeCommunity.title] = { - "high-karma.eth": { - pendingChallenges: [{ challenge: "660 - 256", type: "text/plain" }] - }, - "low-karma.eth": { - pendingChallenges: [{ challenge: "69 * 63", type: "text/plain" }] - } -}; -// comment out because don't know how to make the captcha node code work in the browser -// results[captchaAndMathChallengeCommunity.title] = { -// 'high-karma.eth': { -// pendingChallenges: [ -// { challenge: '...', type: 'image' }, -// { challenge: '94 + 25', type: 'text/plain' } -// ] -// }, -// 'low-karma.eth': { -// pendingChallenges: [ -// { challenge: '...', type: 'image' }, -// { challenge: '99 - 90', type: 'text/plain' } -// ] -// } -// } -results[excludeHighKarmaChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - pendingChallenges: [{ challenge: "82 * 45", type: "text/plain" }] - } -}; -results[excludeAccountAgeChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're not allowed to publish." } - } -}; -results[whitelistChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're not whitelisted." } - } -}; -results[blacklistChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're blacklisted." } - } -}; -// comment out because don't know how to require external challenge in the browser tests -// results[erc20PaymentChallengeCommunity.title] = { -// 'high-karma.eth': { challengeSuccess: true }, -// 'low-karma.eth': { -// challengeSuccess: false, -// challengeErrors: {"0": "Author doesn't have wallet (eth) set." } -// } -// } -results[evmContractCallChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "Author doesn't have a wallet set." } - } -}; -results[passwordChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { challengeSuccess: false, challengeErrors: { 0: "Wrong answer." } } -}; -results[excludeFriendlySubKarmaChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're not allowed to publish." } - } -}; -results[twoOutOf4SuccessChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { - 0: "You're not allowed to publish.", - 1: "You're not allowed to publish.", - 2: "You're blacklisted.", - 3: "You're blacklisted." - } - } -}; -results[twoOutOf4SuccessInverseChallengeCommunity.title] = { - "high-karma.eth": { challengeSuccess: true }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { - 0: "You're blacklisted.", - 1: "You're blacklisted.", - 2: "You're not allowed to publish.", - 3: "You're not allowed to publish." - } - } -}; -results[rateLimitChallengeCommunity.title] = { - "high-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're doing this too much, rate limit: 0 post/h, 10 replies/h, 100 votes/h." } - }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're doing this too much, rate limit: 0 post/h, 10 replies/h, 100 votes/h." } - } -}; -results[rateLimitChallengeSuccessChallengeCommunity.title] = { - "high-karma.eth": { - challengeSuccess: true - }, - "low-karma.eth": { - challengeSuccess: true - } -}; -results[excludeModsChallengeCommunity.title] = { - "high-karma.eth": { - challengeSuccess: true - }, - "low-karma.eth": { - challengeSuccess: false, - challengeErrors: { 0: "You're not a mod." } - } -}; - -// add mock pkc to add the mock community instances -for (const community of communities) { - community._pkc = createPKC(); -} - -export { PKC, communities, authors, communityAuthors, challengeCommentCids, challengeAnswers, results }; - -```ts -``` - ---- - -## docs/challenges/README.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/README.md - -```markdown -NOTE: Challenges included with pkc-js are located in ./pkc-js-challenges - -#### How to use: - -```js -const {getChallengeVerification} = require('./challenges') - -// NOTE: publishing challenge pubsub message and waiting for challenge answer message go inside the getChallengeAnswers callback -// because they are sometimes skipped -const getChallengeAnswers = async (challenges) => { - // ...get challenge answers from user. e.g.: - // step 1. community publishes challenge pubsub message with `challenges` provided in argument of `getChallengeAnswers` - // step 2. community waits for challenge answer pubsub message with `challengeAnswers` and then returns `challengeAnswers` - return challengeAnswers -} - -// NOTE: we try to get challenge verification immediately after receiving challenge request -// because some challenges are automatic and skip the challenge message -let challengeVerification -try { - challengeVerification = await getChallengeVerification(challengeRequest, community, getChallengeAnswers) -} -// getChallengeVerification will throw if one of the getChallenge function throws, which indicates a bug with the challenge script -catch (e) { - // notify the community owner that that one of his challenge is misconfigured via an error event - community.emit('error', e.message) - - // notify the author that his publication wasn't published because the community is misconfigured - challengeVerification = { - challengeSuccess: false, - reason: `One of the community challenges is misconfigured: ${e.message}` - } -} -``` - -#### Types: - -```javascript -// list of challenges included with pkc-js -PKC.challenges = {[challengeName: string]: ChallengeFile} - -// new props -ChallengeRequestMessage { - encrypted: Encrypted - /* ChallengeRequestMessage.encrypted.ciphertext decrypts to JSON { - publication: Publication - challengeAnswers?: string[] // some challenges might be included in community.challenges and can be pre-answered - challengeCommentCids?: string[] // some challenges could require including comment cids in other communities, like friendly community karma challenges - } */ -} -Community { - // challenges is public, part of the IPNS record - challenges: CommunityChallenge[] - // settings is private, not part of the IPNS record - settings: { - challenges: CommunityChallengeSettings[] - } -} - -// public challenges types -CommunityChallenge { // copy values from private community.settings and publish to community.challenges - exclude?: Exclude[] // copied from community.settings.challenges.exclude - description?: string // copied from community.settings.challenges.description - challenge?: string // copied from ChallengeFile.challenge - type?: // copied from ChallengeFile.type -} -CommunityChallengeSettings { // the private settings of the challenge (community.settings.challenges) - path?: string // (only if name is undefined) the path to the challenge js file, used to get the props ChallengeFile {optionInputs, type, getChallenge} - name?: string // (only if path is undefined) the challengeName from PKC.challenges to identify it - options?: [optionPropertyName: string]: string // the options to be used to the getChallenge function, all values must be strings for UI ease of use - exclude?: Exclude[] // singular because it only has to match 1 exclude, the client must know the exclude setting to configure what challengeCommentCids to send - description?: string // describe in the frontend what kind of challenge the user will receive when publishing -} -ChallengeFile { // the result of the function exported by the challenge file - optionInputs?: OptionInput[] // the options inputs fields to display to the user - type: 'image/png' | 'text/plain' | 'chain/' - challenge?: string // some challenges can be static and asked before the user publishes, like a password for example - description?: string // describe what the challenge does to display in the UI - getChallenge: GetChallengeFunction -} -GetChallengeFunction { - (challenge: CommunityChallengeSettings, challengeRequest: ChallengeRequestMessage, challengeIndex: number): Challenge | ChallengeResult -} -Challenge { // if the result of a challenge can't be optained by getChallenge(), return a challenge - challenge: string // e.g. '2 + 2' - verify: async (answer: string): ChallengeResult - type: 'image/png' | 'text/plain' | 'chain/' -} -ChallengeResult { // if the result of a challenge can be optained by getChallenge, return the result - success?: boolean - error?: string // the reason why the challenge failed, add it to ChallengeVerificationMessage.errors -} -Exclude { // all conditions in Exclude are AND, for OR, use another Exclude item in the Exclude array - community?: ExcludeCommunity // exclude if author karma (from challengeRequestMessage.challengeCommentCids) in another community is greater or equal - postScore?: number // exclude if author post score is greater or equal - postReply?: number // exclude if author reply score is greater or equal - firstCommentTimestamp?: number // exclude if author account age is greater or equal than now - firstCommentTimestamp - challenges?: number[] // exclude if all challenges with indexes passed, e.g. challenges: [0, 1] excludes if challenges at index 0 AND 1 passed, plural because has to match all - publicationType?: ExcludePublicationType // exclude post, reply, vote, etc. - role?: string[] // exclude challenge if author.role.role = one of the string, singular because it only has to match 1 role - address?: string[] // exclude challenge if author.address = one of the string, singular because it only has to match 1 address - rateLimit?: number // exclude if publication per hour is lower than ratelimit - rateLimitChallengeSuccess?: boolean // only rate limit if the challengeVerification.challengeSuccess === rateLimitChallengeSuccess -} -ExcludePublicationType { // singular because it only has to match 1 publication type - post?: boolean // exclude challenge if publication is a post - reply?: boolean // exclude challenge if publication is a reply - vote?: boolean // exclude challenge if publication is a vote - commentEdit?: boolean // exclude challenge if publication is a comment edit - commentModeration?: boolean // exclude challenge if publication is a comment moderation -} -ExcludeCommunity { // singular because it only has to match 1 community - addresses: string[] // list of community addresses that can be used to exclude, plural because not a condition field like 'role' - maxCommentCids: number // maximum amount of comment cids that will be fetched to check - postScore?: number - postReply?: number - firstCommentTimestamp?: number // exclude if author account age is greater or equal than now - firstCommentTimestamp -} -OptionInput { - option: string // option property name, e.g. characterCount - label: string // option title, e.g. Character Count - default: string // option default value, e.g. 10 - description?: string // e.g. Amount of characters of the captcha - placeholder?: string // the value to display if the input field is empty, e.g. 10 - required?: boolean // the option is required, the challenge will throw without it -} -``` - -#### Ideas: - -- interface so that the community owner can display on publication.author.community the amount paid by the user for payment challenges -- "standard" challenges like "fail" and "evm-contract-call" so that the frontend could calculate the exclude and result of the challenge to see if the author passes it before even publishing -``` - ---- - -## docs/challenges/question.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/question.md - -```markdown -# Question challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: - -```ts -import type { Challenge, ChallengeFile, ChallengeResult, CommunityChallengeSetting } from "../../../../../community/types.js"; -import type { DecryptedChallengeRequestMessageTypeWithCommunityAuthor } from "../../../../../pubsub-messages/types.js"; - -const optionInputs = >[ - { - option: "question", - label: "Question", - default: "", - description: "The question to answer.", - placeholder: "" - }, - { - option: "answer", - label: "Answer", - default: "", - description: "The answer to the question.", - placeholder: "", - required: true - } -]; - -const type: Challenge["type"] = "text/plain"; - -const description = `Ask a question, like 'What is the password?'`; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number -): Promise => { - if (!communityChallengeSettings?.options?.question) throw Error("No option question"); - let answer = communityChallengeSettings?.options?.answer; - if (!answer) { - throw Error("no option answer"); - } - - // use the answer preincluded in the challenge request when possible - const challengeAnswer = challengeRequestMessage?.challengeAnswers?.[challengeIndex]; - - // the author didn't preinclude his answer, so send him a pubsub challenge message - if (challengeAnswer === undefined) { - return { - challenge: communityChallengeSettings?.options?.question, - verify: async (_answer: string) => { - if (_answer === answer) - return { - success: true - }; - return { - success: false, - error: "Wrong answer." - }; - }, - type - }; - } - - // the author did preinclude his answer, but it's wrong, so send him a failed challenge verification - if (challengeAnswer !== answer) { - return { - success: false, - error: "Wrong answer." - }; - } - - // the author did preinclude his answer, and it's correct, so send him a success challenge verification - return { - success: true - }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - // some challenges can prepublish the challenge so that it can be preanswered - // in the challengeRequestMessage - const question = communityChallengeSettings?.options?.question; - const challenge = question; - - return { getChallenge, optionInputs, type, challenge, description }; -} - -export default ChallengeFileFactory; -``` -``` - ---- - -## docs/challenges/evm-contract-call.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/evm-contract-call.md - -```markdown -# EVM contract call challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: - -```ts -import { LocalCommunity } from "../../../local-community.js"; -import { getPkcAddressFromPublicKey } from "../../../../../../signer/util.js"; -import type { ChainTicker } from "../../../../../../types.js"; -import type { - DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - PublicationWithCommunityAuthorFromDecryptedChallengeRequest -} from "../../../../../../pubsub-messages/types.js"; -import type { Challenge, ChallengeFile, ChallengeResult, CommunityChallengeSetting } from "../../../../../../community/types.js"; -import { decodeFunctionResult, encodeFunctionData } from "viem"; -import Logger from "@pkcprotocol/pkc-logger"; -import type { PKC } from "../../../../../../pkc/pkc.js"; -import { derivePublicationFromChallengeRequest, isStringDomain } from "../../../../../../util.js"; -import { normalize } from "viem/ens"; - -const optionInputs = >[ - { - option: "chainTicker", - label: "chainTicker", - default: "eth", - description: "The chain ticker", - placeholder: "eth", - required: true - }, - { - option: "address", - label: "Address", - default: "", - description: "The contract address.", - placeholder: "0x...", - required: true - }, - { - option: "abi", - label: "ABI", - default: "", - description: "The ABI of the contract method.", - placeholder: '{"constant":true,"inputs":[{"internalType":"address","name":"account...', - required: true - }, - { - option: "condition", - label: "Condition", - default: "", - description: "The condition the contract call response must pass.", - placeholder: ">1000", - required: true - }, - { - option: "error", - label: "Error", - default: `Contract call response doesn't pass condition.`, - description: "The error to display to the author." - } -]; - -const description = "The response from an EVM contract call passes a condition, e.g. a token balance challenge."; - -// Unrelated to challenges API -//prettier-ignore -const nftAbi = [ - {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}, - {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"} - ]; - -const supportedConditionOperators = ["=", ">", "<"]; - -const _getChainProviderWithSafety = (pkc: PKC, chainTicker: ChainTicker) => { - const chainProvider = pkc.chainProviders[chainTicker]; - if (!chainProvider) throw Error("pkc.chainProviders[chainTicker] is not defined"); - return chainProvider; -}; - -const verifyAuthorWalletAddress = async (props: { - publication: PublicationWithCommunityAuthorFromDecryptedChallengeRequest; - chainTicker: string; - condition: string; - abi: any; - error: string; - contractAddress: string; - pkc: PKC; -}): Promise => { - const log = Logger("pkc-js:local-community:evm-contract-call-v1:verifyAuthorWalletAddress"); - const authorWallet = props.publication.author.wallets?.[props.chainTicker]; - if (typeof authorWallet?.address !== "string") return "The author wallet address is not defined"; - // Verify first if the signature and resolved address is correct, before running the smart contract - if (isStringDomain(authorWallet.address)) { - // resolve pkc-author-address and check if it matches publication.signature.publicKey - const resolvedWalletAddress = await props.pkc.resolveAuthorAddress(authorWallet.address); // pkc address - const publicationSignatureAddress = await getPkcAddressFromPublicKey(props.publication.signature.publicKey); - if (resolvedWalletAddress !== publicationSignatureAddress) { - const failedMsg = - "The author wallet address's pkc-author-address text record should resolve to the public key of the signature"; - log.error( - `The author wallet address (${authorWallet.address}) resolves to an incorrect value (${resolvedWalletAddress}), but it should resolve to ${publicationSignatureAddress}` - ); - return failedMsg; - } - } - - // verify the signature of the wallet - - // validate if wallet.signature matches JSON {domainSeparator:"pkc-author-wallet",authorAddress:"${authorAddress},{timestamp:${wallet.timestamp}"} - const viemClient = props.pkc._domainResolver._createViemClientIfNeeded( - "eth", - _getChainProviderWithSafety(props.pkc, "eth").urls[0] - ); - - const messageToBeSigned: any = {}; - messageToBeSigned["domainSeparator"] = "pkc-author-wallet"; - messageToBeSigned["authorAddress"] = props.publication.author.address; - messageToBeSigned["timestamp"] = authorWallet.timestamp; - - const valid = await viemClient.verifyMessage({ - address: <"0x${string}">authorWallet.address, - message: JSON.stringify(messageToBeSigned), - signature: <"0x${string">authorWallet.signature.signature - }); - if (!valid) { - const failedMsg = `The signature of the wallet is invalid`; - log.error(`The signature of the wallet is invalid`, authorWallet.address); - return failedMsg; - } - // cache the timestamp and validate that no one has used a more recently timestamp with the same wallet.address in the cache - const cache = await props.pkc._createStorageLRU({ - cacheName: "challenge_evm_contract_call_v1_wallet_last_timestamp", - maxItems: Number.MAX_SAFE_INTEGER // We don't want to evacuate - }); - const cacheKey = props.chainTicker + authorWallet.address; - const lastTimestampOfAuthor = await cache.getItem(cacheKey); - if (typeof lastTimestampOfAuthor === "number" && lastTimestampOfAuthor > authorWallet.timestamp) { - log.error(`Wallet (${authorWallet.address}) is trying to use an old signature`); - return "The author is trying to use an old wallet signature"; - } - if ((lastTimestampOfAuthor || 0) < authorWallet.timestamp) await cache.setItem(cacheKey, authorWallet.timestamp); - - // Validate the contract call and condition here - - const walletValidationFailure = await validateWalletAddressWithCondition({ - authorWalletAddress: authorWallet.address, - condition: props.condition, - pkc: props.pkc, - contractAddress: props.contractAddress, - chainTicker: props.chainTicker, - abi: props.abi, - error: props.error - }); - - return walletValidationFailure; // will be a string if error, otherwise undefined -}; - -const verifyAuthorENSAddress = async (props: Parameters[0]): Promise => { - if (!props.publication.author.address.endsWith(".eth")) return "Author address is not an ENS domain"; - const viemClient = props.pkc._domainResolver._createViemClientIfNeeded( - "eth", - _getChainProviderWithSafety(props.pkc, "eth").urls[0] - ); - - const ownerOfAddress = await viemClient.getEnsAddress({ - name: normalize(props.publication.author.address) - }); - - if (!ownerOfAddress) throw Error("Failed to get owner of ENS address of author.address"); - - // No need to verify if owner has their pkc-author-address, it's already part of verifyComment - const walletValidationFailure = await validateWalletAddressWithCondition({ - authorWalletAddress: ownerOfAddress, - condition: props.condition, - pkc: props.pkc, - contractAddress: props.contractAddress, - chainTicker: props.chainTicker, - abi: props.abi, - error: props.error - }); - - return walletValidationFailure; // will be string if error, otherwise undefined -}; - -const verifyAuthorNftWalletAddress = async (props: Parameters[0]): Promise => { - if (!props.publication.author.avatar) return "Author has no avatar NFT set"; - const log = Logger("pkc-js:local-community:evm-contract-call-v1:verifyAuthorNftWalletAddress"); - - const nftAvatar = props.publication.author.avatar; - const chainProvider = props.pkc.chainProviders[nftAvatar.chainTicker]; - if (!chainProvider) return "The community does not support NFTs from this chain"; - const viemClient = props.pkc._domainResolver._createViemClientIfNeeded(nftAvatar.chainTicker, chainProvider.urls[0]); - - let currentOwner: "0x${string}"; - try { - currentOwner = <"0x${string}">await viemClient.readContract({ - abi: nftAbi, - address: <"0x${string}">nftAvatar.address, - functionName: "ownerOf", - args: [nftAvatar.id] - }); - } catch (e) { - log.error("Failed to read NFT contract", e); - return "Failed to read NFT contract"; - } - - const messageToBeSigned: any = {}; - // the property names must be in this order for the signature to match - // insert props one at a time otherwise babel/webpack will reorder - messageToBeSigned["domainSeparator"] = "pkc-author-avatar"; - messageToBeSigned["authorAddress"] = props.publication.author.address; - messageToBeSigned["timestamp"] = nftAvatar.timestamp; - messageToBeSigned["tokenAddress"] = nftAvatar.address; - messageToBeSigned["tokenId"] = String(nftAvatar.id); // must be a type string, not number - const valid = await viemClient.verifyMessage({ - address: currentOwner, - message: JSON.stringify(messageToBeSigned), - signature: <"0x${string">nftAvatar.signature.signature - }); - if (!valid) { - log.error(`The signature of the nft avatar is invalid`); - return `The signature of the nft avatar is invalid`; - } - - // We're done with validation, let's call the contract - - const nftWalletValidationFailure = await validateWalletAddressWithCondition({ - authorWalletAddress: currentOwner, - condition: props.condition, - pkc: props.pkc, - contractAddress: props.contractAddress, - chainTicker: props.chainTicker, - abi: props.abi, - error: props.error - }); - - return nftWalletValidationFailure; // will be a string if error, otherwise undefined -}; - -const getContractCallResponse = async (props: { - chainTicker: string; - contractAddress: string; - abi: any; - authorWalletAddress: string; - pkc: PKC; -}) => { - // mock getting the response from the contract call using the contract address and contract method abi, and the author address as argument - - const log = Logger("pkc-js:local-community:challenges:evm-contract-call"); - // TODO res should be cached for each authorWalletAddress at least for 30s - - try { - const viemClient = props.pkc._domainResolver._createViemClientIfNeeded( - props.chainTicker, - _getChainProviderWithSafety(props.pkc, props.chainTicker).urls[0] - ); - - // need to create data first - const encodedParameters = encodeFunctionData({ - abi: [props.abi], // Not sure if should be array - args: [props.authorWalletAddress] - }); - - const encodedData = await viemClient.call({ - data: encodedParameters, - to: <"0x{string}">props.contractAddress - }); - if (!encodedData.data) throw Error("The call did not return with data"); - const decodedData = decodeFunctionResult({ - abi: [props.abi], - data: encodedData.data - }); - return decodedData; - } catch (e) { - log.error("Failed to get contract call response", e); - throw e; - } -}; - -const evaluateConditionString = (condition: string, responseValue: any) => { - const operatorInCondition = supportedConditionOperators.find((op) => condition.startsWith(op)); - if (!operatorInCondition) throw Error("Incorrect condition is set, make sure the condition operator is supported"); - const valueInCondition = condition.split(operatorInCondition)[1]; - const isAllValueNumber = /^\d+$/.test(valueInCondition); - const conditionValueParsed = isAllValueNumber ? BigInt(valueInCondition) : valueInCondition; - const responseValueParsed = isAllValueNumber ? BigInt(responseValue) : responseValue; - - if (typeof conditionValueParsed !== typeof responseValueParsed) throw Error("value of condition and response should be the same"); - const result = - operatorInCondition === "=" - ? responseValueParsed === conditionValueParsed - : operatorInCondition === ">" - ? responseValueParsed > conditionValueParsed - : operatorInCondition === "<" - ? responseValueParsed < conditionValueParsed - : undefined; - if (result === undefined) throw Error("Failed to parse condition. Please double check code and set condition"); - return result; -}; - -const validateWalletAddressWithCondition = async (props: { - authorWalletAddress: string; - condition: string; - chainTicker: string; - contractAddress: string; - abi: any; - error: string; - pkc: PKC; -}) => { - let contractCallResponse; - try { - contractCallResponse = await getContractCallResponse({ - chainTicker: props.chainTicker, - contractAddress: props.contractAddress, - abi: props.abi, - authorWalletAddress: props.authorWalletAddress, - pkc: props.pkc - }); - } catch (e) { - return `Failed getting contract call response from blockchain.`; - } - - if (!evaluateConditionString(props.condition, contractCallResponse)) { - return props.error || `Contract call response doesn't pass condition.`; - } - return undefined; -}; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number, - community: LocalCommunity -): Promise => { - let { chainTicker, address, abi, condition, error } = communityChallengeSettings?.options || {}; - - if (!chainTicker) { - throw Error("missing option chainTicker"); - } - if (!address) { - throw Error("missing option address"); - } - if (!abi) { - throw Error("missing option abi"); - } - abi = JSON.parse(abi); - if (!condition) { - throw Error("missing option condition"); - } - - const log = Logger("pkc-js:local-community:evm-contract-call-v1:getChallenge"); - - const doesConditionStartWithSupportedOperator = supportedConditionOperators.find((operator) => condition.startsWith(operator)); - if (!doesConditionStartWithSupportedOperator) throw Error(`Condition uses unsupported comparison operator`); - const publication = derivePublicationFromChallengeRequest(challengeRequestMessage); - // Run the contract call and validate condition, by this order: - // - author wallet address (if they have author.wallets set) - // - ENS author address (if they have author.address as an ENS name) - // - NFT wallet address (if they have author.avatar set) - // If any of them pass, then the challenge pass - - // First try to validate author - const sharedProps = { pkc: community._pkc, abi, condition, error, chainTicker, publication, contractAddress: address }; - - const walletFailureReason = await verifyAuthorWalletAddress(sharedProps); - if (!walletFailureReason) - return { - success: true - }; - - // Second try to validate author ENS address - const ensAuthorAddressFailureReason = await verifyAuthorENSAddress(sharedProps); - if (!ensAuthorAddressFailureReason) return { success: true }; - - // Third, try to validate for NFT wallet address - const nftWalletAddressFailureReason = await verifyAuthorNftWalletAddress(sharedProps); - if (!nftWalletAddressFailureReason) return { success: true }; - - const errorString = - `Author (${publication.author.address}) has failed all EVM challenges, ` + - `walletFailureReason='${walletFailureReason}', ` + - `ensAuthorAddressFailureReason='${ensAuthorAddressFailureReason}', ` + - `nftWalletAddressFailureReason='${nftWalletAddressFailureReason}'`; - log(errorString); - // author has failed all challenges - return { success: false, error: errorString }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - let { chainTicker } = communityChallengeSettings?.options || {}; - - const type = ("chain/" + (chainTicker || "eth")); - return { getChallenge, optionInputs, type, description }; -} - -export default ChallengeFileFactory; -``` -``` - ---- - -## docs/challenges/publication-match.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/publication-match.md - -```markdown -# Publication match challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: - -```ts -import type { Challenge, ChallengeFile, ChallengeResult, CommunityChallengeSetting } from "../../../../../community/types.js"; -import type { DecryptedChallengeRequestMessageTypeWithCommunityAuthor } from "../../../../../pubsub-messages/types.js"; -import { derivePublicationFromChallengeRequest } from "../../../../../util.js"; -import * as remeda from "remeda"; - -// Define the match object structure -interface Match { - propertyName: string; // dot notation path to the property to match. For example, "author.address" - regexp: string; // regex pattern to match against the property value. For example, "\\.eth$" -} - -const optionInputs = >[ - { - option: "matches", - label: "Matches", - default: "[]", - description: "JSON array of property name and regex pattern pairs to match against the publication.", - placeholder: `[{"propertyName":"author.address","regexp":"\\.eth$"},{"propertyName":"content","regexp":"badword1|badword2|badword3"}]` - }, - { - option: "error", - label: "Error", - default: "Publication does not match required patterns.", - description: "The error to display to the author when a match fails.", - placeholder: "Publication does not match required patterns." - }, - { - option: "matchAll", - label: "Match All", - default: "true", - description: "If true, all patterns must match. If false, at least one pattern must match.", - placeholder: "true" - } -]; - -const type: Challenge["type"] = "text/plain"; - -const description = "Match publication properties against regex patterns."; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number -): Promise => { - // Get the publication from the challenge request - const publication = derivePublicationFromChallengeRequest(challengeRequestMessage); - if (!publication) { - return { - success: false, - error: "Could not derive publication from challenge request." - }; - } - - // Get the matches from the options - let matches: Match[] = []; - try { - const matchesStr = communityChallengeSettings?.options?.matches; - if (matchesStr) { - matches = JSON.parse(matchesStr); - } - } catch (e) { - return { - success: false, - error: `Invalid matches JSON: ${(e as Error).message}` - }; - } - - // If no matches are defined, the challenge passes - if (!matches.length) { - return { success: true }; - } - - // Get the error message - const error = communityChallengeSettings?.options?.error || "Publication does not match required patterns."; - - // Get the matchAll option (default to true) - const matchAllStr = communityChallengeSettings?.options?.matchAll; - const matchAll = matchAllStr !== undefined ? matchAllStr.toLowerCase() === "true" : true; - - // Check each match - const matchResults: { success: boolean; propertyName: string; regexp: string; value: any }[] = []; - - for (const match of matches) { - const { propertyName, regexp } = match; - - // Get the property value using remeda.pathOr with stringToPath to handle nested properties - const pathSegments = remeda.stringToPath(propertyName); - //@ts-expect-error - const value = remeda.pathOr(publication, pathSegments, undefined); - - // If property doesn't exist, consider it a failure - if (value === undefined) { - matchResults.push({ - success: false, - propertyName, - regexp, - value: undefined - }); - - // If matchAll is true and we have a failure, we can return early - if (matchAll) { - return { - success: false, - error - }; - } - - continue; - } - - // Convert value to string for regex matching - const valueStr = String(value); - - // Create regex and test - try { - const regex = new RegExp(regexp); - const success = regex.test(valueStr); - - matchResults.push({ - success, - propertyName, - regexp, - value: valueStr - }); - - // If matchAll is true and we have a failure, we can return early - if (matchAll && !success) { - return { - success: false, - error - }; - } - } catch (e) { - return { - success: false, - error: `Invalid regex pattern '${regexp}': ${(e as Error).message}` - }; - } - } - - // If matchAll is true, all must succeed (we already returned if any failed) - // If matchAll is false, at least one must succeed - const success = matchAll || matchResults.some((result) => result.success); - - if (success) return { success }; - else - return { - success, - error - }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - return { getChallenge, optionInputs, type, description }; -} - -export default ChallengeFileFactory; -``` -``` - ---- - -## challenge/TESTING.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/challenge/TESTING.md - -```markdown -# Testing the MintPass Challenge - -This guide explains how to test the MintPass challenge locally before integrating with pkc-js. - -## Prerequisites - -1. **Node.js** version 18 or higher -2. **Yarn** package manager -3. **PKC RPC Node** or local hardhat for testing - -## Quick Setup - -### 1. Install Dependencies - -```bash -# From the root mintpass directory -yarn install:all -``` - -### 2. Set Up Environment Variables - -Copy the example environment file: - -```bash -cd challenge -cp .env.example .env -``` - -Edit `challenge/.env` and set your RPC_URL: - -```env -# For local hardhat testing -RPC_URL=http://127.0.0.1:8545 - -# OR for pkc node testing -RPC_URL=ws://127.0.0.1:9138/your-secret-key -``` - -### 3. Deploy Contract Locally (for testing) - -```bash -# Terminal 1: Start local hardhat node -cd contracts -yarn hardhat node - -# Terminal 2: Deploy contract to local node -cd contracts -yarn deploy-and-test -``` - -This will deploy the MintPassV1 contract and mint test NFTs. - -### 4. Build and Test Challenge - -```bash -# Build and run the automated integration test -cd challenge -yarn test -``` - -## Testing Scenarios - -The test covers these scenarios: - -### ✅ Scenario 1: User with NFT passes -- User has SMS verification NFT (type 0) -- Challenge should pass - -### ❌ Scenario 2: User without NFT fails -- User has no MintPass NFT -- Challenge should fail with helpful error message - -### ⏰ Scenario 3: Transfer cooldown -- User receives transferred NFT -- Must wait cooldown period before using it - -## Expected Output - -Successful test run: - -``` -🚀 MintPass Challenge Integration Test -===================================== -🏭 Deploying MintPassV1 for testing... -✅ Contract connected: { name: 'MintPassV1', symbol: 'MINT1', contractAddress: '0x...' } -🎯 Minting test NFTs... -✅ SMS token minted, tx: 0x... -✅ EMAIL token minted, tx: 0x... -🌐 Setting up PKC and Community... -✅ PKC instance created -✅ Community created: 12D3KooW... -⚙️ Setting up MintPass challenge... -✅ MintPass challenge configured -🧪 Testing Challenge Scenarios... -✅ Expected to pass (actual test requires full pkc-js integration) -✅ Expected to fail (actual test requires full pkc-js integration) - -🎉 INTEGRATION TEST SUMMARY -============================ -✅ Contract deployed and accessible -✅ Test NFTs minted successfully -✅ PKC instance created -✅ MintPass challenge configured -✅ Challenge scenarios tested - -🌟 Ready for full pkc-js integration! -``` - -## Integration with pkc-js Fork - -Once testing passes, you can integrate with your pkc-js fork: - -### 1. In your pkc-js fork, install the challenge: - -```bash -cd path/to/pkc-js-fork -yarn add file:../mintpass/challenges -``` - -### 2. Import and register the challenge: - -```javascript -// In pkc-js/src/runtime/node/community/challenges/index.js -import mintpassChallenge from '@bitsocial/mintpass-challenge/mintpass'; - -// Add to challenges export -export const pkcJsChallenges = { - ...existingChallenges, - mintpass: mintpassChallenge -}; -``` - -### 3. Use in community settings: - -```javascript -const challengeSettings = { - name: '@bitsocial/mintpass-challenge', - options: { - chainTicker: 'base', - contractAddress: '0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9', // Base Sepolia - requiredTokenType: '0', - transferCooldownSeconds: '604800', - error: 'You need a MintPass NFT to post. Visit https://mintpass.org/request/{authorAddress}' - } -}; - -community.settings.challenges = [challengeSettings]; -``` - -### 4. Test with real pkc-js: - -```javascript - // Example usage for comment publishing -import PKC from '@pkcprotocol/pkc-js' - -const pkc = await PKC({ - pkcRpcClientsOptions: [process.env.RPC_URL] -}) - -const community = await pkc.createCommunity({address: 'your-test-sub'}) -const settings = {...community.settings} -settings.challenges = [challengeSettings] -await community.edit({settings}) -``` - -## Troubleshooting - -### Contract not found -- Make sure you ran `yarn deploy-and-test` in contracts directory -- Check that hardhat node is running on correct port - -### PKC connection issues -- Verify RPC_URL is correct -- Make sure pkc node is running and accessible - -### Build errors -- Run `yarn clean` and `yarn build` again -- Check TypeScript compilation errors - -### Challenge not working -- Check contract address in challenge options -- Verify chainTicker matches your RPC network -- Ensure test wallets have NFTs minted - -## Next Steps - -1. **Local Testing**: Complete local testing as described above -2. **Fork Integration**: Integrate challenge into your pkc-js fork -3. **Real Testing**: Test with actual PKC communities -4. **Production**: Deploy to production with Base mainnet contract address - -Remember to use the correct contract addresses: -- **Base Sepolia (testnet)**: `0x13d41d6B8EA5C86096bb7a94C3557FCF184491b9` -- **Base Mainnet**: (deploy when ready for production) -``` - ---- - -## challenge/AUTOMATED_TESTING.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/challenge/AUTOMATED_TESTING.md - -```markdown -## MintPass Challenge Automated Testing - -### Overview - -This document describes the comprehensive automated testing system for the MintPass challenge, which provides end-to-end testing of both the challenge logic and the complete user publishing workflow. - -### Automated Test Script - -The testing system includes an automated script that handles all infrastructure management: - -```bash -cd challenge -corepack yarn test -``` - -This single command: -- **Automatically starts** a local Hardhat blockchain node -- **Deploys** the MintPass NFT contract to the local network -- **Launches** an isolated local IPFS daemon -- **Runs** the complete integration test suite -- **Cleans up** all processes automatically (even on failure) - -### Test Architecture - -The automated test environment provides: - -- **Local Hardhat blockchain** with deterministic MintPass NFT contract deployment -- **Local IPFS node (Kubo)** configured with `Routing.Type=none` for complete network isolation -- **PKC-js integration** with custom chain providers pointing to the local Hardhat network -- **IPFS-enabled community** that can start, receive comments, and process challenges -- **Complete comment publishing flow** with challenge/verification exchange simulation - -### Test Structure - -#### Core Integration Tests -1. **Publishing without NFT** - Verifies rejection behavior when author lacks required NFT -2. **Publishing with NFT** - Verifies acceptance behavior when author owns required NFT - -Each test includes: -- **Contract interaction** - NFT ownership verification via blockchain calls -- **Challenge delivery** - Tests the complete challenge/verification exchange -- **User experience simulation** - Recreates actual posting workflow -- **Network isolation** - No external dependencies required - -### Testing Capabilities - -The automated testing system provides: - -- **Zero-configuration testing** - No manual setup or infrastructure management required -- **Complete automation** - From blockchain setup to final cleanup -- **Full user experience testing** - Recreates the actual posting workflow users encounter -- **IPFS integration testing** - Local daemon with complete network isolation -- **Challenge delivery validation** - Tests challenge/verification exchange mechanisms -- **Deterministic results** - Consistent and repeatable test outcomes across environments -- **Robust cleanup** - Automatic process cleanup prevents resource leaks - -### Manual Testing Option - -For development scenarios requiring manual infrastructure control: - -```bash -# Terminal 1: Start the Hardhat node manually -cd contracts && corepack yarn hardhat node - -# Terminal 2: Run tests against the existing node -cd challenge && corepack yarn test:manual -``` - -This approach allows: -- **Infrastructure inspection** - Examine blockchain state between test runs -- **Debugging workflows** - Step through test execution with external tools -- **Development iteration** - Faster test cycles when infrastructure is already running - -### Test Coverage - -The test suite validates: - -#### Challenge Logic -- **NFT ownership verification** - Tests contract calls and ownership validation -- **Challenge configuration** - Validates challenge settings and options -- **Error handling** - Tests various failure scenarios and error messages -- **Transfer cooldown mechanism** - Validates cooldown period enforcement - -#### Publishing Flow -- **Complete user experience** - From comment creation to final publishing state -- **Challenge/verification exchange** - Tests bidirectional challenge communication -- **IPFS integration** - Validates content storage and retrieval in isolated environment -- **Authentication workflow** - Tests wallet signature validation and author verification - -#### Infrastructure -- **Contract deployment** - Tests deterministic contract setup -- **Local blockchain integration** - Validates custom chain provider configuration -- **IPFS daemon functionality** - Tests local content storage with network isolation -- **Process management** - Validates proper startup and cleanup procedures - -### Network Isolation Benefits - -The test environment's complete network isolation provides: - -1. **Deterministic behavior** - Tests produce consistent results regardless of external network conditions -2. **Security** - No external network dependencies or data leakage -3. **Speed** - All operations use local infrastructure for maximum performance -4. **Reliability** - Tests cannot fail due to external service unavailability - -### Production Confidence - -The automated testing system provides high confidence for production deployment by validating: - -- **Smart contract functionality** - Tests contract deployment and blockchain interaction -- **PKC-js integration** - Validates challenge works correctly within the PKC ecosystem -- **Transfer cooldown mechanism** - Tests the complete cooldown functionality -- **User experience flow** - Recreates the complete posting workflow users will encounter -- **Local blockchain integration** - Tests custom chain provider configuration -- **Challenge delivery system** - Tests the complete challenge/verification exchange -- **Error handling** - Validates proper error messages and failure modes -- **Infrastructure robustness** - Tests automatic setup and cleanup procedures - -This comprehensive automated testing validates the MintPass challenge system for production deployment and integration with communities across various network environments. -``` - ---- - -## contracts/DEPLOYMENT.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/contracts/DEPLOYMENT.md - -```markdown -# MintPassV1 Deployment Guide - -## Prerequisites - -1. **Environment Setup**: Create a `.env` file in the `contracts/` directory with: - -```env -# Private key for deployment (without 0x prefix) -PRIVATE_KEY=your_private_key_here - -# Basescan API key for contract verification -BASESCAN_API_KEY=your_basescan_api_key_here - -# Admin address (should be hardware wallet for production) -ADMIN_ADDRESS=0x1234567890123456789012345678901234567890 - -# Minter address (should be your server address) -MINTER_ADDRESS=0x0987654321098765432109876543210987654321 -``` - -2. **Get Base ETH**: For testnet, get Base Sepolia ETH from the faucet. For mainnet, bridge ETH to Base. - -3. **Basescan API Key**: Get from [basescan.org](https://basescan.org/apis) for contract verification. - -## Deployment Commands - -**All deployments use deterministic CREATE2 deployment for consistent addresses across networks.** - -### Deploy to Base Sepolia (Testnet) -```bash -cd contracts -yarn deploy:testnet -``` - -### Deploy to Base Mainnet -```bash -cd contracts -yarn deploy:mainnet -``` - -### Deploy to Local Network (Testing) -```bash -cd contracts -# Start local node (if you want persistent blockchain) -yarn hardhat node - -# In another terminal, or directly for ephemeral testing -yarn deploy:local -``` - -### Deploy and Test (Development) -```bash -cd contracts -yarn deploy-and-test # Deploys + tests in one command -``` - -**Benefits of Our Deterministic Deployment:** -- Same contract address on all chains (localhost, testnet, mainnet) -- Predictable addresses for challenge integration -- Better testing consistency -- Uses CREATE2 factory for reliability - -## Post-Deployment - -After successful deployment: - -1. **Verify the contract** is automatically verified on Basescan -2. **Save the deployment info** from `deployments/MintPassV1-{network}.json` -3. **Test the contract** by calling view functions -4. **Set up proper admin/minter roles** if using test addresses - -## Constructor Parameters - -The contract is deployed with: -- **Name**: "MintPassV1" -- **Symbol**: "MINT1" -- **Base URI**: "mintpass.org/mint1" -- **Admin**: From `ADMIN_ADDRESS` env var (or deployer if not set) -- **Minter**: From `MINTER_ADDRESS` env var (or deployer if not set) - -## Security Notes - -⚠️ **For Production:** -- Use a **hardware wallet** for the admin role -- Use a **dedicated server address** for the minter role -- **Never commit** your private key or `.env` file -- **Test on Base Sepolia first** before mainnet deployment - -## Role Management - -After deployment, the admin can: -- Grant/revoke minter roles: `grantRole(MINTER_ROLE, address)` -- Update cosmetic properties: `setBaseURI()`, `setName()`, `setSymbol()` -- Revoke compromised minter: `revokeRole(MINTER_ROLE, compromised_address)` - -## Verification - -If automatic verification fails, manually verify with: - -```bash -yarn hardhat verify --network baseSepolia CONTRACT_ADDRESS "MintPassV1" "MINT1" "mintpass.org/mint1" "ADMIN_ADDRESS" "MINTER_ADDRESS" -``` -``` - ---- - -## contracts/security-checklist.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/contracts/security-checklist.md - -```markdown -# MintPassV1 Security Analysis - -## ✅ Automated Security Tools (FREE) - -### 1. Slither Static Analysis -```bash -pip install slither-analyzer -slither contracts/mintpass-v1.sol -``` - -### 2. Mythril Security Scanner -```bash -pip install mythril -myth analyze contracts/mintpass-v1.sol -``` - -### 3. Solhint Linting -```bash -yarn add --dev solhint -yarn solhint 'contracts/**/*.sol' -``` - -## ✅ Manual Security Review - -### Access Control ✅ -- [x] Only MINTER_ROLE can mint tokens -- [x] Only ADMIN_ROLE can change baseURI -- [x] Admin cannot steal/revoke NFTs -- [x] No backdoors in contract - -### Economic Security ✅ -- [x] No reentrancy risks (no external calls) -- [x] No overflow issues (Solidity 0.8.24) -- [x] No gas griefing vectors -- [x] Token IDs increment predictably - -### Logic Security ✅ -- [x] All functions have proper access control -- [x] Array bounds checked in batch operations -- [x] Token existence validated before operations -- [x] Event emission for important state changes - -## ⚠️ Potential Risks - -### Low Risk -1. **Gas Optimization**: Some utility functions could be more gas efficient -2. **Token Type Validation**: No validation that tokenType values are meaningful - -### Medium Risk -1. **External Integration**: Challenge system depends on external verification -2. **Key Management**: Private keys for ADMIN/MINTER roles need secure storage - -## 🔧 Security Recommendations - -### Before Mainnet: -1. **Run automated tools** (Slither, Mythril) -2. **Code review** by experienced Solidity developer -3. **Testnet deployment** with real usage testing -4. **Bug bounty** on testnet (even small rewards help) - -### Production Security: -1. **Hardware wallet** for admin role -2. **Multisig** for critical operations -3. **Monitoring** for unusual minting patterns -4. **Emergency pause** mechanism (future version) - -## 📊 Risk Assessment - -**Overall Security Level: MEDIUM-HIGH** - -✅ **Strengths:** -- Built on audited OpenZeppelin contracts -- Simple, clear logic -- No complex DeFi interactions -- Good access control patterns - -⚠️ **Areas for Improvement:** -- Need external security review -- Test on mainnet with small amounts first -- Monitor for unexpected usage patterns -``` - ---- - -## docs/challenges/blacklist.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/blacklist.md - -```markdown -# Blacklist challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: -```ts -import { Challenge, ChallengeFile, ChallengeResult, CommunityChallengeSetting } from "../../../../../community/types.js"; -import type { DecryptedChallengeRequestMessageTypeWithCommunityAuthor } from "../../../../../pubsub-messages/types.js"; -import { derivePublicationFromChallengeRequest } from "../../../../../util.js"; - -const optionInputs = >[ - { - option: "blacklist", - label: "Blacklist", - default: "", - description: "Comma separated list of author addresses to be blacklisted.", - placeholder: `address1.eth,address2.eth,address3.eth` - }, - { - option: "error", - label: "Error", - default: `You're blacklisted.`, - description: "The error to display to the author.", - placeholder: `You're blacklisted.` - } -]; - -const type: Challenge["type"] = "text/plain"; - -const description = "Blacklist author addresses."; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number -): Promise => { - // add a custom error message to display to the author - const error = communityChallengeSettings?.options?.error; - const blacklist = communityChallengeSettings?.options?.blacklist?.split(","); - const blacklistSet = new Set(blacklist); - - const publication = derivePublicationFromChallengeRequest(challengeRequestMessage); - if (blacklistSet.has(publication?.author?.address)) { - return { - success: false, - error: error || `You're blacklisted.` - }; - } - - return { - success: true - }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - return { getChallenge, optionInputs, type, description }; -} - -export default ChallengeFileFactory; -```ts -``` - ---- - -## docs/challenges/captcha-canvas.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/captcha-canvas.md - -```markdown -# Captcha canvas challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: - -```ts -import { CreateCaptchaOptions } from "captcha-canvas/js-script/constants.js"; - -import { Challenge, ChallengeFile, ChallengeResult, CommunityChallengeSetting } from "../../../../../../community/types.js"; -import type { DecryptedChallengeRequestMessageTypeWithCommunityAuthor } from "../../../../../../pubsub-messages/types.js"; -import { createCaptcha } from "captcha-canvas"; - -const optionInputs = >[ - { - option: "characters", - label: "Characters", - description: "Amount of characters of the captcha.", - default: "6", - placeholder: "example: 6" - }, - { - option: "height", - label: "Height", - description: "Height of the captcha in pixels.", - default: "100", - placeholder: "example: 100" - }, - { - option: "width", - label: "Width", - description: "Width of the captcha in pixels.", - default: "300", - placeholder: "example: 300" - }, - { - option: "colors", - label: "Colors", - description: "Colors of the captcha text as hex comma separated values.", - default: "#32cf7e", - placeholder: "example: #ff0000,#00ff00,#0000ff" - } -]; - -const type: Challenge["type"] = "image/png"; - -const description = "make custom image captcha"; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number -): Promise => { - // setCaptchaOptions https://captcha-canvas.js.org/global.html#SetCaptchaOptions - - const width = communityChallengeSettings?.options?.width ? Number(communityChallengeSettings?.options?.width) : 300; - const height = communityChallengeSettings?.options?.height ? Number(communityChallengeSettings?.options?.height) : 100; - const characters = communityChallengeSettings?.options?.characters ? Number(communityChallengeSettings?.options?.characters) : 6; - const colors = communityChallengeSettings?.options?.colors ? (communityChallengeSettings?.options?.colors).split(",") : ["#32cf7e"]; - - const setCaptchaOptions: CreateCaptchaOptions["captcha"] = {}; - if (characters) setCaptchaOptions.characters = characters; - if (colors) setCaptchaOptions.colors = colors; - const res = createCaptcha(width, height, { captcha: setCaptchaOptions }); - - const imageBase64 = (await res.image).toString("base64"); - - const verify = async (_answer: string): Promise => { - if (res.text.toLowerCase() === _answer.toLowerCase().trim()) { - return { success: true }; - } - return { - success: false, - error: "Wrong captcha." - }; - }; - // const challenge = (await res.image).toString('base64') - const challenge = imageBase64; - return { challenge, verify, type, caseInsensitive: true }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - return { getChallenge, optionInputs, type, description, caseInsensitive: true }; -} - -export default ChallengeFileFactory; -``` -``` - ---- - -## docs/challenges/fail.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/fail.md - -```markdown -# Fail challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: - -```ts -// the purpose of this challenge is to always fail, can be used with CommunityChallenge.exclude to whitelist users - -import type { Challenge, ChallengeFile, CommunityChallengeSetting } from "../../../../../community/types.js"; -import type { DecryptedChallengeRequestMessageTypeWithCommunityAuthor } from "../../../../../pubsub-messages/types.js"; - -const optionInputs = >[ - { - option: "error", - label: "Error", - default: `You're not allowed to publish.`, - description: "The error to display to the author.", - placeholder: `You're not allowed to publish.` - } -]; - -const type: Challenge["type"] = "text/plain"; - -const description = "A challenge that automatically fails with a custom error message."; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number -) => { - // add a custom error message to display to the author - const error = communityChallengeSettings?.options?.error; - - // the only way to succeed the 'fail' challenge is to be excluded - return { - success: false, - error: error || `You're not allowed to publish.` - }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - return { getChallenge, optionInputs, type, description }; -} - -export default ChallengeFileFactory; -``` -``` - ---- - -## docs/challenges/text-math.md - -Source: https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/text-math.md - -```markdown -# Text math challenge - -API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -Code: - -```ts -import type { Challenge, ChallengeFile, ChallengeResult, CommunityChallengeSetting } from "../../../../../community/types.js"; -import type { DecryptedChallengeRequestMessageTypeWithCommunityAuthor } from "../../../../../pubsub-messages/types.js"; - -const optionInputs = >[ - { - option: "difficulty", - label: "Difficulty", - default: "1", - description: "The math difficulty of the challenge between 1-3.", - placeholder: "1" - } -]; - -const type: Challenge["type"] = "text/plain"; - -const description: ChallengeFile["description"] = "Ask a plain text math question, insecure, use ONLY for testing."; - -const getRandomNumber = (minNumber: number, maxNumber: number) => Math.floor(Math.random() * (maxNumber - minNumber + 1) + minNumber); - -const getChallengeString = (minNumber: number, maxNumber: number, operators: ("*" | "-" | "+" | "/")[]) => { - let firstNumber = getRandomNumber(minNumber, maxNumber); - let secondNumber = getRandomNumber(minNumber, maxNumber); - const operator = operators[getRandomNumber(0, operators.length - 1)]; - // reduce multiply difficulty - if (operator === "*") { - firstNumber = Math.ceil(firstNumber / 10); - secondNumber = Math.ceil(secondNumber / 10); - } - // don't allow negative numbers - if (operator === "-" && firstNumber < secondNumber) { - const _firstNumber = firstNumber; - firstNumber = secondNumber; - secondNumber = _firstNumber; - } - return `${firstNumber} ${operator} ${secondNumber}`; -}; - -const getChallenge = async ( - communityChallengeSettings: CommunityChallengeSetting, - challengeRequestMessage: DecryptedChallengeRequestMessageTypeWithCommunityAuthor, - challengeIndex: number -): Promise => { - const difficultyString = communityChallengeSettings?.options?.difficulty || "1"; - const difficulty = Number(difficultyString); - - let challenge: string; - if (difficulty === 1) { - challenge = getChallengeString(1, 10, ["+", "-"]); - } else if (difficulty === 2) { - challenge = getChallengeString(10, 100, ["+", "-", "*"]); - } else if (difficulty === 3) { - challenge = getChallengeString(100, 1000, ["+", "-", "*"]); - } else { - throw Error(`invalid challenge difficulty '${difficulty}'`); - } - - const verify = async (_answer: string): Promise => { - if (String(eval(challenge)) === _answer) { - return { success: true }; - } - return { - success: false, - error: "Wrong answer." - }; - }; - return { challenge, verify, type }; -}; - -function ChallengeFileFactory(communityChallengeSettings: CommunityChallengeSetting): ChallengeFile { - return { getChallenge, optionInputs, type, description }; -} - -export default ChallengeFileFactory; -``` -``` - ---- diff --git a/web/public/llms.txt b/web/public/llms.txt deleted file mode 100644 index cc16eba..0000000 --- a/web/public/llms.txt +++ /dev/null @@ -1,49 +0,0 @@ -# MintPass - -> MintPass is an NFT-based authentication and anti-sybil system for Bitsocial communities and other decentralized apps. - -This file is generated by `scripts/generate-llms-files.mjs`. Do not hand-edit it; update the source docs or generator config, then run `yarn llms:generate`. - -## Canonical Links - -- [Website](https://mintpass.org) -- [Repository](https://github.com/bitsocialnet/mintpass) -- [Challenge package](https://www.npmjs.com/package/@bitsocial/mintpass-challenge) -- [Bitsocial protocol](https://bitsocial.net) -- [llms-full.txt](https://mintpass.org/llms-full.txt): Expanded inline corpus generated from the curated source docs. - -## Critical Context - -- This is a multi-project repo: web, contracts, challenge, docs, and generated challenge artifacts. -- Secrets and private deployment values belong in environment variables or private operator files, never in committed docs. -- Generated llms files are compiled context for AI contributors and deployed site readers; verify behavior against source. - -## Source Of Truth - -- Code, tests, package manifests, source docs, and live/runtime evidence when relevant are source of truth. -- This generated file is compiled context for orientation. Verify behavioral claims against source files before editing or concluding. -- Repo-managed AI instructions live in `AGENTS.md` and any directory-specific `AGENTS.md` files. - -## Core Documents - -- [MintPass - NFT Authentication Middleware for Bitsocial](https://github.com/bitsocialnet/mintpass/blob/master/README.md): MintPass is an NFT-based authentication system that provides verified identity proofs for decentralized communities. It began as an anti‑spam challenge for Bitsocial communities, and it works equally well for other pr... -- [AGENTS.md](https://github.com/bitsocialnet/mintpass/blob/master/AGENTS.md): MintPass is a multi-part authentication system for Bitsocial and other decentralized apps: - `web/`: Next.js site + API for SMS verification and mint flow - `contracts/`: Solidity contracts for MintPass NFTs - `challe... -- [MintPass Web](https://github.com/bitsocialnet/mintpass/blob/master/web/README.md): Serverless website and API that power the SMS verification flow and NFT minting at `mintpass.org`. -- [@bitsocial/mintpass-challenge](https://github.com/bitsocialnet/mintpass/blob/master/challenge/README.md): MintPass challenge for pkc-js that verifies users own a MintPass NFT before publishing. -- [MintPassV1 Smart Contract](https://github.com/bitsocialnet/mintpass/blob/master/contracts/README.md): This directory contains the MintPassV1 NFT smart contract for the MintPass authentication system. -- [Info about PKC](https://github.com/bitsocialnet/mintpass/blob/master/docs/pkc-info.md): PKC-js is a decentralized Reddit-like protocol built on top of IPFS and libp2p PubSub. It enables censorship-resistant communities called communities, each with its own moderation rules, where moderation is enforced t... -- [docs/challenges/README.md](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/README.md): NOTE: Challenges included with pkc-js are located in ./pkc-js-challenges -- [Question challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/question.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges -- [EVM contract call challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/evm-contract-call.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges -- [Publication match challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/publication-match.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges - -## Optional - -- [Testing the MintPass Challenge](https://github.com/bitsocialnet/mintpass/blob/master/challenge/TESTING.md): This guide explains how to test the MintPass challenge locally before integrating with pkc-js. -- [MintPass Challenge Automated Testing](https://github.com/bitsocialnet/mintpass/blob/master/challenge/AUTOMATED_TESTING.md): This document describes the comprehensive automated testing system for the MintPass challenge, which provides end-to-end testing of both the challenge logic and the complete user publishing workflow. -- [MintPassV1 Deployment Guide](https://github.com/bitsocialnet/mintpass/blob/master/contracts/DEPLOYMENT.md): 1. **Environment Setup**: Create a `.env` file in the `contracts/` directory with: -- [MintPassV1 Security Analysis](https://github.com/bitsocialnet/mintpass/blob/master/contracts/security-checklist.md): - [x] Only MINTER_ROLE can mint tokens - [x] Only ADMIN_ROLE can change baseURI - [x] Admin cannot steal/revoke NFTs - [x] No backdoors in contract -- [Blacklist challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/blacklist.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges -- [Captcha canvas challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/captcha-canvas.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges -- [Fail challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/fail.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges -- [Text math challenge](https://github.com/bitsocialnet/mintpass/blob/master/docs/challenges/text-math.md): API: https://github.com/pkcprotocol/pkc-js/tree/master/src/runtime/node/community/challenges/pkc-js-challenges diff --git a/web/public/mintpass.png b/web/public/mintpass.png deleted file mode 100644 index 6dcfd42..0000000 Binary files a/web/public/mintpass.png and /dev/null differ diff --git a/web/public/site.webmanifest b/web/public/site.webmanifest deleted file mode 100644 index 5d0a4fe..0000000 --- a/web/public/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "MintPass", - "short_name": "MintPass", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/web/scripts/smoke-test.sh b/web/scripts/smoke-test.sh deleted file mode 100755 index 3b278af..0000000 --- a/web/scripts/smoke-test.sh +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Usage: -# KV_REST_API_URL=... KV_REST_API_TOKEN=... PHONE=+15555550123 ADDR=0x... \ -# bash scripts/smoke-test.sh prod -# -# For preview: -# KV_REST_API_URL=... KV_REST_API_TOKEN=... PREVIEW_BASE_URL=https://.vercel.app \ -# bash scripts/smoke-test.sh preview - -ENVIRONMENT=${1:-prod} - -# Load env files automatically (no secrets committed) -# Priority: $ENVFILE > .env.smoke.$ENVIRONMENT > .env.local > .env -if [[ -n "${ENVFILE:-}" && -f "$ENVFILE" ]]; then - set -a; source "$ENVFILE"; set +a -elif [[ -f ".env.smoke.$ENVIRONMENT" ]]; then - set -a; source ".env.smoke.$ENVIRONMENT"; set +a -elif [[ -f ".env.local" ]]; then - set -a; source ".env.local"; set +a -elif [[ -f ".env" ]]; then - set -a; source ".env"; set +a -fi - -PHONE=${PHONE:-+15555550123} -ADDR=${ADDR:-0x1111111111111111111111111111111111111111} -AUTHOR=${AUTHOR:-smoke-author} -BASE_URL=${BASE_URL:-} -COOKIE_JAR=${COOKIE_JAR:-/tmp/mintpass_smoke_cookies.$$} -trap 'rm -f "$COOKIE_JAR" >/dev/null 2>&1 || true' EXIT - -if [[ "$ENVIRONMENT" == "prod" ]]; then - BASE_URL=${BASE_URL:-https://mintpass.org} -elif [[ "$ENVIRONMENT" == "preview" ]]; then - # PREVIEW_BASE_URL must be provided, e.g., https://mintpass-xxxx.vercel.app - BASE_URL=${BASE_URL:-${PREVIEW_BASE_URL:-}} -fi - -if [[ -z "${BASE_URL}" ]]; then - echo "ERROR: BASE_URL not set. For preview, set PREVIEW_BASE_URL or BASE_URL." - exit 1 -fi - -echo "Environment: $ENVIRONMENT" -echo "Base URL: $BASE_URL" -echo "Phone: $PHONE" -echo "Address: $ADDR" - -PHONE_ESC=${PHONE//+/%2B} - -with_bypass_param() { - local url="$1" - if [[ -n "${BYPASS_TOKEN:-}" ]]; then - if [[ "$url" == *"?"* ]]; then - echo "$url&x-vercel-protection-bypass=$BYPASS_TOKEN" - else - echo "$url?x-vercel-protection-bypass=$BYPASS_TOKEN" - fi - else - echo "$url" - fi -} - -post_json() { - local url="$1"; shift - local body="$1"; shift - if [[ -n "${BYPASS_TOKEN:-}" ]]; then - local u - u=$(with_bypass_param "$url") - if [[ -n "${SMOKE_TEST_TOKEN:-}" ]]; then - curl -sS -i -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X POST "$u" \ - -H "x-vercel-protection-bypass: $BYPASS_TOKEN" \ - -H "x-smoke-test-token: $SMOKE_TEST_TOKEN" \ - -H 'content-type: application/json' \ - -d "$body" - else - curl -sS -i -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X POST "$u" \ - -H "x-vercel-protection-bypass: $BYPASS_TOKEN" \ - -H 'content-type: application/json' \ - -d "$body" - fi - else - if [[ -n "${SMOKE_TEST_TOKEN:-}" ]]; then - curl -sS -i -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X POST "$url" \ - -H "x-smoke-test-token: $SMOKE_TEST_TOKEN" \ - -H 'content-type: application/json' \ - -d "$body" - else - curl -sS -i -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X POST "$url" \ - -H 'content-type: application/json' \ - -d "$body" - fi - fi -} - -kv_get_code() { - # Returns the code or empty - local key - if [[ -n "${HASH_PEPPER:-}" ]]; then - # Compute HMAC-SHA256("phone:" + PHONE) in hex (domain-separated) - local hex - if command -v openssl >/dev/null 2>&1; then - local msg - msg="phone:${PHONE}" - # Compatible parsing across OpenSSL variants - hex=$(printf "%s" "$msg" | openssl dgst -sha256 -hmac "$HASH_PEPPER" 2>/dev/null | sed 's/^.*= //') - elif command -v node >/dev/null 2>&1; then - hex=$(HASH_PEPPER="$HASH_PEPPER" PHONE="$PHONE" node -e "const c=require('crypto');const p=process.env.HASH_PEPPER;const ph=process.env.PHONE||'';const h=c.createHmac('sha256',p).update('phone:'+ph).digest('hex');console.log(h)" 2>/dev/null) - else - echo "ERROR: HASH_PEPPER is set but neither openssl nor node is available to compute the HMAC." 1>&2 - echo "Install openssl or node, or unset HASH_PEPPER for plaintext fallback (not recommended for preview/prod)." 1>&2 - exit 1 - fi - key="sms:code:${hex}" - else - key="sms:code:${PHONE_ESC}" - fi - local resp - resp=$(curl -sS -H "Authorization: Bearer $KV_REST_API_TOKEN" \ - "$KV_REST_API_URL/get/$key") - # Expecting JSON like: {"result":"123456"} or {"result":null} - echo "$resp" | grep -oE '"result":"[0-9]{6}"' | grep -oE '[0-9]{6}' || true -} - -debug_get_code() { - # Returns the code from the Preview function (requires SMOKE_TEST_TOKEN) or empty - if [[ -z "${SMOKE_TEST_TOKEN:-}" ]]; then - return 0 - fi - local url - url=$(with_bypass_param "$BASE_URL/api/debug/code") - local resp - resp=$(curl -sS -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X POST "$url" \ - -H "x-smoke-test-token: $SMOKE_TEST_TOKEN" \ - -H 'content-type: application/json' \ - -d "{\"phoneE164\":\"$PHONE\"}") - echo "$resp" | grep -oE '"code":"[0-9]{6}"' | grep -oE '[0-9]{6}' || true -} - -step() { - echo - echo "==> $1" -} - -# Capture response body from last HTTP message in curl output (headers + blank line + body) -extract_json_body() { - local file="$1" - awk 'NR==1{in_body=0} /^[[:space:]]*$/{if(NR>1)in_body=1;next} in_body{print}' "$file" -} - -parse_mode() { - local body="$1" - local mode - if command -v node >/dev/null 2>&1; then - mode=$(printf '%s' "$body" | node -e " - let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{ - try{const j=JSON.parse(d);const m=j.mode;console.log((m==='verify'||m==='kv-fallback')?m:'kv-fallback');}catch{console.log('kv-fallback');} - }); - " 2>/dev/null) || mode="kv-fallback" - else - mode=$(echo "$body" | sed -n 's/.*"mode"[[:space:]]*:[[:space:]]*"\(verify\|kv-fallback\)".*/\1/p' | head -1) - [[ -z "$mode" ]] && mode="kv-fallback" - fi - echo "$mode" -} - -SEND_RESP_FILE=$(mktemp) -trap 'rm -f "$COOKIE_JAR" "$SEND_RESP_FILE" >/dev/null 2>&1 || true' EXIT - -if [[ -n "${BYPASS_TOKEN:-}" ]]; then - step "Set Vercel bypass cookie" - curl -sS -i -c "$COOKIE_JAR" \ - "$(with_bypass_param "$BASE_URL/?x-vercel-set-bypass-cookie=true")" >/dev/null || true -fi - -step "Request SMS code" -post_json "$BASE_URL/api/sms/send" "{\"phoneE164\":\"$PHONE\",\"address\":\"$ADDR\"}" >"$SEND_RESP_FILE" 2>&1 - -SEND_BODY=$(extract_json_body "$SEND_RESP_FILE") -MODE=$(parse_mode "$SEND_BODY") - -# Check for send failure -if echo "$SEND_BODY" | grep -qE '"error"'; then - echo "ERROR: Send failed. Response: $SEND_BODY" - exit 1 -fi - -echo "Send mode: $MODE" - -if [[ "$MODE" == "verify" ]]; then - if [[ "$ENVIRONMENT" == "prod" ]]; then - step "Send-health (send ok) + eligibility check (prod verify mode; skipping OTP/mint)" - post_json "$BASE_URL/api/pre-check-eligibility" "{\"address\":\"$ADDR\",\"phoneE164\":\"$PHONE\"}" - echo - echo "Done. (prod verify mode: OTP verify/mint skipped by design)" - exit 0 - else - echo - echo "SKIP: mode=verify in $ENVIRONMENT — full E2E only when mode=kv-fallback." - echo "OTP verify and mint steps skipped." - step "Pre-check eligibility only" - post_json "$BASE_URL/api/pre-check-eligibility" "{\"address\":\"$ADDR\",\"phoneE164\":\"$PHONE\"}" - echo - echo "Done. (verify mode in non-prod: OTP/mint skipped)" - exit 0 - fi -fi - -# mode=kv-fallback: full E2E -if [[ -z "${KV_REST_API_URL:-}" || -z "${KV_REST_API_TOKEN:-}" ]]; then - echo "ERROR: KV_REST_API_URL and KV_REST_API_TOKEN are required for mode=kv-fallback." - exit 1 -fi - -step "Fetch code" -CODE="" -if [[ -n "${SMOKE_TEST_TOKEN:-}" ]]; then - for i in {1..4}; do - CODE=$(debug_get_code) - if [[ -n "$CODE" ]]; then break; fi - sleep 1 - done -fi - -if [[ -z "$CODE" ]]; then - for i in {1..8}; do - CODE=$(kv_get_code) - if [[ -n "$CODE" ]]; then break; fi - sleep 2 - done -fi -if [[ -z "$CODE" ]]; then - echo "ERROR: No code found in KV for $PHONE." - exit 1 -fi -echo "CODE=$CODE" - -step "Verify code" -post_json "$BASE_URL/api/sms/verify" "{\"phoneE164\":\"$PHONE\",\"code\":\"$CODE\"}" - -step "Check eligibility" -post_json "$BASE_URL/api/check-eligibility" "{\"address\":\"$ADDR\",\"phoneE164\":\"$PHONE\"}" - -step "Mint (stubbed)" -post_json "$BASE_URL/api/mint" "{\"address\":\"$ADDR\",\"phoneE164\":\"$PHONE\",\"authorAddress\":\"$AUTHOR\"}" - -echo -echo "Done." - - diff --git a/web/scripts/start-dev.mjs b/web/scripts/start-dev.mjs deleted file mode 100644 index 6451a08..0000000 --- a/web/scripts/start-dev.mjs +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env node - -import { existsSync } from "node:fs"; -import { spawn, spawnSync } from "node:child_process"; -import { get as httpGet } from "node:http"; -import { get as httpsGet } from "node:https"; -import path from "node:path"; -import process from "node:process"; -import { fileURLToPath } from "node:url"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const webRoot = path.resolve(__dirname, ".."); -const isWindows = process.platform === "win32"; -const binDir = path.join(webRoot, "node_modules", ".bin"); -const executableSuffix = isWindows ? ".cmd" : ""; -const portlessBin = path.join(binDir, `portless${executableSuffix}`); -const appName = "mintpass"; -const canonicalBranches = new Set(["main", "master"]); -const usePortless = process.env.PORTLESS !== "0" && !isWindows && existsSync(portlessBin); -const portlessProxyPort = process.env.PORTLESS_PORT || "443"; -const portlessEnv = { - ...process.env, - PORTLESS_PORT: portlessProxyPort, - PORTLESS_HTTPS: process.env.PORTLESS_HTTPS ?? "1", - PORTLESS_LAN: process.env.PORTLESS_LAN ?? "0", -}; - -function sanitizeLabel(value) { - return value - .toLowerCase() - .replace(/[^a-z0-9]+/g, "-") - .replace(/^-+|-+$/g, "") - .replace(/-{2,}/g, "-"); -} - -function getCurrentBranch() { - const result = spawnSync("git", ["branch", "--show-current"], { - cwd: webRoot, - encoding: "utf8", - }); - - if (result.status !== 0) { - return null; - } - - return result.stdout.trim() || null; -} - -function getPortlessAppName() { - const branch = getCurrentBranch(); - - if (!branch || canonicalBranches.has(branch)) { - return appName; - } - - const lastSegment = branch.split("/").pop() ?? branch; - const label = sanitizeLabel(lastSegment); - - return `${label}.${appName}`; -} - -function ensurePortlessProxy() { - const result = spawnSync(portlessBin, ["proxy", "start", "--port", portlessProxyPort, "--https"], { - cwd: webRoot, - env: portlessEnv, - stdio: "inherit", - }); - - if (result.status !== 0) { - process.exit(result.status ?? 1); - } -} - -if (usePortless) { - ensurePortlessProxy(); -} - -const portlessAppName = getPortlessAppName(); -const publicUrl = usePortless ? `https://${portlessAppName}.localhost` : null; -const command = usePortless ? portlessBin : "next"; -const args = usePortless - ? [portlessAppName, "next", "dev", "--turbopack"] - : ["dev", "--turbopack"]; - -const child = spawn(command, args, { - cwd: webRoot, - stdio: "inherit", - env: usePortless ? portlessEnv : process.env, -}); - -if (publicUrl && process.env.BROWSER !== "none") { - waitForUrlReady(publicUrl, 30_000) - .then(() => { - console.log(`Opening ${publicUrl} in browser...`); - openInBrowser(publicUrl); - }) - .catch((error) => { - console.warn(`Could not auto-open ${publicUrl}: ${error.message}`); - }); -} - -child.on("exit", (code, signal) => { - if (signal) { - process.kill(process.pid, signal); - return; - } - - process.exit(code ?? 0); -}); - -async function waitForUrlReady(url, timeoutMs) { - const startedAt = Date.now(); - - while (Date.now() - startedAt < timeoutMs) { - const ready = await new Promise((resolve) => { - const parsedUrl = new URL(url); - const getUrl = parsedUrl.protocol === "https:" ? httpsGet : httpGet; - const onResponse = (response) => { - response.resume(); - const statusCode = response.statusCode ?? 500; - resolve(statusCode >= 200 && statusCode < 400); - }; - const request = - parsedUrl.protocol === "https:" - ? getUrl(parsedUrl, { rejectUnauthorized: false }, onResponse) - : getUrl(parsedUrl, onResponse); - - request.on("error", () => resolve(false)); - request.setTimeout(2_000, () => { - request.destroy(); - resolve(false); - }); - }); - - if (ready) { - return; - } - - await new Promise((resolve) => setTimeout(resolve, 200)); - } - - throw new Error(`Timed out waiting for ${url}`); -} - -function openInBrowser(url) { - const opener = - process.platform === "darwin" ? { cmd: "open", args: [url] } - : process.platform === "win32" ? { cmd: "cmd", args: ["/c", "start", '""', url] } - : { cmd: "xdg-open", args: [url] }; - - spawn(opener.cmd, opener.args, { stdio: "ignore", detached: true }).unref(); -} diff --git a/web/src/components/confetti-celebration.tsx b/web/src/components/confetti-celebration.tsx deleted file mode 100644 index 9228c88..0000000 --- a/web/src/components/confetti-celebration.tsx +++ /dev/null @@ -1,59 +0,0 @@ -"use client"; - -import confetti from "canvas-confetti"; -import { useEffect, useRef } from "react"; - -export function ConfettiCelebration() { - const animationIdRef = useRef(null); - - useEffect(() => { - const celebrateWithSideCannons = () => { - const end = Date.now() + 1 * 1000; // 1 second - const colors = ["#9ddcdd", "#077b91"]; // MintPass project colors - - const frame = () => { - if (Date.now() > end) return; - - confetti({ - particleCount: 2, - angle: 60, - spread: 55, - startVelocity: 60, - origin: { x: 0, y: 0.5 }, - colors: colors, - }); - confetti({ - particleCount: 2, - angle: 120, - spread: 55, - startVelocity: 60, - origin: { x: 1, y: 0.5 }, - colors: colors, - }); - - if (animationIdRef.current !== null) { - cancelAnimationFrame(animationIdRef.current); - } - animationIdRef.current = requestAnimationFrame(frame); - }; - - frame(); - - // Return cleanup function - return () => { - if (animationIdRef.current !== null) { - cancelAnimationFrame(animationIdRef.current); - animationIdRef.current = null; - } - }; - }; - - // Start celebration and get cleanup function - const cleanup = celebrateWithSideCannons(); - - // Return cleanup to useEffect - return cleanup; - }, []); - - return null; // This component just triggers effects -} diff --git a/web/src/components/footer.tsx b/web/src/components/footer.tsx deleted file mode 100644 index 83e8efc..0000000 --- a/web/src/components/footer.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import Link from 'next/link'; - -export function Footer() { - return ( - - ); -} diff --git a/web/src/components/header.tsx b/web/src/components/header.tsx deleted file mode 100644 index 02707c2..0000000 --- a/web/src/components/header.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useRouter } from 'next/router'; -import { useCallback } from 'react'; -import Image from 'next/image'; -import { AnimatedThemeToggler } from './magicui/animated-theme-toggler'; -import Link from 'next/link'; -import { Button } from '@/components/ui/button'; - -type HeaderProps = { - /** Additional content to show in the navigation area (like links) */ - children?: React.ReactNode; -}; - -export function Header({ children }: HeaderProps) { - const router = useRouter(); - - const handleTitleClick = useCallback(() => { - router.push('/'); - }, [router]); - - return ( -
-
- -
- - {children} - -
-
-
- ); -} diff --git a/web/src/components/magicui/animated-theme-toggler.tsx b/web/src/components/magicui/animated-theme-toggler.tsx deleted file mode 100644 index aba0e86..0000000 --- a/web/src/components/magicui/animated-theme-toggler.tsx +++ /dev/null @@ -1,109 +0,0 @@ -"use client"; - -import { Moon, SunDim } from "lucide-react"; -import { useRef } from "react"; -import { flushSync } from "react-dom"; -import { useTheme } from "next-themes"; -import { cn } from "@/lib/utils"; - -type props = { - className?: string; -}; - -export const AnimatedThemeToggler = ({ className }: props) => { - const { setTheme, resolvedTheme } = useTheme(); - const buttonRef = useRef(null); - const mounted = resolvedTheme !== undefined; - - const changeTheme = async () => { - if (!buttonRef.current) return; - - const newTheme = resolvedTheme === "dark" ? "light" : "dark"; - - // Check if user prefers reduced motion - const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - - // Check if browser supports view transitions with proper feature detection - if (prefersReducedMotion || typeof document.startViewTransition !== 'function') { - setTheme(newTheme); - return; - } - - try { - await document.startViewTransition(() => { - flushSync(() => { - setTheme(newTheme); - }); - }).ready; - } catch { - // Fallback if View Transitions fails/rejects - console.warn('startViewTransition failed; falling back to direct theme set'); - flushSync(() => { - setTheme(newTheme); - }); - } - - // Guard against unmounts or ref becoming null after async boundary - const buttonEl = buttonRef.current; - if (!buttonEl || !buttonEl.isConnected || !document.documentElement) return; - - const { top, left, width, height } = buttonEl.getBoundingClientRect(); - - // Calculate center coordinates - const centerX = left + width / 2; - const centerY = top + height / 2; - - // Calculate maximum distances from center to viewport edges - const dx = Math.max(centerX, window.innerWidth - centerX); - const dy = Math.max(centerY, window.innerHeight - centerY); - const maxRad = Math.hypot(dx, dy); - - // As a final guard, wrap animate in try/catch to avoid runtime errors - try { - document.documentElement.animate( - { - clipPath: [ - `circle(0px at ${centerX}px ${centerY}px)`, - `circle(${maxRad}px at ${centerX}px ${centerY}px)`, - ], - }, - { - duration: 700, - easing: "ease-in-out", - pseudoElement: "::view-transition-new(root)", - }, - ); - } catch { - // no-op: animation is best-effort - } - }; - - // Show consistent loading state until hydration is complete - if (!mounted) { - return ( - - ); - } - - return ( - - ); -}; diff --git a/web/src/components/magicui/confetti.tsx b/web/src/components/magicui/confetti.tsx deleted file mode 100644 index e5a9774..0000000 --- a/web/src/components/magicui/confetti.tsx +++ /dev/null @@ -1,171 +0,0 @@ -"use client"; - -import type { - GlobalOptions as ConfettiGlobalOptions, - CreateTypes as ConfettiInstance, - Options as ConfettiOptions, -} from "canvas-confetti"; -import confetti from "canvas-confetti"; -import type { ReactNode } from "react"; -import React, { - createContext, - forwardRef, - useCallback, - useEffect, - useImperativeHandle, - useMemo, - useRef, -} from "react"; - -import { Button, ButtonProps } from "@/components/ui/button"; - -type Api = { - fire: (options?: ConfettiOptions) => void; -}; - -type Props = React.ComponentPropsWithRef<"canvas"> & { - options?: ConfettiOptions; - globalOptions?: ConfettiGlobalOptions; - manualstart?: boolean; - children?: ReactNode; -}; - -export type ConfettiRef = Api | null; - -const ConfettiContext = createContext({} as Api); - -// Define component first -const ConfettiComponent = forwardRef((props, ref) => { - const { - options, - globalOptions = { resize: true, useWorker: true }, - manualstart = false, - children, - ...rest - } = props; - const instanceRef = useRef(null); - - const canvasRef = useCallback( - (node: HTMLCanvasElement) => { - if (node !== null) { - if (instanceRef.current) { - try { - instanceRef.current.reset(); - } catch {} - instanceRef.current = null; - } - instanceRef.current = confetti.create(node, { - resize: true, // Default value - ...globalOptions, // Allow globalOptions to override defaults - }); - } else { - if (instanceRef.current) { - try { - instanceRef.current.reset(); - } catch {} - instanceRef.current = null; - } - } - }, - [globalOptions], - ); - - const fire = useCallback( - async (opts = {}) => { - try { - await instanceRef.current?.({ ...(options ?? {}), ...(opts ?? {}) }); - } catch (error) { - console.error("Confetti error:", error); - } - }, - [options], - ); - - const api = useMemo( - () => ({ - fire, - }), - [fire], - ); - - useImperativeHandle(ref, () => api, [api]); - - useEffect(() => { - if (!manualstart) { - (async () => { - try { - await fire(); - } catch (error) { - console.error("Confetti effect error:", error); - } - })(); - } - }, [manualstart, fire]); - - // Cleanup confetti instance on unmount - useEffect(() => { - return () => { - if (instanceRef.current) { - instanceRef.current.reset(); - instanceRef.current = null; - } - }; - }, []); - - return ( - - - {children} - - ); -}); - -// Set display name immediately -ConfettiComponent.displayName = "Confetti"; - -// Export as Confetti -export const Confetti = ConfettiComponent; - -interface ConfettiButtonProps extends Omit { - options?: ConfettiOptions & - ConfettiGlobalOptions & { canvas?: HTMLCanvasElement }; - children?: React.ReactNode; - onClick?: (event: React.MouseEvent) => void | Promise; -} - -const ConfettiButtonComponent = ({ - options, - children, - onClick, - ...props -}: ConfettiButtonProps) => { - const handleClick = async (event: React.MouseEvent) => { - try { - // Call original onClick handler if provided - if (typeof onClick === 'function') await onClick(event); - - const rect = event.currentTarget.getBoundingClientRect(); - const x = rect.left + rect.width / 2; - const y = rect.top + rect.height / 2; - await confetti({ - ...(options ?? {}), - origin: { - x: x / window.innerWidth, - y: y / window.innerHeight, - }, - }); - } catch (error) { - console.error("Confetti button error:", error); - } - }; - - return ( - - ); -}; - -ConfettiButtonComponent.displayName = "ConfettiButton"; - -export const ConfettiButton = ConfettiButtonComponent; diff --git a/web/src/components/magicui/rainbow-button.tsx b/web/src/components/magicui/rainbow-button.tsx deleted file mode 100644 index 3e1543c..0000000 --- a/web/src/components/magicui/rainbow-button.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { cn } from "@/lib/utils"; -import { Slot } from "@radix-ui/react-slot"; -import { cva, VariantProps } from "class-variance-authority"; -import React from "react"; - -const rainbowButtonVariants = cva( - cn( - "relative cursor-pointer group transition-all animate-rainbow", - "inline-flex items-center justify-center gap-2 shrink-0", - "rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive", - "text-sm font-medium whitespace-nowrap", - "disabled:pointer-events-none disabled:opacity-50", - "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0", - ), - { - variants: { - variant: { - default: - "border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]", - outline: - "border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]", - }, - size: { - default: "h-9 px-4 py-2", - sm: "h-8 rounded-xl px-3 text-xs", - lg: "h-11 rounded-xl px-8", - icon: "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -); - -interface RainbowButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -const RainbowButton = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - return ( - - ); - }, -); - -RainbowButton.displayName = "RainbowButton"; - -export { RainbowButton, rainbowButtonVariants, type RainbowButtonProps }; diff --git a/web/src/components/page-card.tsx b/web/src/components/page-card.tsx deleted file mode 100644 index 1bfdd35..0000000 --- a/web/src/components/page-card.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from 'react' - -import { cn } from '@/lib/utils' -import { Card, CardContent, CardHeader, CardFooter } from './ui/card' - -type PageCardProps = { - title: string - children: React.ReactNode - contentClassName?: string - titleClassName?: string - containerClassName?: string - titleAs?: React.ElementType - footer?: React.ReactNode - footerClassName?: string -} - -export function PageCard({ - title, - children, - contentClassName = '', - titleClassName = '', - containerClassName, - titleAs = 'h2', - footer, - footerClassName = '', -}: PageCardProps) { - const TitleTag = titleAs - - // Get appropriate font size class based on heading level - const getDefaultTitleClasses = (tagName: React.ElementType) => { - if (typeof tagName === 'string') { - switch (tagName) { - case 'h1': return 'text-xl md:text-2xl font-semibold leading-none tracking-tight' - case 'h2': return 'text-lg md:text-xl font-semibold leading-none tracking-tight' - case 'h3': return 'text-base md:text-lg font-semibold leading-none tracking-tight' - default: return 'font-semibold leading-none tracking-tight' - } - } - return 'font-semibold leading-none tracking-tight' - } - - return ( -
- - - - {title} - - - - {children} - - {footer && ( - - {footer} - - )} - -
- ) -} - - diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx deleted file mode 100644 index 1926964..0000000 --- a/web/src/components/ui/button.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" - -const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", - { - variants: { - variant: { - default: - "bg-primary text-primary-foreground shadow hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", - outline: - "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-9 px-4 py-2", - sm: "h-8 rounded-md px-3 text-xs", - lg: "h-10 rounded-md px-8", - icon: "h-9 w-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, type, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - - ) - } -) -Button.displayName = "Button" - -export { Button, buttonVariants } diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx deleted file mode 100644 index 6b1adea..0000000 --- a/web/src/components/ui/card.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLHeadingElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/web/src/components/ui/command.tsx b/web/src/components/ui/command.tsx deleted file mode 100644 index 9036e39..0000000 --- a/web/src/components/ui/command.tsx +++ /dev/null @@ -1,153 +0,0 @@ -"use client" - -import * as React from "react" -import { type DialogProps } from "@radix-ui/react-dialog" -import { Command as CommandPrimitive } from "cmdk" -import { Search } from "lucide-react" - -import { cn } from "@/lib/utils" -import { Dialog, DialogContent } from "@/components/ui/dialog" - -const Command = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -Command.displayName = CommandPrimitive.displayName - -const CommandDialog = ({ children, ...props }: DialogProps) => { - return ( - - - - {children} - - - - ) -} - -const CommandInput = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( -
- - -
-)) - -CommandInput.displayName = CommandPrimitive.Input.displayName - -const CommandList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) - -CommandList.displayName = CommandPrimitive.List.displayName - -const CommandEmpty = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->((props, ref) => ( - -)) - -CommandEmpty.displayName = CommandPrimitive.Empty.displayName - -const CommandGroup = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) - -CommandGroup.displayName = CommandPrimitive.Group.displayName - -const CommandSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -CommandSeparator.displayName = CommandPrimitive.Separator.displayName - -const CommandItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) - -CommandItem.displayName = CommandPrimitive.Item.displayName - -const CommandShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) -} -CommandShortcut.displayName = "CommandShortcut" - -export { - Command, - CommandDialog, - CommandInput, - CommandList, - CommandEmpty, - CommandGroup, - CommandItem, - CommandShortcut, - CommandSeparator, -} diff --git a/web/src/components/ui/dialog.tsx b/web/src/components/ui/dialog.tsx deleted file mode 100644 index 9dbeaa0..0000000 --- a/web/src/components/ui/dialog.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { X } from "lucide-react" - -import { cn } from "@/lib/utils" - -const Dialog = DialogPrimitive.Root - -const DialogTrigger = DialogPrimitive.Trigger - -const DialogPortal = DialogPrimitive.Portal - -const DialogClose = DialogPrimitive.Close - -const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName - -const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - -)) -DialogContent.displayName = DialogPrimitive.Content.displayName - -const DialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DialogHeader.displayName = "DialogHeader" - -const DialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DialogFooter.displayName = "DialogFooter" - -const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName - -const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName - -export { - Dialog, - DialogPortal, - DialogOverlay, - DialogTrigger, - DialogClose, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, -} diff --git a/web/src/components/ui/dropdown-menu.tsx b/web/src/components/ui/dropdown-menu.tsx deleted file mode 100644 index e804bca..0000000 --- a/web/src/components/ui/dropdown-menu.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { Check, ChevronRight, Circle } from "lucide-react" - -import { cn } from "@/lib/utils" - -const DropdownMenu = DropdownMenuPrimitive.Root - -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger - -const DropdownMenuGroup = DropdownMenuPrimitive.Group - -const DropdownMenuPortal = DropdownMenuPrimitive.Portal - -const DropdownMenuSub = DropdownMenuPrimitive.Sub - -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup - -const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, children, ...props }, ref) => ( - - {children} - - -)) -DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName - -const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName - -const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)) -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName - -const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - svg]:size-4 [&>svg]:shrink-0", - inset && "pl-8", - className - )} - {...props} - /> -)) -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName - -const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName - -const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName - -const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName - -const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName - -const DropdownMenuShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) -} -DropdownMenuShortcut.displayName = "DropdownMenuShortcut" - -export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, -} diff --git a/web/src/components/ui/input-otp.tsx b/web/src/components/ui/input-otp.tsx deleted file mode 100644 index 2c733bd..0000000 --- a/web/src/components/ui/input-otp.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import * as React from "react" -import { OTPInput, OTPInputContext } from "input-otp" -import { Minus } from "lucide-react" - -import { cn } from "@/lib/utils" - -const InputOTP = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, containerClassName, ...props }, ref) => ( - -)) -InputOTP.displayName = "InputOTP" - -const InputOTPGroup = React.forwardRef< - React.ElementRef<"div">, - React.ComponentPropsWithoutRef<"div"> ->(({ className, ...props }, ref) => ( -
-)) -InputOTPGroup.displayName = "InputOTPGroup" - -const InputOTPSlot = React.forwardRef< - React.ElementRef<"div">, - React.ComponentPropsWithoutRef<"div"> & { index: number } ->(({ index, className, ...props }, ref) => { - const inputOTPContext = React.useContext(OTPInputContext) - - // Compute safe defaults and then render once - const slots = inputOTPContext?.slots - const hasValidIndex = Array.isArray(slots) && index >= 0 && index < slots.length - const char = hasValidIndex ? slots[index].char : '' - const hasFakeCaret = hasValidIndex ? slots[index].hasFakeCaret : false - const isActive = hasValidIndex ? slots[index].isActive : false - - return ( -
- {char} - {hasFakeCaret && ( -
-
-
- )} -
- ) -}) -InputOTPSlot.displayName = "InputOTPSlot" - -const InputOTPSeparator = React.forwardRef< - React.ElementRef<"div">, - React.ComponentPropsWithoutRef<"div"> ->(({ ...props }, ref) => ( -
- -
-)) -InputOTPSeparator.displayName = "InputOTPSeparator" - -export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx deleted file mode 100644 index 69b64fb..0000000 --- a/web/src/components/ui/input.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Input = React.forwardRef>( - ({ className, type, ...props }, ref) => { - return ( - - ) - } -) -Input.displayName = "Input" - -export { Input } diff --git a/web/src/components/ui/label.tsx b/web/src/components/ui/label.tsx deleted file mode 100644 index 683faa7..0000000 --- a/web/src/components/ui/label.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" - -const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" -) - -const Label = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps ->(({ className, ...props }, ref) => ( - -)) -Label.displayName = LabelPrimitive.Root.displayName - -export { Label } diff --git a/web/src/components/ui/phone-input.tsx b/web/src/components/ui/phone-input.tsx deleted file mode 100644 index da55dec..0000000 --- a/web/src/components/ui/phone-input.tsx +++ /dev/null @@ -1,298 +0,0 @@ -import * as React from "react"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; -import * as RPNInput from "react-phone-number-input"; -import flags from "react-phone-number-input/flags"; -import { parsePhoneNumber } from "react-phone-number-input"; - -import { Button } from "@/components/ui/button"; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command"; -import { Input } from "@/components/ui/input"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { cn } from "@/lib/utils"; - -// Countries restricted for SMS verification due to policy and security reasons -const UNSUPPORTED_COUNTRIES: RPNInput.Country[] = [ - "AF", // Afghanistan - "BY", // Belarus - "CU", // Cuba - "IR", // Iran - "IL", // Israel - "KP", // North Korea - "MM", // Myanmar - "RU", // Russia - "SY", // Syria - "VE", // Venezuela - "AZ", // Azerbaijan - "BD", // Bangladesh - "ID", // Indonesia - "OM", // Oman - "LK", // Sri Lanka - "PS", // Palestinian Territory - "TJ", // Tajikistan - "VN", // Vietnam - "PK", // Pakistan - "DZ", // Algeria - "NG", // Nigeria - "TN", // Tunisia -]; - -type PhoneInputProps = Omit, "onChange" | "value" | "ref"> & - Omit, "onChange"> & { - onChange?: (value: RPNInput.Value) => void; - onCountryChange?: (country: RPNInput.Country | undefined) => void; - }; - -const PhoneInput: React.ForwardRefExoticComponent = React.forwardRef< - React.ElementRef, - PhoneInputProps ->(({ className, onChange, onCountryChange, value, ...props }, ref) => { - return ( - { - const phoneValue = value || ("" as RPNInput.Value); - onChange?.(phoneValue); - - // Extract country from phone number and call onCountryChange - if (onCountryChange) { - let country: RPNInput.Country | undefined; - try { - if (phoneValue) { - const parsed = parsePhoneNumber(phoneValue); - country = parsed?.country; - } - } catch { - // If parsing fails, country remains undefined - } - onCountryChange(country); - } - }} - {...props} - /> - ); -}); -PhoneInput.displayName = "PhoneInput"; - -const InputComponent = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ), -); -InputComponent.displayName = "InputComponent"; - -type CountryEntry = { label: string; value: RPNInput.Country | undefined }; - -type CountrySelectProps = { - disabled?: boolean; - value: RPNInput.Country; - options: CountryEntry[]; - onChange: (country: RPNInput.Country) => void; -}; - -const CountrySelect = ({ - disabled, - value: selectedCountry, - options: countryList, - onChange, -}: CountrySelectProps) => { - const scrollAreaRef = React.useRef(null); - const inputRef = React.useRef(null); - const [searchValue, setSearchValue] = React.useState(""); - const [isOpen, setIsOpen] = React.useState(false); - - // Detect mobile device - calculated during rendering (not expensive, no need for useMemo) - const isMobileDevice = - typeof window !== "undefined" && - /android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent); - - // Track whether user has manually clicked the input (to allow manual search on mobile) - const [hasUserFocused, setHasUserFocused] = React.useState(false); - - // Helper function to blur input on mobile devices only - const blurInputAfterOpen = React.useCallback(() => { - requestAnimationFrame(() => { - if (inputRef.current) { - inputRef.current.blur(); - } - }); - }, []); - - const filteredCountryList = countryList.filter(({ value }) => value); - - return ( - { - setIsOpen(open); - if (open) { - setSearchValue(""); - setHasUserFocused(false); - // Only prevent auto-focus on mobile devices - if (isMobileDevice) { - blurInputAfterOpen(); - } - // On desktop, allow natural autofocus behavior - } - }} - > - - - - - - { - setSearchValue(value); - requestAnimationFrame(() => { - if (scrollAreaRef.current) { - const viewportElement = scrollAreaRef.current.querySelector( - "[data-radix-scroll-area-viewport]", - ); - if (viewportElement) { - viewportElement.scrollTop = 0; - } - } - }); - }} - onFocus={(e) => { - // On mobile, prevent automatic focus (but allow it after user clicks the input) - if (isMobileDevice && !hasUserFocused) { - e.preventDefault(); - e.target.blur(); - return false; - } - // On desktop, allow all focus events naturally - }} - onClick={() => { - // When user clicks the input, allow focus (works on both mobile and desktop) - setHasUserFocused(true); - }} - placeholder="Search country..." - autoFocus={!isMobileDevice} - /> - - - No country found. - - {filteredCountryList.map(({ value, label }) => - value ? ( - setIsOpen(false)} - isSupported={!UNSUPPORTED_COUNTRIES.includes(value)} - /> - ) : null, - )} - - - - - - - ); -}; - -interface CountrySelectOptionProps extends RPNInput.FlagProps { - selectedCountry: RPNInput.Country; - onChange: (country: RPNInput.Country) => void; - onSelectComplete: () => void; - isSupported: boolean; -} - -const CountrySelectOption = ({ - country, - countryName, - selectedCountry, - onChange, - onSelectComplete, - isSupported, -}: CountrySelectOptionProps) => { - const handleSelect = () => { - if (!isSupported) return; // Don't allow selection of unsupported countries - onChange(country); - onSelectComplete(); - }; - - // Calculate country code during rendering - const countryCode = React.useMemo(() => { - try { - const code = RPNInput.getCountryCallingCode(country); - return code ? `+${code}` : null; - } catch { - return null; - } - }, [country]); - - return ( - - - {countryName} - {!isSupported ? ( - not available - ) : countryCode ? ( - {countryCode} - ) : null} - - - ); -}; - -const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => { - type PhoneFlagComponent = React.ComponentType<{ title?: string }>; - const map = flags as Record; - const Flag = country ? map[country] : undefined; - - return ( - - {Flag ? : null} - - ); -}; - -export { PhoneInput, UNSUPPORTED_COUNTRIES }; diff --git a/web/src/components/ui/popover.tsx b/web/src/components/ui/popover.tsx deleted file mode 100644 index fdcb511..0000000 --- a/web/src/components/ui/popover.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from "react" -import * as PopoverPrimitive from "@radix-ui/react-popover" - -import { cn } from "@/lib/utils" - -const Popover = PopoverPrimitive.Root - -const PopoverTrigger = PopoverPrimitive.Trigger - -const PopoverAnchor = PopoverPrimitive.Anchor - -const PopoverContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( - - - -)) -PopoverContent.displayName = PopoverPrimitive.Content.displayName - -export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } diff --git a/web/src/components/ui/scroll-area.tsx b/web/src/components/ui/scroll-area.tsx deleted file mode 100644 index d59c53c..0000000 --- a/web/src/components/ui/scroll-area.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from "react" -import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" - -import { cn } from "@/lib/utils" - -const ScrollArea = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - {children} - - - - -)) -ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName - -const ScrollBar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, orientation = "vertical", ...props }, ref) => ( - - - -)) -ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName - -export { ScrollArea, ScrollBar } diff --git a/web/src/components/ui/shadcn-io/hexagon-background/index.tsx b/web/src/components/ui/shadcn-io/hexagon-background/index.tsx deleted file mode 100644 index 3e7ea31..0000000 --- a/web/src/components/ui/shadcn-io/hexagon-background/index.tsx +++ /dev/null @@ -1,250 +0,0 @@ -'use client'; - -import * as React from 'react'; - -import { cn } from '@/lib/utils'; - -type HexagonBackgroundProps = React.ComponentProps<'div'> & { - children?: React.ReactNode; - hexagonProps?: React.ComponentProps<'div'>; - hexagonSize?: number; // value greater than 50 - hexagonMargin?: number; -}; - -function HexagonBackground({ - className, - children, - hexagonProps, - hexagonSize = 75, - hexagonMargin = 3, - ...props -}: HexagonBackgroundProps) { - // Validate/coerce size early (do not early-return before hooks) - const size = Number(hexagonSize); - const isValidSize = Number.isFinite(size) && size > 50; - - const hexagonWidth = size; - const hexagonHeight = size * 1.1; - const rowSpacing = size * 0.8; - const baseMarginTop = -36 - 0.275 * (size - 100); - const computedMarginTop = baseMarginTop + hexagonMargin; - const oddRowMarginLeft = -(size / 2); - const evenRowMarginLeft = hexagonMargin / 2; - - const [gridDimensions, setGridDimensions] = React.useState({ - rows: 0, - columns: 0, - }); - const containerRef = React.useRef(null); - const trailDurationMs = 700; - const activeTimersRef = React.useRef>(new Map()); - const activeKeysRef = React.useRef>(new Set()); - const debounceTimerRef = React.useRef(null); - const expiryMapRef = React.useRef>(new Map()); - const hexRefsRef = React.useRef>(new Map()); - - const updateGridDimensions = React.useCallback(() => { - if (typeof window === 'undefined' || !isValidSize) return; - // Use the full document height instead of just viewport height - const documentHeight = Math.max( - document.body.scrollHeight, - document.documentElement.scrollHeight, - window.innerHeight - ); - const rows = Math.ceil(documentHeight / rowSpacing); - const columns = Math.ceil(window.innerWidth / hexagonWidth) + 1; - setGridDimensions({ rows, columns }); - }, [rowSpacing, hexagonWidth, isValidSize]); - - React.useEffect(() => { - if (!isValidSize) return; - updateGridDimensions(); - window.addEventListener('resize', updateGridDimensions); - - // Prefer a scoped ResizeObserver on the component container - let ro: ResizeObserver | null = null; - if (typeof ResizeObserver !== 'undefined' && containerRef.current) { - ro = new ResizeObserver(() => { - if (debounceTimerRef.current !== null) { - clearTimeout(debounceTimerRef.current); - } - debounceTimerRef.current = window.setTimeout(updateGridDimensions, 100); - }); - ro.observe(containerRef.current); - } - - return () => { - window.removeEventListener('resize', updateGridDimensions); - if (ro) ro.disconnect(); - if (debounceTimerRef.current !== null) { - clearTimeout(debounceTimerRef.current); - debounceTimerRef.current = null; - } - }; - }, [updateGridDimensions, isValidSize]); - - const clearActiveKey = React.useCallback((key: string) => { - const timersMap = activeTimersRef.current; - const existingTimeout = timersMap.get(key); - if (existingTimeout !== undefined) { - window.clearTimeout(existingTimeout); - timersMap.delete(key); - } - expiryMapRef.current.delete(key); - activeKeysRef.current.delete(key); - const el = hexRefsRef.current.get(key); - if (el) { - el.removeAttribute('data-active'); - } - }, []); - - const clearAllActive = React.useCallback(() => { - for (const key of Array.from(activeKeysRef.current)) { - clearActiveKey(key); - } - }, [clearActiveKey]); - - const activateHex = React.useCallback((key: string) => { - if (activeKeysRef.current.has(key)) return; - activeKeysRef.current.add(key); - const el = hexRefsRef.current.get(key); - if (el) { - el.setAttribute('data-active', 'true'); - } - expiryMapRef.current.set(key, Date.now() + trailDurationMs + 50); - const timeoutId = window.setTimeout(() => { - clearActiveKey(key); - }, trailDurationMs); - activeTimersRef.current.set(key, timeoutId); - }, [trailDurationMs, clearActiveKey]); - - const updateActiveFromPoint = React.useCallback((clientX: number, clientY: number) => { - if (!containerRef.current) return; - const rect = containerRef.current.getBoundingClientRect(); - const x = clientX - rect.left; - const y = clientY - rect.top; - - const row = Math.floor(y / rowSpacing); - if (row < 0 || row >= gridDimensions.rows) return; - const shift = (((row + 1) % 2 === 0 ? evenRowMarginLeft : oddRowMarginLeft) - 10); - const col = Math.floor((x - shift) / hexagonWidth); - if (col < 0 || col >= gridDimensions.columns) return; - const key = `${row}-${col}`; - - // If this hex is not currently in the trail, add it and schedule removal - if (!activeTimersRef.current.has(key)) { - activateHex(key); - } - }, [gridDimensions.rows, gridDimensions.columns, rowSpacing, hexagonWidth, evenRowMarginLeft, oddRowMarginLeft, activateHex]); - - React.useEffect(() => { - if (!isValidSize) return; - const move = (e: MouseEvent) => updateActiveFromPoint(e.clientX, e.clientY); - const leave = () => clearAllActive(); - const onVisibility = () => clearAllActive(); - window.addEventListener('mousemove', move, { passive: true }); - window.addEventListener('mouseleave', leave, { passive: true }); - window.addEventListener('blur', leave); - document.addEventListener('visibilitychange', onVisibility); - return () => { - window.removeEventListener('mousemove', move); - window.removeEventListener('mouseleave', leave); - window.removeEventListener('blur', leave); - document.removeEventListener('visibilitychange', onVisibility); - clearAllActive(); - }; - }, [updateActiveFromPoint, isValidSize, clearAllActive]); - - // Failsafe GC: periodically clear any overdue actives in case a timeout was throttled/lost - React.useEffect(() => { - if (!isValidSize) return; - const intervalId = window.setInterval(() => { - const now = Date.now(); - const expired: string[] = []; - for (const [key, expiry] of expiryMapRef.current) { - if (expiry <= now) expired.push(key); - } - if (expired.length) { - for (const key of expired) clearActiveKey(key); - } - }, 500); - return () => window.clearInterval(intervalId); - }, [isValidSize, clearActiveKey]); - - // Trail is managed with per-hex timeouts; no idle loop needed - - if (!isValidSize) return null; - - return ( -
- -
- {Array.from({ length: gridDimensions.rows }).map((_, rowIndex) => ( -
- {Array.from({ length: gridDimensions.columns }).map( - (_, colIndex) => ( -
{ - const key = `${rowIndex}-${colIndex}`; - if (el) { - hexRefsRef.current.set(key, el); - } else { - hexRefsRef.current.delete(key); - } - }} - /> - ), - )} -
- ))} -
-
- {children} -
-
- ); -} - -export { HexagonBackground, type HexagonBackgroundProps }; \ No newline at end of file diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts deleted file mode 100644 index bd0c391..0000000 --- a/web/src/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/web/src/pages/404.tsx b/web/src/pages/404.tsx deleted file mode 100644 index b52f11a..0000000 --- a/web/src/pages/404.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/router'; -import Link from 'next/link'; -import { Header } from '../components/header'; -import { Footer } from '../components/footer'; -import { PageCard } from '../components/page-card'; - - -export default function NotFoundPage() { - const router = useRouter(); - const [secondsLeft, setSecondsLeft] = useState(5); - - useEffect(() => { - const interval = setInterval(() => { - setSecondsLeft((prev) => { - if (prev <= 1) { - clearInterval(interval); - void router.replace('/'); - return 0; - } - return prev - 1; - }); - }, 1000); - return () => clearInterval(interval); - }, [router]); - - return ( -
-
-
- -

- The page you are looking for doesn't exist or may have moved. -

-

- Redirecting to home in {secondsLeft} second{secondsLeft !== 1 ? 's' : ''}... -

-
-
-
-
- ); -} diff --git a/web/src/pages/_app.tsx b/web/src/pages/_app.tsx deleted file mode 100644 index 29a43d3..0000000 --- a/web/src/pages/_app.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { AppProps } from 'next/app'; -import { ThemeProvider } from 'next-themes'; -import '../styles/globals.css'; -import { HexagonBackground } from '@/components/ui/shadcn-io/hexagon-background'; -import { Analytics } from '@vercel/analytics/next'; - -export default function App({ Component, pageProps }: AppProps) { - return ( - - - - - - - ); -} diff --git a/web/src/pages/_document.tsx b/web/src/pages/_document.tsx deleted file mode 100644 index d65126a..0000000 --- a/web/src/pages/_document.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Html, Head, Main, NextScript } from 'next/document'; - -export default function Document() { - return ( - - - {/* Standard favicon */} - - - {/* PNG favicons for different sizes */} - - - - {/* Apple Touch Icon */} - - - {/* Android/Chrome icons */} - - - - {/* Web App Manifest */} - - - {/* Theme color for mobile browsers */} - - - {/* Additional meta tags */} - - - - - - - -
- - - - ); -} diff --git a/web/src/pages/admin.tsx b/web/src/pages/admin.tsx deleted file mode 100644 index eb39ccb..0000000 --- a/web/src/pages/admin.tsx +++ /dev/null @@ -1,327 +0,0 @@ -import { useRef, useState } from 'react'; -import type { GetServerSideProps, GetServerSidePropsContext } from 'next'; -import { verifyAdminToken } from '../../lib/admin-auth'; -import { Header } from '../components/header'; -import { Footer } from '../components/footer'; -import { Button } from '../components/ui/button'; -import { Input } from '../components/ui/input'; -import { PhoneInput } from '../components/ui/phone-input'; -import { Label } from '../components/ui/label'; -import { PageCard } from '../components/page-card'; - -async function postJson( - path: string, - body: unknown, - options: { timeout?: number; maxRetries?: number } = {} -): Promise { - const { timeout = 5000, maxRetries = 3 } = options; - let lastError: Error | null = null; - - function isHttpError(e: unknown): e is { status?: number; nonRetryable?: boolean } { - return typeof e === 'object' && e !== null && ('status' in e || 'nonRetryable' in e); - } - - for (let attempt = 1; attempt <= maxRetries; attempt++) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); - - try { - const res = await fetch(path, { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify(body), - signal: controller.signal, - }); - - clearTimeout(timeoutId); - - const json = (await res.json().catch(() => ({}))) as unknown; - - // Don't retry on 4xx errors - if (res.status >= 400 && res.status < 500) { - const errMsg = (json as { error?: string })?.error || 'Request failed'; - const e = new Error(errMsg) as Error & { status?: number; nonRetryable?: boolean }; - e.status = res.status; - e.nonRetryable = true; - throw e; - } - - if (!res.ok) { - // Retry on 5xx errors - if (attempt < maxRetries) { - const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); // Exponential backoff, max 5s - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } - const errMsg = (json as { error?: string })?.error || 'Request failed'; - throw new Error(errMsg); - } - - return json as T; - } catch (error: unknown) { - clearTimeout(timeoutId); - lastError = error instanceof Error ? error : new Error('Request failed'); - - // Don't retry on abort/timeout for non-network errors - if (error instanceof Error && error.name === 'AbortError') { - if (attempt < maxRetries) { - const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } - throw new Error('Request timed out'); - } - - // Retry only on network/fetch errors - const status = isHttpError(error) ? error.status : undefined; - const nonRetryable = isHttpError(error) ? Boolean(error.nonRetryable) : false; - if (!nonRetryable && (status === undefined || status < 400 || status >= 500) && attempt < maxRetries) { - const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } - - throw lastError; - } - } - - throw lastError || new Error('All retry attempts failed'); -} - -type Props = { authorized: boolean }; - -export default function AdminPage({ authorized: initialAuthorized }: Props) { - const [authorized, setAuthorized] = useState(initialAuthorized); - const [password, setPassword] = useState(''); - const [address, setAddress] = useState(''); - const [phone, setPhone] = useState(''); - const [clearIpCooldowns, setClearIpCooldowns] = useState(true); // Default to true since IP cooldowns often block testing - const [loading, setLoading] = useState(false); - const [message, setMessage] = useState(''); - const [error, setError] = useState(''); - const [targetIp, setTargetIp] = useState(''); - const [ipError, setIpError] = useState(''); - const clearInFlightRef = useRef(false); - - // Simple IPv4/IPv6 validators - const isValidIPv4 = (ip: string) => /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/.test(ip); - const isValidIPv6 = (ip: string) => { - try { - // Use the browser's URL parsing for a quick sanity check; fallback to simple test - // Note: Prefer server-side net.isIP for true validation on the API route. - return ip.includes(':') && /^[0-9a-fA-F:.%]+$/.test(ip); - } catch { - return false; - } - }; - const isValidIp = (ip: string) => isValidIPv4(ip) || isValidIPv6(ip); - - async function handleLogin() { - if (!password.trim()) { - setError('Admin password required'); - return; - } - - try { - setLoading(true); - setError(''); - await postJson<{ ok: boolean }>('/api/admin/login', { password }); - setPassword(''); - setAuthorized(true); - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : 'Login failed'; - setError(msg); - } finally { - setLoading(false); - } - } - - async function handleLogout() { - try { - setLoading(true); - await postJson<{ ok: boolean }>('/api/admin/logout', {}); - setAuthorized(false); - setMessage('Logged out'); - } catch (e) { - console.error('Logout failed:', e); - setError('Logout failed'); - } finally { - setLoading(false); - } - } - - async function handleClearUser() { - // Prevent accidental double-submits by ignoring while in flight - if (clearInFlightRef.current) return; - try { - setError(''); - setMessage(''); - setIpError(''); - if (clearIpCooldowns) { - const trimmed = targetIp.trim(); - const hasIdentity = (address.trim().length > 0) || (phone.trim().length > 0); - if (!hasIdentity && !trimmed) { - setError('Provide a target IP or an address/phone to clear IP cooldowns'); - return; - } - if (trimmed && !isValidIp(trimmed)) { - setIpError('Enter a valid IPv4 or IPv6 address'); - return; - } - } - clearInFlightRef.current = true; - setLoading(true); - - const result = await postJson<{ - ok: boolean; - message: string; - deletedKeys: number; - attemptedKeys: number; - debug?: { - keysAttempted: string[]; - addressKeys: number; - phoneKeys: number; - ipCooldownKeys: number; - }; - }>('/api/admin/clear-user', { - address: address.trim() || undefined, - phoneE164: phone.trim() || undefined, - clearIpCooldowns: clearIpCooldowns, - targetIp: clearIpCooldowns ? targetIp.trim() : undefined, - }); - - setMessage(result.message); - // Log debug info in development for transparency - if (result.debug) { - console.log('Admin clear debug info:', result.debug); - } - setAddress(''); - setPhone(''); - setTargetIp(''); - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : 'Failed to clear user'; - console.error('Admin clear user error:', e); - setError(msg); - } finally { - setLoading(false); - clearInFlightRef.current = false; - } - } - - return ( -
-
-
- {!authorized ? ( - -
- - setPassword(e.target.value)} - placeholder="Enter your password" - autoComplete="current-password" - /> -
- {error &&

{error}

} - -
- ) : ( - -
- - setAddress(e.target.value)} - placeholder="0x123..." - /> -
- -
- - setPhone(value || '')} - placeholder="Enter phone number" - defaultCountry="US" - /> -
- -
- setClearIpCooldowns(e.target.checked)} - className="h-4 w-4 rounded border-input text-primary" - /> - -
- - {clearIpCooldowns && ( -
- - { - const v = e.target.value; - setTargetIp(v); - const trimmed = v.trim(); - if (trimmed.length === 0) { - setIpError(''); - } else { - setIpError(isValidIp(trimmed) ? '' : 'Enter a valid IPv4 or IPv6 address'); - } - }} - placeholder="127.0.0.1 or ::1" - /> - {ipError &&

{ipError}

} -
- )} - - {message &&

{message}

} - {error &&

{error}

} - -
- - -
-
- )} -
-
-
- ); -} - -export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => { - const req = context.req; - // Next.js may not parse cookies on Node 19/Edge; ensure we handle both - const cookieHeader = req.headers?.cookie as string | undefined; - let token: string | undefined = req.cookies?.['admin_session']; - if (!token && typeof cookieHeader === 'string') { - const parts = cookieHeader.split(';'); - for (const p of parts) { - const [k, ...v] = p.trim().split('='); - if (k === 'admin_session') { - token = v.join('='); - break; - } - } - } - const authorized = verifyAdminToken(token); - return { props: { authorized } }; -}; diff --git a/web/src/pages/api/admin/clear-user.ts b/web/src/pages/api/admin/clear-user.ts deleted file mode 100644 index 70844a2..0000000 --- a/web/src/pages/api/admin/clear-user.ts +++ /dev/null @@ -1,180 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import net from 'node:net'; -import { z } from 'zod'; -import { kv } from '@vercel/kv'; -import { hashIdentifier } from '../../../../lib/hash'; -import { getHashedIpsForAddress, getHashedIpsForPhone } from '../../../../lib/kv'; -import { requireAdmin } from '../../../../lib/admin-auth'; -import { createRatelimit } from '../../../../lib/rate-limit'; - -const Body = z - .object({ - address: z.string().min(1).optional(), - phoneE164: z.string().min(5).optional(), - clearIpCooldowns: z.boolean().optional(), - targetIp: z.string().trim().optional(), - }) - .refine( - (data) => { - const hasIdentity = Boolean(data.address) || Boolean(data.phoneE164); - const wantsIpClear = Boolean(data.clearIpCooldowns); - const hasTargetIp = typeof data.targetIp === 'string' && data.targetIp.trim().length > 0; - // Valid if: provided address/phone for general clear, OR if requesting IP cooldown clear with either a target IP or identity to resolve from - return hasIdentity || (wantsIpClear && (hasTargetIp || hasIdentity)); - }, - { - message: 'Provide address or phoneE164, or include targetIp when clearing IP cooldowns', - path: ['address', 'phoneE164', 'targetIp'], - } - ); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); - - if (!requireAdmin(req, res)) return; - - const parse = Body.safeParse(req.body); - if (!parse.success) { - const firstIssue = parse.error.issues?.[0]; - const message = firstIssue?.message || 'Invalid body'; - return res.status(400).json({ error: message }); - } - - const { address, phoneE164, clearIpCooldowns, targetIp } = parse.data; - const adminIp = (req.headers['cf-connecting-ip'] as string) || (req.headers['x-real-ip'] as string) || req.socket.remoteAddress || 'unknown'; - // Lightweight rate limit for clear operations - const clearLimit = createRatelimit('rl:admin:clear', 10, 60); // 10 per minute per project - const { success: allowClear } = await clearLimit.limit('all'); - if (!allowClear) return res.status(429).json({ error: 'Too many clear requests' }); - - const keysToDelete: string[] = []; - - if (address) { - const lowerAddr = address.toLowerCase(); - const hashedAddr = hashIdentifier('addr', lowerAddr); - keysToDelete.push( - `mint:address:${hashedAddr}`, - `mint:address:${lowerAddr}` // Legacy plaintext fallback - ); - } - - if (phoneE164) { - const hashedPhone = hashIdentifier('phone', phoneE164); - keysToDelete.push( - `mint:phone:${hashedPhone}`, - `mint:phone:${phoneE164}`, // Legacy plaintext fallback - `sms:code:${hashedPhone}`, - `sms:code:${phoneE164}`, // Legacy plaintext fallback - `sms:verified:${hashedPhone}`, - `sms:verified:${phoneE164}`, // Legacy plaintext fallback - `cd:sms:phone:${hashedPhone}`, - `cd:sms:phone:${phoneE164}` // Legacy plaintext fallback - ); - } - - if (clearIpCooldowns) { - // If a specific target IP is provided, clear its cooldowns (hashed + legacy plaintext) - const targetIpRaw = typeof targetIp === 'string' ? targetIp.trim() : undefined; - if (targetIpRaw) { - const isValidIp = net.isIP(targetIpRaw) > 0; - if (!isValidIp) { - return res.status(400).json({ error: 'Invalid targetIp' }); - } - try { - const ts = new Date().toISOString(); - console.info('[AUDIT] admin_clear_ip_cooldowns_intent', { ts, actor: adminIp, targetIp: targetIpRaw }); - } catch {} - const hashedIp = hashIdentifier('ip', targetIpRaw); - keysToDelete.push( - `cd:mint:ip:${hashedIp}`, - `cd:mint:ip:${targetIpRaw}`, - `cd:sms:ip:${hashedIp}`, - `cd:sms:ip:${targetIpRaw}` - ); - } - - // Also support resolving hashed IPs associated to the provided address/phone - const hashedIps: Set = new Set(); - try { - if (phoneE164) { - for (const h of await getHashedIpsForPhone(phoneE164)) hashedIps.add(h); - } - if (address) { - for (const h of await getHashedIpsForAddress(address)) hashedIps.add(h); - } - } catch {} - if (hashedIps.size > 0) { - for (const ipHash of hashedIps) { - keysToDelete.push(`cd:mint:ip:${ipHash}`, `cd:sms:ip:${ipHash}`); - } - } else if (!targetIpRaw) { - // If no targetIp was supplied and no associations were found, return a helpful error - return res.status(404).json({ error: 'No associated IP cooldowns found for the provided address/phone' }); - } - } - - // Delete all keys and get actual deletion count - let actualDeletedCount = 0; - if (keysToDelete.length > 0) { - try { - const deleteResult = await kv.del(...keysToDelete); - actualDeletedCount = typeof deleteResult === 'number' ? deleteResult : 0; - } catch (err) { - // Audit failure (avoid PII; hash already applied in keys) - try { - const ts = new Date().toISOString(); - const actor = req.headers['cf-connecting-ip'] || req.headers['x-real-ip'] || req.socket.remoteAddress || 'unknown'; - console.error('[AUDIT] admin_clear_user_failed', { ts, actor, attemptedKeys: keysToDelete.length, error: 'kv_del_failed' }); - } catch {} - console.error('Failed to delete keys in clear-user', { address, phoneE164, keysToDelete, err }); - return res.status(500).json({ error: 'Failed to delete some keys' }); - } - } - - // Debug info for development (derive counts from keysToDelete) - const debugInfo = process.env.NODE_ENV === 'development' ? ((): { - keysAttempted: string[]; - addressKeys: number; - phoneKeys: number; - ipCooldownKeys: number; - } => { - let addressKeys = 0; - let phoneKeys = 0; - let ipCooldownKeys = 0; - if (address) { - // Two address keys are added when address is provided - addressKeys = keysToDelete.filter((k) => k.startsWith('mint:address:')).length; - } - if (phoneE164) { - // Eight phone-related keys are added when phoneE164 is provided - const phonePrefixes = ['mint:phone:', 'sms:code:', 'sms:verified:', 'cd:sms:phone:']; - phoneKeys = keysToDelete.filter((k) => phonePrefixes.some((p) => k.startsWith(p))).length; - } - if (clearIpCooldowns) { - // Four IP cooldown keys are added when clearIpCooldowns is set - const ipPrefixes = ['cd:mint:ip:', 'cd:sms:ip:']; - ipCooldownKeys = keysToDelete.filter((k) => ipPrefixes.some((p) => k.startsWith(p))).length; - } - return { - keysAttempted: keysToDelete, - addressKeys, - phoneKeys, - ipCooldownKeys, - }; - })() : undefined; - - // Audit success - try { - const ts = new Date().toISOString(); - const actor = req.headers['cf-connecting-ip'] || req.headers['x-real-ip'] || req.socket.remoteAddress || 'unknown'; - console.info('[AUDIT] admin_clear_user_success', { ts, actor, attemptedKeys: keysToDelete.length, deletedKeys: actualDeletedCount }); - } catch {} - - return res.status(200).json({ - ok: true, - deletedKeys: actualDeletedCount, - attemptedKeys: keysToDelete.length, - message: `Successfully cleared ${actualDeletedCount} existing entries (checked ${keysToDelete.length} possible keys)`, - debug: debugInfo - }); -} diff --git a/web/src/pages/api/admin/login.ts b/web/src/pages/api/admin/login.ts deleted file mode 100644 index a13e570..0000000 --- a/web/src/pages/api/admin/login.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { z } from 'zod'; -import { AdminAuth, setAdminSessionCookie, createAdminToken } from '../../../../lib/admin-auth'; -import { getAdminPassword } from '../../../../lib/env'; -import { getClientIp } from '../../../../lib/request-ip'; -import { createRatelimit, ratelimitKeyForIp } from '../../../../lib/rate-limit'; -import { timingSafeEqual, createHash } from 'crypto'; - -const Body = z.object({ password: z.string().min(1) }); - -const loginRatelimit = createRatelimit('rl:admin:login', 5, 60); // 5 per minute per IP - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); - - // Rate limit by IP - const ip = getClientIp(req); - const key = ratelimitKeyForIp(ip); - const { success } = await loginRatelimit.limit(key); - if (!success) return res.status(429).json({ error: 'Too many login attempts' }); - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: 'Invalid body' }); - - const envPassword = getAdminPassword(); - if (!envPassword) return res.status(500).json({ error: 'ADMIN_PASSWORD not configured' }); - - // timing-safe compare of hashed values to normalize length - const providedHash = createHash('sha256').update(parse.data.password).digest(); - const expectedHash = createHash('sha256').update(envPassword).digest(); - const ok = timingSafeEqual(providedHash, expectedHash); - if (!ok) { - // Audit failed login attempt (do not log secrets) - try { - const ts = new Date().toISOString(); - const ipForLog = ip; - console.warn('[AUDIT] admin_login_failed', { ts, ip: ipForLog, reason: 'Invalid password' }); - } catch {} - return res.status(401).json({ error: 'Invalid password' }); - } - - const token = createAdminToken(); - setAdminSessionCookie(res, token, AdminAuth.DEFAULT_SESSION_TTL_SECONDS); - return res.status(200).json({ ok: true }); -} - - diff --git a/web/src/pages/api/admin/logout.ts b/web/src/pages/api/admin/logout.ts deleted file mode 100644 index 1ee7ba4..0000000 --- a/web/src/pages/api/admin/logout.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { clearAdminSessionCookie } from '../../../../lib/admin-auth'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); - // Strict CSRF protection: require Origin and validate protocol+host - const origin = req.headers['origin'] as string | undefined; - const host = req.headers['host']; - const expectedHost = typeof host === 'string' ? host : undefined; - if (!origin || !expectedHost) return res.status(403).json({ error: 'Invalid origin' }); - try { - const u = new URL(origin); - const isProd = process.env.NODE_ENV === 'production'; - if ((isProd && u.protocol !== 'https:') || u.host !== expectedHost) { - return res.status(403).json({ error: 'Invalid origin' }); - } - } catch { - return res.status(403).json({ error: 'Invalid origin' }); - } - - clearAdminSessionCookie(res); - return res.status(200).json({ ok: true }); -} - - diff --git a/web/src/pages/api/check-eligibility.ts b/web/src/pages/api/check-eligibility.ts deleted file mode 100644 index 84fb2fe..0000000 --- a/web/src/pages/api/check-eligibility.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { z } from 'zod'; -import { hasMinted, hasPhoneMinted, isPhoneVerified } from '../../../lib/kv'; -import { globalIpRatelimit } from '../../../lib/rate-limit'; -import { getClientIp } from '../../../lib/request-ip'; -import { hashIdentifier } from '../../../lib/hash'; - -const Body = z.object({ - address: z.string().min(1), - phoneE164: z.string().min(5), - authorAddress: z.string().min(1).optional(), -}); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); - - // Global IP rate limiting - const ip = getClientIp(req); - const hashedIp = hashIdentifier('ip', ip); - const { success, limit, reset, remaining } = await globalIpRatelimit.limit(hashedIp); - res.setHeader('X-RateLimit-Limit', String(limit)); - res.setHeader('X-RateLimit-Remaining', String(remaining)); - res.setHeader('X-RateLimit-Reset', String(reset)); - if (!success) return res.status(429).json({ error: 'Too many requests' }); - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: 'Invalid body' }); - const { address, phoneE164 } = parse.data; - - const [mintedAddr, mintedPhone, verified] = await Promise.all([ - hasMinted(address), - hasPhoneMinted(phoneE164), - isPhoneVerified(phoneE164), - ]); - - const eligible = !mintedAddr && !mintedPhone && verified; - return res.status(200).json({ eligible, mintedAddr, mintedPhone, verified }); -} - - diff --git a/web/src/pages/api/debug/code.ts b/web/src/pages/api/debug/code.ts deleted file mode 100644 index de2e775..0000000 --- a/web/src/pages/api/debug/code.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { z } from "zod"; -import { readSmsCode } from "../../../../lib/kv"; -import { isProductionRuntime, shouldUseKvFallback } from "../../../../lib/sms"; - -const Body = z.object({ - phoneE164: z.string().min(5), -}); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== "POST") return res.status(405).json({ error: "Method not allowed" }); - if (isProductionRuntime()) return res.status(404).json({ error: "Not found" }); - - const hdr = (req.headers["x-smoke-test-token"] as string) || ""; - if (!shouldUseKvFallback(hdr)) { - return res.status(401).json({ ok: false, match: false }); - } - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: "Invalid body" }); - const { phoneE164 } = parse.data; - - const code = await readSmsCode(phoneE164); - return res.status(200).json({ ok: true, code }); -} diff --git a/web/src/pages/api/mint.ts b/web/src/pages/api/mint.ts deleted file mode 100644 index 3bc2721..0000000 --- a/web/src/pages/api/mint.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { z } from 'zod'; -import { hasMinted, hasPhoneMinted, isPhoneVerified, markMinted, addIpAssociationForPhone, addIpAssociationForAddress } from '../../../lib/kv'; -import { getClientIp } from '../../../lib/request-ip'; -import { isMintIpInCooldown, setMintIpCooldown } from '../../../lib/cooldowns'; -import { env, requireEnv } from '../../../lib/env'; -import { MintPassV1Abi } from '../../../lib/abi'; -import { Wallet, JsonRpcProvider, Contract } from 'ethers'; -import { hashIdentifier } from '../../../lib/hash'; -import { globalIpRatelimit } from '../../../lib/rate-limit'; - -const Body = z.object({ - address: z.string().min(1), - phoneE164: z.string().min(5), - tokenType: z.number().int().min(0).max(65535).optional(), - authorAddress: z.string().min(1), -}); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: 'Invalid body' }); - const { address, phoneE164, tokenType = 0 } = parse.data; - const ip = getClientIp(req); - - // Global IP rate limiting - const hashedIp = hashIdentifier('ip', ip); - const { success, limit, reset, remaining } = await globalIpRatelimit.limit(hashedIp); - res.setHeader('X-RateLimit-Limit', String(limit)); - res.setHeader('X-RateLimit-Remaining', String(remaining)); - res.setHeader('X-RateLimit-Reset', String(reset)); - if (!success) return res.status(429).json({ error: 'Too many requests' }); - - const [mintedAddr, mintedPhone, verified] = await Promise.all([ - hasMinted(address), - hasPhoneMinted(phoneE164), - isPhoneVerified(phoneE164), - ]); - - if (!verified) return res.status(400).json({ error: 'Phone not verified' }); - if (mintedAddr || mintedPhone) return res.status(400).json({ error: 'Already minted' }); - - // Optional IP-based mint cooldown - if (await isMintIpInCooldown(ip)) { - return res.status(429).json({ error: 'Mint cooldown active for this IP' }); - } - - // Derive ISO country code from edge headers if present (available for future geo-blocking) - // const hdrCountry = (req.headers['x-vercel-ip-country'] as string) || ''; - // const country2 = (hdrCountry || '').toUpperCase(); // Available for future use - - // If on-chain envs are configured, perform on-chain mint; otherwise, stub-mark as minted - let txHash: string | null = null; - if ( - env.MINTER_PRIVATE_KEY && - env.BASE_SEPOLIA_RPC_URL && - env.MINTPASSV1_ADDRESS_BASE_SEPOLIA - ) { - try { - const provider = new JsonRpcProvider(requireEnv('BASE_SEPOLIA_RPC_URL')); - const wallet = new Wallet(requireEnv('MINTER_PRIVATE_KEY'), provider); - const contract = new Contract(requireEnv('MINTPASSV1_ADDRESS_BASE_SEPOLIA'), MintPassV1Abi, wallet) as unknown as { - estimateGas: { mint: (to: string, tokenType: number) => Promise }; - mint: ( - to: string, - tokenType: number, - overrides?: { gasLimit?: bigint } - ) => Promise<{ hash: string; wait: () => Promise<{ hash?: string; status?: number; transactionHash?: string }>; }>; - }; - // Ethers v6 compatibility: estimateGas helpers may be stripped depending on build - // Use a narrow contract type and fall back to provider auto-estimation when unavailable - type MintContract = { - estimateGas?: { mint?: (to: string, tokenType: number) => Promise }; - mint: ( - to: string, - tokenType: number, - overrides?: { gasLimit?: bigint } - ) => Promise<{ hash: string; wait: () => Promise<{ hash?: string; status?: number; transactionHash?: string }>; }>; - }; - const mintContract = contract as unknown as MintContract; - let gasOverrides: { gasLimit?: bigint } | undefined; - try { - const estimated = await mintContract.estimateGas?.mint?.(address, tokenType); - if (typeof estimated === 'bigint') { - gasOverrides = { gasLimit: estimated + (estimated / BigInt(5)) }; - } - } catch (estErr) { - console.warn('[mint] estimateGas.mint failed; proceeding without overrides', { - address, - tokenType, - err: estErr instanceof Error ? estErr.message : String(estErr), - }); - gasOverrides = undefined; - } - const tx = gasOverrides - ? await mintContract.mint(address, tokenType, gasOverrides) - : await mintContract.mint(address, tokenType); - const receipt = await tx.wait(); - const status = receipt.status; - if (typeof status === 'number' && status !== 1) { - console.error('[mint] Receipt status not successful', { hash: tx.hash, address, tokenType, status }); - return res.status(500).json({ error: 'On-chain mint failed (status)' }); - } - txHash = receipt?.transactionHash ?? receipt?.hash ?? tx.hash; - } catch (err) { - console.error('[mint] On-chain mint error', { - address, - tokenType, - rpc: env.BASE_SEPOLIA_RPC_URL?.slice(0, 16), - contract: env.MINTPASSV1_ADDRESS_BASE_SEPOLIA, - err, - }); - return res.status(500).json({ error: err instanceof Error ? err.message : 'On-chain mint failed' }); - } - } - - await markMinted(address, phoneE164); - await setMintIpCooldown(ip); - // Index hashed IP associations for phone and address (mint attempt) - try { - await Promise.all([ - addIpAssociationForPhone(phoneE164, ip), - addIpAssociationForAddress(address, ip), - ]); - } catch {} - - return res.status(200).json({ ok: true, txHash, tokenType }); -} - - diff --git a/web/src/pages/api/pre-check-eligibility.ts b/web/src/pages/api/pre-check-eligibility.ts deleted file mode 100644 index 2c4f6af..0000000 --- a/web/src/pages/api/pre-check-eligibility.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { z } from 'zod'; -import { hasMinted, hasPhoneMinted } from '../../../lib/kv'; -import { globalIpRatelimit } from '../../../lib/rate-limit'; -import { getClientIp } from '../../../lib/request-ip'; -import { hashIdentifier } from '../../../lib/hash'; - -const Body = z.object({ - address: z.string().min(1), - phoneE164: z.string().min(5), -}); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); - - // Global IP rate limiting - const ip = getClientIp(req); - const hashedIp = hashIdentifier('ip', ip); - const { success, limit, reset, remaining } = await globalIpRatelimit.limit(hashedIp); - res.setHeader('X-RateLimit-Limit', String(limit)); - res.setHeader('X-RateLimit-Remaining', String(remaining)); - res.setHeader('X-RateLimit-Reset', String(reset)); - if (!success) return res.status(429).json({ error: 'Too many requests' }); - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: 'Invalid body' }); - const { address, phoneE164 } = parse.data; - - const [mintedAddr, mintedPhone] = await Promise.all([ - hasMinted(address), - hasPhoneMinted(phoneE164), - ]); - - const eligible = !mintedAddr && !mintedPhone; - return res.status(200).json({ - eligible, - mintedAddr, - mintedPhone, - reason: !eligible - ? mintedAddr - ? 'Address has already minted a MintPass NFT' - : 'Phone number has already been used to mint a MintPass NFT' - : null - }); -} diff --git a/web/src/pages/api/sms/send.ts b/web/src/pages/api/sms/send.ts deleted file mode 100644 index 73b2f1c..0000000 --- a/web/src/pages/api/sms/send.ts +++ /dev/null @@ -1,174 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { z } from "zod"; -import { globalIpRatelimit } from "../../../../lib/rate-limit"; -import { - saveSmsCode, - addIpAssociationForPhone, - addIpAssociationForAddress, -} from "../../../../lib/kv"; -import { assessIpReputation } from "../../../../lib/ip-reputation"; -import { analyzePhone } from "../../../../lib/phone-intel"; -import { getClientIp } from "../../../../lib/request-ip"; -import { - isSmsSendInCooldown, - setSmsSendCooldown, - getSmsSendCooldownRemaining, -} from "../../../../lib/cooldowns"; -import { - isProductionRuntime, - isVerifyConfigured, - startSmsVerification, - shouldUseKvFallback, - type SmsProviderError, -} from "../../../../lib/sms"; -import { hashIdentifier } from "../../../../lib/hash"; - -const Body = z.object({ - phoneE164: z.string().min(5), - address: z.string().min(1), -}); - -function generateCode(): string { - return Math.floor(100000 + Math.random() * 900000).toString(); -} - -async function readCooldownRemaining(ip: string, phoneE164: string): Promise { - try { - return await getSmsSendCooldownRemaining(ip, phoneE164); - } catch { - return 0; - } -} - -function toClientProviderError(providerError: SmsProviderError | undefined) { - if (!providerError) return undefined; - return { - provider: providerError.provider, - status: providerError.status, - errorCode: providerError.errorCode, - errorMessage: providerError.errorMessage, - // Backwards-compatible aliases for existing client-side error rendering. - code: providerError.errorCode, - message: providerError.errorMessage, - }; -} - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== "POST") return res.status(405).json({ error: "Method not allowed" }); - - // Rate limit by IP - const ip = getClientIp(req); - const hashedIp = hashIdentifier("ip", ip); - const { success, limit, reset, remaining } = await globalIpRatelimit.limit(hashedIp); - res.setHeader("X-RateLimit-Limit", String(limit)); - res.setHeader("X-RateLimit-Remaining", String(remaining)); - res.setHeader("X-RateLimit-Reset", String(reset)); - if (!success) return res.status(429).json({ error: "Too many requests" }); - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: "Invalid body" }); - const { phoneE164, address } = parse.data; - - // Reject VPNs/proxies/cloud-provider IPs if IP intelligence is configured - const rep = await assessIpReputation(req); - if (rep.isVpnOrProxy || rep.isCloudProvider) { - return res.status(400).json({ error: "VPNs and proxies are not allowed" }); - } - - // Cooldown checks per IP and phone - if (await isSmsSendInCooldown(ip, phoneE164)) { - let remainingSeconds = 0; - try { - remainingSeconds = await getSmsSendCooldownRemaining(ip, phoneE164); - } catch { - remainingSeconds = 0; - } - const remainingTime = remainingSeconds > 0 ? `${remainingSeconds}s` : ""; - const errorMessage = remainingTime - ? `Please wait ${remainingTime} before requesting another code` - : "Please wait before requesting another code"; - if (remainingSeconds > 0) res.setHeader("Retry-After", String(remainingSeconds)); - return res.status(429).json({ error: errorMessage, cooldownSeconds: remainingSeconds }); - } - - // Reject disposable/VOIP/high-risk numbers if phone intelligence is configured - const pcheck = await analyzePhone(phoneE164); - // Allow high-risk numbers only in Preview environment to enable testing - const isPreviewEnv = (process.env.VERCEL_ENV || "").toLowerCase() === "preview"; - if (!isPreviewEnv && pcheck.isHighRisk) { - return res.status(400).json({ error: "Phone number not eligible" }); - } - - const smokeHeader = (req.headers["x-smoke-test-token"] as string) || ""; - const useKvFallback = shouldUseKvFallback(smokeHeader); - const mode: "verify" | "kv-fallback" = useKvFallback ? "kv-fallback" : "verify"; - if (mode === "verify" && !isVerifyConfigured()) { - const errorMessage = isProductionRuntime() - ? "SMS verification service unavailable." - : "SMS verification service unavailable. Use the smoke fallback token in non-production."; - return res.status(503).json({ error: errorMessage }); - } - - let fallbackCode: string | undefined; - if (mode === "kv-fallback") { - fallbackCode = generateCode(); - await saveSmsCode(phoneE164, fallbackCode); - } - - // Keep cooldown writes at send time for both verify and fallback modes. - await setSmsSendCooldown(ip, phoneE164); - - // Index hashed IP associations for phone and address for admin tooling - try { - await Promise.all([ - addIpAssociationForPhone(phoneE164, ip), - addIpAssociationForAddress(address, ip), - ]); - } catch {} - - if (mode === "kv-fallback") { - return res.status(200).json({ - ok: true, - mode, - ...(fallbackCode ? { debugCode: fallbackCode } : {}), - }); - } - - const result = await startSmsVerification(phoneE164, { - timeoutMs: 5000, - maxRetries: 1, - baseDelayMs: 300, - deviceIp: ip, - }); - - if (!result.ok) { - const remainingSeconds = await readCooldownRemaining(ip, phoneE164); - const providerError = toClientProviderError(result.providerError); - const isClientError = - typeof providerError?.status === "number" && - providerError.status >= 400 && - providerError.status < 500; - - const statusCode = isClientError ? 400 : 502; - const errorMessage = isClientError - ? "Unable to deliver SMS to this number. Service is not available for this destination yet." - : "SMS provider error. Please try again later."; - - // Log minimal diagnostic info with hashed phone (no PII or OTP values). - try { - console.warn("SMS send failed", { - phone: hashIdentifier("phone", phoneE164), - status: providerError?.status, - provider: providerError?.provider, - code: providerError?.errorCode, - message: providerError?.errorMessage, - }); - } catch {} - - return res - .status(statusCode) - .json({ error: errorMessage, cooldownSeconds: remainingSeconds, providerError }); - } - - return res.status(200).json({ ok: true, sid: result.sid, mode }); -} diff --git a/web/src/pages/api/sms/verify.ts b/web/src/pages/api/sms/verify.ts deleted file mode 100644 index d63caf8..0000000 --- a/web/src/pages/api/sms/verify.ts +++ /dev/null @@ -1,120 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { z } from "zod"; -import { - clearSmsCode, - markPhoneVerified, - readSmsCode, - addIpAssociationForPhone, -} from "../../../../lib/kv"; -import { globalIpRatelimit } from "../../../../lib/rate-limit"; -import { getClientIp } from "../../../../lib/request-ip"; -import { hashIdentifier } from "../../../../lib/hash"; -import { - checkSmsVerification, - isProductionRuntime, - isVerifyConfigured, - shouldUseKvFallback, - type SmsProviderError, -} from "../../../../lib/sms"; - -const Body = z.object({ - phoneE164: z.string().min(5), - code: z.string().length(6), -}); - -function toClientProviderError(providerError: SmsProviderError | undefined) { - if (!providerError) return undefined; - return { - provider: providerError.provider, - status: providerError.status, - errorCode: providerError.errorCode, - errorMessage: providerError.errorMessage, - // Backwards-compatible aliases for existing client-side error rendering. - code: providerError.errorCode, - message: providerError.errorMessage, - }; -} - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== "POST") return res.status(405).json({ error: "Method not allowed" }); - - // Global IP rate limiting - const ip = getClientIp(req); - const hashedIp = hashIdentifier("ip", ip); - const { success, limit, reset, remaining } = await globalIpRatelimit.limit(hashedIp); - res.setHeader("X-RateLimit-Limit", String(limit)); - res.setHeader("X-RateLimit-Remaining", String(remaining)); - res.setHeader("X-RateLimit-Reset", String(reset)); - if (!success) return res.status(429).json({ error: "Too many requests" }); - - const parse = Body.safeParse(req.body); - if (!parse.success) return res.status(400).json({ error: "Invalid body" }); - const { phoneE164, code } = parse.data; - - const smokeHeader = (req.headers["x-smoke-test-token"] as string) || ""; - const useKvFallback = shouldUseKvFallback(smokeHeader); - - if (useKvFallback) { - const stored = await readSmsCode(phoneE164); - if (!stored) { - return res.status(400).json({ error: "Code expired or invalid. Request a new code." }); - } - - // Normalize both sides to string to tolerate provider/SDK returning numbers - const storedStr = typeof stored === "string" ? stored : String(stored); - const codeStr = String(code); - if (storedStr !== codeStr) { - return res - .status(400) - .json({ error: "Invalid code", debug: { posted: codeStr, stored: storedStr } }); - } - } else { - if (!isVerifyConfigured()) { - const errorMessage = isProductionRuntime() - ? "SMS verification service unavailable." - : "SMS verification service unavailable. Use the smoke fallback token in non-production."; - return res.status(503).json({ error: errorMessage }); - } - - const verifyResult = await checkSmsVerification(phoneE164, code, { - timeoutMs: 5000, - maxRetries: 1, - baseDelayMs: 300, - deviceIp: ip, - }); - if (!verifyResult.ok) { - if (verifyResult.reason === "invalid_code") { - return res.status(400).json({ error: "Invalid code" }); - } - if (verifyResult.reason === "expired_or_invalid") { - return res.status(400).json({ error: "Code expired or invalid. Request a new code." }); - } - - const providerError = toClientProviderError(verifyResult.providerError); - try { - console.warn("SMS verify failed", { - phone: hashIdentifier("phone", phoneE164), - status: providerError?.status, - provider: providerError?.provider, - code: providerError?.errorCode, - message: providerError?.errorMessage, - }); - } catch {} - - return res.status(502).json({ - error: "SMS provider error. Please try again later.", - providerError, - }); - } - } - - await markPhoneVerified(phoneE164); - // Always clear fallback KV code after successful verification. - await clearSmsCode(phoneE164); - // Index hashed IP association on verify as well (covers flows where send indexing failed) - try { - const ip = getClientIp(req); - await addIpAssociationForPhone(phoneE164, ip); - } catch {} - return res.status(200).json({ ok: true }); -} diff --git a/web/src/pages/index.tsx b/web/src/pages/index.tsx deleted file mode 100644 index 5d1e1a8..0000000 --- a/web/src/pages/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Link from 'next/link'; -import { useRouter } from 'next/router'; -import { Header } from '../components/header'; -import { Footer } from '../components/footer'; -import { PageCard } from '../components/page-card'; -import { RainbowButton } from '../components/magicui/rainbow-button'; - -export default function Home() { - const router = useRouter(); - const hideNft = router.query['hide-nft'] === 'true'; - - return ( -
-
-
- -

- {hideNft - ? "Verify your phone and get authenticated for secure access." - : "Verify your phone and receive your authentication NFT." - } -

- - Start Verification - -
-
-
-
- ); -} diff --git a/web/src/pages/privacy-policy.tsx b/web/src/pages/privacy-policy.tsx deleted file mode 100644 index 3b8704c..0000000 --- a/web/src/pages/privacy-policy.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import Link from 'next/link'; -import { Header } from '../components/header'; -import { Footer } from '../components/footer'; -import { PageCard } from '../components/page-card'; - -export default function PrivacyPolicy() { - return ( -
-
-
- -

- This document describes how the MintPass website and backend handle data. MintPass is operated by{' '} - - Bitsocial - - {' '}This is provided for transparency and is not legal advice. -

- -
-

Summary

-
    -
  • No marketing analytics. No sale of personal data.
  • -
  • Minimal operational data only: verification codes, verification markers, rate-limit and cooldown keys, and mint association records.
  • -
  • Phone numbers and IPs are stored as HMAC-SHA256 digests when a hash pepper is configured.
  • -
  • SMS codes are short‑lived; verification markers expire shortly after use.
  • -
  • Mint association (wallet ↔ phone) persists to enforce anti-Sybil guarantees.
  • -
-
- -
-

Data We Process

-
    -
  • - Phone number (E.164): used to send and verify one‑time SMS codes. Stored as a hashed key when HASH_PEPPER is set. The code value itself is temporary. -
  • -
  • - IP address: used for global rate limits and cooldowns. Stored as a hashed key when HASH_PEPPER is set. -
  • -
  • - Ethereum address: used to check and record whether a wallet has minted an authentication NFT. -
  • -
  • - Optional IP/phone reputation signals if you enable providers (e.g., VPN/Proxy or VOIP checks). These are used to block abuse and are not retained beyond the result state. -
  • -
-
- -
-

Retention

-
    -
  • SMS codes: ~5 minutes TTL.
  • -
  • SMS verified markers: short TTL (minutes) after successful verification.
  • -
  • Rate‑limit and cooldown keys (IP/phone): short TTL as configured in the environment.
  • -
  • Mint association (wallet and phone): retained to prevent duplicate mints and preserve anti-Sybil guarantees.
  • -
-
- -
-

“No‑logs” Clarification

-

- MintPass uses a minimal‑data model rather than a strict "no‑logs" policy. We avoid server request logging for analytics and do not profile users. However, we must keep short‑lived operational keys (e.g., OTPs, verification markers, rate‑limits/cooldowns) and a persistent mint association record to protect against abuse. This means MintPass is not "strict no‑logs," but it is "no analytics, minimal operational data." -

-
- -
-

Infrastructure

-

- The site is hosted on Vercel. Operational state is stored in Upstash Redis (KV). Regions are configured to US‑East (iad1) in the reference setup. SMS delivery is via a provider such as Twilio if configured. The code is open source; deployments are configured to align with the repository, but users should consider the trust assumptions inherent in any hosted service. -

-
- -
-

Your Choices

-
    -
  • You can choose not to use MintPass, in which case no data is collected.
  • -
  • You can request deletion of verification cooldowns and related state via the admin tooling where appropriate; note that mint association records are retained by design.
  • -
-
- -
-

Contact

-

- For questions about this policy, open an issue on the project repository or contact{' '} - - Bitsocial - - . See the project README for details. -

-
- -
- - Terms and Conditions - -
-
-
-
-
- ); -} - - diff --git a/web/src/pages/request/[eth-address].tsx b/web/src/pages/request/[eth-address].tsx deleted file mode 100644 index b58bfe0..0000000 --- a/web/src/pages/request/[eth-address].tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { useRouter } from 'next/router'; -import type { GetServerSideProps } from 'next'; -import RequestPage from '../request'; - -type RequestWithEthAddressProps = { - isNorthAmerica: boolean; -}; - -export default function RequestWithEthAddress({ isNorthAmerica }: RequestWithEthAddressProps) { - const router = useRouter(); - const { 'eth-address': ethAddress } = router.query as { 'eth-address'?: string }; - return ; -} - -export const getServerSideProps: GetServerSideProps = async (ctx) => { - const countryHeader = (ctx.req.headers['x-vercel-ip-country'] as string) || ''; - const country = countryHeader.toUpperCase(); - const isNorthAmerica = country === 'US' || country === 'CA'; - return { props: { isNorthAmerica } }; -}; diff --git a/web/src/pages/request/index.tsx b/web/src/pages/request/index.tsx deleted file mode 100644 index e27d7d1..0000000 --- a/web/src/pages/request/index.tsx +++ /dev/null @@ -1,501 +0,0 @@ -import { useEffect, useState } from "react"; -import type { GetServerSideProps } from "next"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { Button } from "../../components/ui/button"; -import { Input } from "../../components/ui/input"; -import { PhoneInput, UNSUPPORTED_COUNTRIES } from "../../components/ui/phone-input"; -import * as RPNInput from "react-phone-number-input"; -import { - InputOTP, - InputOTPGroup, - InputOTPSlot, - InputOTPSeparator, -} from "../../components/ui/input-otp"; -import { Label } from "../../components/ui/label"; -import { Header } from "../../components/header"; -import { Footer } from "../../components/footer"; -import { PageCard } from "../../components/page-card"; -import { ConfettiCelebration } from "../../components/confetti-celebration"; -import { ExternalLink } from "lucide-react"; - -async function postJson(path: string, body: unknown, opts?: { timeoutMs?: number }): Promise { - const timeoutMs = - typeof opts?.timeoutMs === "number" && opts.timeoutMs > 0 ? opts.timeoutMs : 10000; - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); - try { - const res = await fetch(path, { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - signal: controller.signal, - }); - const json = (await res.json().catch(() => ({}))) as unknown; - if (!res.ok) { - const data = json as { error?: string; cooldownSeconds?: unknown; providerError?: unknown }; - const errMsg = data?.error || "Request failed"; - const error = new Error(errMsg) as Error & { - cooldownSeconds?: number; - providerError?: unknown; - status?: number; - }; - error.status = res.status; - const cooldownSecondsValue = data?.cooldownSeconds; - if (typeof cooldownSecondsValue === "number") { - error.cooldownSeconds = cooldownSecondsValue; - } - const providerErrorValue = (data as { providerError?: unknown })?.providerError; - if (providerErrorValue && typeof providerErrorValue === "object") { - (error as { providerError?: unknown }).providerError = providerErrorValue; - } - throw error; - } - return json as T; - } catch (e: unknown) { - const name = (e as { name?: string } | null)?.name || ""; - if (name === "AbortError") { - throw new Error("Network timeout. Please try again."); - } - throw e as Error; - } finally { - clearTimeout(timer); - } -} - -export default function RequestPage({ - prefilledAddress = "", - isNorthAmerica = false, -}: { - prefilledAddress?: string; - isNorthAmerica?: boolean; -}) { - const router = useRouter(); - const [address, setAddress] = useState(""); - const [phone, setPhone] = useState(""); - const [code, setCode] = useState(""); - const [step, setStep] = useState<"enter" | "code" | "done">("enter"); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - const [txHash, setTxHash] = useState(null); - const [cooldownSeconds, setCooldownSeconds] = useState(0); - const [selectedCountry, setSelectedCountry] = useState(undefined); - - // Parse query parameters for demo customization - const hideNft = router.query["hide-nft"] === "true"; - const hideAddress = router.query["hide-address"] !== "false"; // defaults to true - - // Determine if address is prefilled from props or URL - const addressFromQuery = (router.query["eth-address"] as string) || ""; - const isAddressPrefilled = !!(prefilledAddress || addressFromQuery); - const prefilledAddressValue = prefilledAddress || addressFromQuery; - - // Determine if address input should be shown - const shouldShowAddressInput = !hideAddress || !isAddressPrefilled; - - useEffect(() => { - // Hydrate address from query if not passed as prop - const initial = prefilledAddressValue; - if (initial) setAddress(initial); - }, [prefilledAddressValue]); - - // Navigation protection during SMS verification - const isVerificationInProgress = step === "code" || (loading && step === "enter"); - - // Protect against tab closing during verification - useEffect(() => { - const handleBeforeUnload = (e: BeforeUnloadEvent) => { - if (isVerificationInProgress) { - e.preventDefault(); - e.returnValue = "You have an SMS verification in progress. Are you sure you want to leave?"; - return e.returnValue; - } - }; - - window.addEventListener("beforeunload", handleBeforeUnload); - return () => window.removeEventListener("beforeunload", handleBeforeUnload); - }, [isVerificationInProgress]); - - // Protect against navigation during verification - useEffect(() => { - const handleRouteChangeStart = (url: string) => { - if (isVerificationInProgress && !url.startsWith("/request")) { - const confirmed = window.confirm( - "You have an SMS verification in progress. Are you sure you want to leave?", - ); - if (!confirmed) { - router.events.emit("routeChangeError"); - throw "Route change aborted by user"; - } - } - }; - - router.events.on("routeChangeStart", handleRouteChangeStart); - return () => router.events.off("routeChangeStart", handleRouteChangeStart); - }, [router.events, isVerificationInProgress]); - - // Countdown timer effect - useEffect(() => { - if (cooldownSeconds > 0) { - const timer = setInterval(() => { - setCooldownSeconds((prev) => { - if (prev <= 1) { - setError(""); // Clear error when countdown reaches 0 - return 0; - } - return prev - 1; - }); - }, 1000); - - return () => clearInterval(timer); - } - }, [cooldownSeconds]); - - // Simple calculation during rendering (no need for useMemo) - // const canVerify = code.trim().length === 6; - - // Check if selected country is supported by SMS provider - const isCountrySupported = - !selectedCountry || !UNSUPPORTED_COUNTRIES.includes(selectedCountry as RPNInput.Country); - - function handleOtpComplete(value: string) { - if (loading) return; - const normalized = (value || "").trim(); - if (normalized.length === 6) { - setCode(normalized); - void handleVerifyAndMint(); - } - } - - async function handleSendCodeClick() { - // Determine current address based on whether input is shown and has value - const currentAddress = shouldShowAddressInput - ? address.trim() || prefilledAddressValue - : prefilledAddressValue; - - // Validate address - if (!currentAddress || currentAddress.trim().length === 0) { - setError("Please enter an Ethereum address"); - return; - } - - // Validate phone - if (!phone || phone.length < 5) { - setError("Please enter a valid phone number"); - return; - } - - // All validations passed, check eligibility and send code - await handleCheckEligibilityAndSendCode(currentAddress); - } - - async function handleCheckEligibilityAndSendCode(currentAddress: string) { - try { - setLoading(true); - setError(""); - - // First check eligibility - const result = await postJson<{ - eligible: boolean; - reason?: string; - }>("/api/pre-check-eligibility", { address: currentAddress.trim(), phoneE164: phone.trim() }); - - if (!result.eligible) { - if (result.reason) { - setError(result.reason); - } else { - setError("Not eligible to mint"); - } - return; - } - - // If eligible, send SMS code - await postJson<{ - ok: true; - sid?: string; - mode: "verify" | "kv-fallback"; - debugCode?: string; - }>("/api/sms/send", { phoneE164: phone, address: currentAddress }, { timeoutMs: 15000 }); - setStep("code"); - } catch (e: unknown) { - if (e instanceof Error) { - const errorWithCooldown = e as Error & { - status?: number; - cooldownSeconds?: number; - providerError?: { - provider?: string; - status?: number; - code?: number | string; - message?: string; - errorCode?: number | string; - errorMessage?: string; - }; - }; - const pe = errorWithCooldown.providerError; - const providerCode = pe?.errorCode ?? pe?.code; - const providerMessage = pe?.errorMessage ?? pe?.message; - // Prefer provider message/code if present - if (pe && (providerMessage || providerCode !== undefined)) { - const prov = pe.provider ? `${pe.provider} ` : ""; - const codePart = - providerCode !== undefined && providerCode !== null - ? ` (code ${String(providerCode)})` - : ""; - const detail = `${prov}${providerMessage || "delivery error"}${codePart}`; - setError(`${e.message} — ${detail}`); - } else if ( - errorWithCooldown.status === 429 && - typeof errorWithCooldown.cooldownSeconds === "number" && - errorWithCooldown.cooldownSeconds > 0 - ) { - // Only show cooldown countdown on explicit 429 - setCooldownSeconds(errorWithCooldown.cooldownSeconds); - setError(e.message); - } else { - // Generic error fallback - setError(e.message); - } - } else { - setError("Failed to send code"); - } - } finally { - setLoading(false); - } - } - - async function handleVerifyAndMint() { - try { - setError(""); - setLoading(true); - const currentAddress = shouldShowAddressInput - ? address.trim() || prefilledAddressValue - : prefilledAddressValue; - - await postJson("/api/sms/verify", { phoneE164: phone, code }); - const elig = await postJson<{ - eligible: boolean; - mintedAddr: boolean; - mintedPhone: boolean; - verified: boolean; - }>("/api/check-eligibility", { address: currentAddress, phoneE164: phone }); - - if (!elig.eligible) { - if (!elig.verified) { - setError("Phone number not verified"); - } else if (elig.mintedAddr) { - setError("Address has already minted the authentication NFT"); - } else if (elig.mintedPhone) { - setError("Phone number has already been used to mint the authentication NFT"); - } else { - setError("Not eligible to mint"); - } - return; - } - const mint = await postJson<{ ok: boolean; txHash?: string }>("/api/mint", { - address: currentAddress, - phoneE164: phone, - authorAddress: currentAddress, - }); - setTxHash(mint.txHash || null); - setStep("done"); - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : "Failed to verify or mint"; - setError(msg); - } finally { - setLoading(false); - } - } - - return ( -
-
- {step === "done" && } -
- - {step === "enter" && ( - - )} - - ) : undefined - } - > - {step === "enter" && ( -
- {shouldShowAddressInput && ( -
- - { - setAddress(e.target.value); - // Clear error and cooldown on input change - setCooldownSeconds(0); - setError(""); - }} - placeholder="0x..." - disabled={loading} - /> -
- )} -
- - { - const next = value || ""; - setPhone(next); - // Clear error and cooldown on input change - setCooldownSeconds(0); - setError(""); - }} - onCountryChange={(country) => { - setSelectedCountry(country); - // Clear error when country changes - setError(""); - }} - placeholder="Enter phone number" - defaultCountry="US" - disabled={loading} - /> -
- {isNorthAmerica ? ( -

- By clicking “Send code”, you agree to the{" "} - - Terms and Conditions - {" "} - and{" "} - - Privacy Policy - {" "} - and consent to one-time phone verification via SMS. Message and data rates may - apply. -

- ) : ( -

- By clicking “Send code”, you agree to the{" "} - - Terms and Conditions - {" "} - and{" "} - - Privacy Policy - - . -

- )} - {(error || !isCountrySupported || cooldownSeconds > 0) && ( -

- {!isCountrySupported - ? "SMS verification is not available for the selected country due to security restrictions." - : cooldownSeconds > 0 - ? `Please wait ${cooldownSeconds}s before requesting another code` - : error} -

- )} -
- )} - - {step === "code" && ( -
-
- -
- - - - - - - - - - - - - -
-

- {loading - ? "Verifying... please don't close this page" - : "Please enter the 6-digit code to proceed with verification"} -

-
- {error &&

{error}

} -
- )} - - {step === "done" && ( -
-
-

- You are now authenticated by all communities that use MintPass as anti-spam - challenge. You can close this page and head back to the Bitsocial application of - your choice. -

-
- {!hideNft && ( - <> - {txHash ? ( - - ) : ( -

- On-chain mint not configured; recorded as minted. -

- )} - - )} -
- )} -
-
-
-
- ); -} - -export const getServerSideProps: GetServerSideProps = async (ctx) => { - const countryHeader = (ctx.req.headers["x-vercel-ip-country"] as string) || ""; - const country = countryHeader.toUpperCase(); - const isNorthAmerica = country === "US" || country === "CA"; - return { props: { isNorthAmerica } }; -}; diff --git a/web/src/pages/terms-and-conditions.tsx b/web/src/pages/terms-and-conditions.tsx deleted file mode 100644 index 321f6c7..0000000 --- a/web/src/pages/terms-and-conditions.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import Link from 'next/link'; -import { Header } from '../components/header'; -import { Footer } from '../components/footer'; -import { PageCard } from '../components/page-card'; - -export default function TermsAndConditions() { - return ( -
-
-
- -

- Please read these terms carefully before using MintPass. MintPass is operated by{' '} - - Bitsocial - -

- -
-

Service

-

- MintPass provides an authentication NFT minting flow used by participating applications. The service verifies a phone number with an SMS code and, if eligible, mints an NFT to a provided Ethereum address (or records an equivalent mint state when on‑chain is disabled). -

-
- -
-

Eligibility

-
    -
  • You must submit a valid phone number you control for verification.
  • -
  • One authentication NFT per phone number and per wallet address.
  • -
  • We may block abusive activity, VPN/proxy usage, or high‑risk phone numbers.
  • -
-
- -
-

Assumptions and Risks

-
    -
  • Crypto transactions are irreversible and may incur gas fees. You are responsible for your wallet security.
  • -
  • The project code is open source; hosted deployments still require trust in the operator's configuration.
  • -
  • The service may change or be discontinued without notice.
  • -
-
- -
-

No Warranty

-

- The service is provided "as is," without warranties of any kind. To the maximum extent permitted by law, the operators and contributors disclaim all implied warranties and are not liable for damages arising from the use or inability to use the service. -

-
- -
-

Policy Changes

-

- We may update these terms and the privacy policy. Continued use of the service after changes constitutes acceptance of the updated terms. -

-
- -
- - Privacy Policy - -
-
-
-
-
- ); -} - - diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css deleted file mode 100644 index 001dc4d..0000000 --- a/web/src/styles/globals.css +++ /dev/null @@ -1,86 +0,0 @@ - -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 186 94% 28%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 186 94% 28%; - --radius: 0.5rem; -} - -.dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 174 56% 48%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 84.2% 65.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 174 56% 48%; -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } - :root { - --color-1: oklch(66.2% 0.225 25.9); - --color-2: oklch(60.4% 0.26 302); - --color-3: oklch(69.6% 0.165 251); - --color-4: oklch(80.2% 0.134 225); - --color-5: oklch(90.7% 0.231 133); - } - .dark { - /* Use lower lightness and adjusted chroma for contrast in dark mode */ - --color-1: oklch(62% 0.18 28); - --color-2: oklch(58% 0.22 305); - --color-3: oklch(60% 0.14 255); - --color-4: oklch(68% 0.11 230); - --color-5: oklch(74% 0.19 135); - } -} - -/* View transition styles - only disable default transitions, not all animations */ -::view-transition-old(root), ::view-transition-new(root) { - mix-blend-mode: normal; -} - -::view-transition-old(root) { - animation: none; -} - -::view-transition-new(root) { - animation: none; -} diff --git a/web/src/types/view-transitions.d.ts b/web/src/types/view-transitions.d.ts deleted file mode 100644 index 71d7e63..0000000 --- a/web/src/types/view-transitions.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Type declarations for View Transitions API -// See: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API - -interface ViewTransition { - /** Promise that fulfills when the transition is ready to start */ - ready: Promise; - /** Promise that fulfills when the transition animation finishes */ - finished: Promise; - /** Promise that fulfills when the update callback is done */ - updateCallbackDone: Promise; - /** Skips the animation part of the view transition */ - skipTransition(): void; -} - -interface Document { - /** - * Starts a view transition - * @param callback - Function that makes DOM changes - * @returns ViewTransition object - */ - startViewTransition?: (callback?: () => void | Promise) => ViewTransition; -} - -interface KeyframeAnimationOptions extends KeyframeEffectOptions { - /** Unique id for the animation (spec addition) */ - id?: string; - /** Associated timeline for the animation (spec addition) */ - timeline?: AnimationTimeline | null; -} diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts deleted file mode 100644 index de139be..0000000 --- a/web/tailwind.config.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { Config } from 'tailwindcss'; - -const config: Config = { - darkMode: ['class'], - content: [ - './pages/**/*.{js,ts,jsx,tsx,mdx}', - './src/**/*.{js,ts,jsx,tsx,mdx}', - './components/**/*.{js,ts,jsx,tsx,mdx}', - ], - theme: { - extend: { - animation: { - rainbow: 'rainbow 2s linear infinite', - 'caret-blink': 'caret-blink 1.25s ease-out infinite', - }, - keyframes: { - rainbow: { - '0%': { - 'background-position': '0%', - }, - '100%': { - 'background-position': '200%', - }, - }, - 'caret-blink': { - '0%,70%,100%': { opacity: '1' }, - '20%,50%': { opacity: '0' }, - }, - }, - colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - }, - }, - plugins: [], -}; - -export default config; diff --git a/web/tsconfig.json b/web/tsconfig.json deleted file mode 100644 index cda5f48..0000000 --- a/web/tsconfig.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "incremental": true, - "types": [ - "node" - ], - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/web/yarn.lock b/web/yarn.lock deleted file mode 100644 index 41be09b..0000000 --- a/web/yarn.lock +++ /dev/null @@ -1,7542 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"@adraffy/ens-normalize@npm:1.10.1": - version: 1.10.1 - resolution: "@adraffy/ens-normalize@npm:1.10.1" - checksum: 10c0/fdd647604e8fac6204921888aaf5a6bc65eabf0d2921bc5f93b64d01f4bc33ead167c1445f7de05468d05cd92ac31b74c68d2be840c62b79d73693308f885c06 - languageName: node - linkType: hard - -"@alloc/quick-lru@npm:^5.2.0": - version: 5.2.0 - resolution: "@alloc/quick-lru@npm:5.2.0" - checksum: 10c0/7b878c48b9d25277d0e1a9b8b2f2312a314af806b4129dc902f2bc29ab09b58236e53964689feec187b28c80d2203aff03829754773a707a8a5987f1b7682d92 - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/code-frame@npm:7.29.0" - dependencies: - "@babel/helper-validator-identifier": "npm:^7.28.5" - js-tokens: "npm:^4.0.0" - picocolors: "npm:^1.1.1" - checksum: 10c0/d34cc504e7765dfb576a663d97067afb614525806b5cad1a5cc1a7183b916fec8ff57fa233585e3926fd5a9e6b31aae6df91aa81ae9775fb7a28f658d3346f0d - languageName: node - linkType: hard - -"@babel/compat-data@npm:^7.28.6": - version: 7.29.0 - resolution: "@babel/compat-data@npm:7.29.0" - checksum: 10c0/08f348554989d23aa801bf1405aa34b15e841c0d52d79da7e524285c77a5f9d298e70e11d91cc578d8e2c9542efc586d50c5f5cf8e1915b254a9dcf786913a94 - languageName: node - linkType: hard - -"@babel/core@npm:^7.24.4": - version: 7.29.0 - resolution: "@babel/core@npm:7.29.0" - dependencies: - "@babel/code-frame": "npm:^7.29.0" - "@babel/generator": "npm:^7.29.0" - "@babel/helper-compilation-targets": "npm:^7.28.6" - "@babel/helper-module-transforms": "npm:^7.28.6" - "@babel/helpers": "npm:^7.28.6" - "@babel/parser": "npm:^7.29.0" - "@babel/template": "npm:^7.28.6" - "@babel/traverse": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" - "@jridgewell/remapping": "npm:^2.3.5" - convert-source-map: "npm:^2.0.0" - debug: "npm:^4.1.0" - gensync: "npm:^1.0.0-beta.2" - json5: "npm:^2.2.3" - semver: "npm:^6.3.1" - checksum: 10c0/5127d2e8e842ae409e11bcbb5c2dff9874abf5415e8026925af7308e903f4f43397341467a130490d1a39884f461bc2b67f3063bce0be44340db89687fd852aa - languageName: node - linkType: hard - -"@babel/generator@npm:^7.29.0": - version: 7.29.1 - resolution: "@babel/generator@npm:7.29.1" - dependencies: - "@babel/parser": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" - "@jridgewell/gen-mapping": "npm:^0.3.12" - "@jridgewell/trace-mapping": "npm:^0.3.28" - jsesc: "npm:^3.0.2" - checksum: 10c0/349086e6876258ef3fb2823030fee0f6c0eb9c3ebe35fc572e16997f8c030d765f636ddc6299edae63e760ea6658f8ee9a2edfa6d6b24c9a80c917916b973551 - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/helper-compilation-targets@npm:7.28.6" - dependencies: - "@babel/compat-data": "npm:^7.28.6" - "@babel/helper-validator-option": "npm:^7.27.1" - browserslist: "npm:^4.24.0" - lru-cache: "npm:^5.1.1" - semver: "npm:^6.3.1" - checksum: 10c0/3fcdf3b1b857a1578e99d20508859dbd3f22f3c87b8a0f3dc540627b4be539bae7f6e61e49d931542fe5b557545347272bbdacd7f58a5c77025a18b745593a50 - languageName: node - linkType: hard - -"@babel/helper-globals@npm:^7.28.0": - version: 7.28.0 - resolution: "@babel/helper-globals@npm:7.28.0" - checksum: 10c0/5a0cd0c0e8c764b5f27f2095e4243e8af6fa145daea2b41b53c0c1414fe6ff139e3640f4e2207ae2b3d2153a1abd346f901c26c290ee7cb3881dd922d4ee9232 - languageName: node - linkType: hard - -"@babel/helper-module-imports@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/helper-module-imports@npm:7.28.6" - dependencies: - "@babel/traverse": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" - checksum: 10c0/b49d8d8f204d9dbfd5ac70c54e533e5269afb3cea966a9d976722b13e9922cc773a653405f53c89acb247d5aebdae4681d631a3ae3df77ec046b58da76eda2ac - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/helper-module-transforms@npm:7.28.6" - dependencies: - "@babel/helper-module-imports": "npm:^7.28.6" - "@babel/helper-validator-identifier": "npm:^7.28.5" - "@babel/traverse": "npm:^7.28.6" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 10c0/6f03e14fc30b287ce0b839474b5f271e72837d0cafe6b172d759184d998fbee3903a035e81e07c2c596449e504f453463d58baa65b6f40a37ded5bec74620b2b - languageName: node - linkType: hard - -"@babel/helper-string-parser@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/helper-string-parser@npm:7.27.1" - checksum: 10c0/8bda3448e07b5583727c103560bcf9c4c24b3c1051a4c516d4050ef69df37bb9a4734a585fe12725b8c2763de0a265aa1e909b485a4e3270b7cfd3e4dbe4b602 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.28.5": - version: 7.28.5 - resolution: "@babel/helper-validator-identifier@npm:7.28.5" - checksum: 10c0/42aaebed91f739a41f3d80b72752d1f95fd7c72394e8e4bd7cdd88817e0774d80a432451bcba17c2c642c257c483bf1d409dd4548883429ea9493a3bc4ab0847 - languageName: node - linkType: hard - -"@babel/helper-validator-option@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/helper-validator-option@npm:7.27.1" - checksum: 10c0/6fec5f006eba40001a20f26b1ef5dbbda377b7b68c8ad518c05baa9af3f396e780bdfded24c4eef95d14bb7b8fd56192a6ed38d5d439b97d10efc5f1a191d148 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/helpers@npm:7.28.6" - dependencies: - "@babel/template": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" - checksum: 10c0/c4a779c66396bb0cf619402d92f1610601ff3832db2d3b86b9c9dd10983bf79502270e97ac6d5280cea1b1a37de2f06ecbac561bd2271545270407fbe64027cb - languageName: node - linkType: hard - -"@babel/parser@npm:^7.24.4, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/parser@npm:7.29.0" - dependencies: - "@babel/types": "npm:^7.29.0" - bin: - parser: ./bin/babel-parser.js - checksum: 10c0/333b2aa761264b91577a74bee86141ef733f9f9f6d4fc52548e4847dc35dfbf821f58c46832c637bfa761a6d9909d6a68f7d1ed59e17e4ffbb958dc510c17b62 - languageName: node - linkType: hard - -"@babel/template@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/template@npm:7.28.6" - dependencies: - "@babel/code-frame": "npm:^7.28.6" - "@babel/parser": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" - checksum: 10c0/66d87225ed0bc77f888181ae2d97845021838c619944877f7c4398c6748bcf611f216dfd6be74d39016af502bca876e6ce6873db3c49e4ac354c56d34d57e9f5 - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.28.6, @babel/traverse@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/traverse@npm:7.29.0" - dependencies: - "@babel/code-frame": "npm:^7.29.0" - "@babel/generator": "npm:^7.29.0" - "@babel/helper-globals": "npm:^7.28.0" - "@babel/parser": "npm:^7.29.0" - "@babel/template": "npm:^7.28.6" - "@babel/types": "npm:^7.29.0" - debug: "npm:^4.3.1" - checksum: 10c0/f63ef6e58d02a9fbf3c0e2e5f1c877da3e0bc57f91a19d2223d53e356a76859cbaf51171c9211c71816d94a0e69efa2732fd27ffc0e1bbc84b636e60932333eb - languageName: node - linkType: hard - -"@babel/types@npm:^7.28.6, @babel/types@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/types@npm:7.29.0" - dependencies: - "@babel/helper-string-parser": "npm:^7.27.1" - "@babel/helper-validator-identifier": "npm:^7.28.5" - checksum: 10c0/23cc3466e83bcbfab8b9bd0edaafdb5d4efdb88b82b3be6728bbade5ba2f0996f84f63b1c5f7a8c0d67efded28300898a5f930b171bb40b311bca2029c4e9b4f - languageName: node - linkType: hard - -"@effect/platform-node-shared@npm:4.0.0-beta.70": - version: 4.0.0-beta.70 - resolution: "@effect/platform-node-shared@npm:4.0.0-beta.70" - dependencies: - "@types/ws": "npm:^8.18.1" - ws: "npm:^8.20.0" - peerDependencies: - effect: ^4.0.0-beta.70 - checksum: 10c0/91f1543a56806647405499677b45cf596be89e614c74cf016de5d105b2cc3413c326339851617e38ded4f1cbd61e9082fee00d9e2098856a0a4334aa1d7b49d1 - languageName: node - linkType: hard - -"@emnapi/core@npm:1.10.0": - version: 1.10.0 - resolution: "@emnapi/core@npm:1.10.0" - dependencies: - "@emnapi/wasi-threads": "npm:1.2.1" - tslib: "npm:^2.4.0" - checksum: 10c0/f51d08227857b60632de7714d708124f0e100a1462dde6df8221760939aa3204a73193830371830fac0716f3ccd2129f2cac1b17cd7d7958bc4da9018a296edb - languageName: node - linkType: hard - -"@emnapi/core@npm:^1.4.3": - version: 1.4.5 - resolution: "@emnapi/core@npm:1.4.5" - dependencies: - "@emnapi/wasi-threads": "npm:1.0.4" - tslib: "npm:^2.4.0" - checksum: 10c0/da4a57f65f325d720d0e0d1a9c6618b90c4c43a5027834a110476984e1d47c95ebaed4d316b5dddb9c0ed9a493ffeb97d1934f9677035f336d8a36c1f3b2818f - languageName: node - linkType: hard - -"@emnapi/core@npm:^1.7.1": - version: 1.8.1 - resolution: "@emnapi/core@npm:1.8.1" - dependencies: - "@emnapi/wasi-threads": "npm:1.1.0" - tslib: "npm:^2.4.0" - checksum: 10c0/2c242f4b49779bac403e1cbcc98edacdb1c8ad36562408ba9a20663824669e930bc8493be46a2522d9dc946b8d96cd7073970bae914928c7671b5221c85b432e - languageName: node - linkType: hard - -"@emnapi/runtime@npm:1.10.0": - version: 1.10.0 - resolution: "@emnapi/runtime@npm:1.10.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/953f14991d1aefb92ee6f8eb27dea725e484791a53a0cb5f47d9e0087b9a2c929ff2e92adf95af15d6ad456db6300c6b761ebf72b50a875b874a83520b3ba093 - languageName: node - linkType: hard - -"@emnapi/runtime@npm:^1.4.3": - version: 1.4.5 - resolution: "@emnapi/runtime@npm:1.4.5" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/37a0278be5ac81e918efe36f1449875cbafba947039c53c65a1f8fc238001b866446fc66041513b286baaff5d6f9bec667f5164b3ca481373a8d9cb65bfc984b - languageName: node - linkType: hard - -"@emnapi/runtime@npm:^1.7.0": - version: 1.9.0 - resolution: "@emnapi/runtime@npm:1.9.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/f825e53b2d3f9d31fd880e669197d006bb5158c3a52ab25f0546f3d52ac58eb539a4bd1dcc378af6c10d202956fa064b28ab7b572a76de58972c0b8656a692ef - languageName: node - linkType: hard - -"@emnapi/runtime@npm:^1.7.1": - version: 1.8.1 - resolution: "@emnapi/runtime@npm:1.8.1" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/f4929d75e37aafb24da77d2f58816761fe3f826aad2e37fa6d4421dac9060cbd5098eea1ac3c9ecc4526b89deb58153852fa432f87021dc57863f2ff726d713f - languageName: node - linkType: hard - -"@emnapi/wasi-threads@npm:1.0.4": - version: 1.0.4 - resolution: "@emnapi/wasi-threads@npm:1.0.4" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/2c91a53e62f875800baf035c4d42c9c0d18e5afd9a31ca2aac8b435aeaeaeaac386b5b3d0d0e70aa7a5a9852bbe05106b1f680cd82cce03145c703b423d41313 - languageName: node - linkType: hard - -"@emnapi/wasi-threads@npm:1.1.0": - version: 1.1.0 - resolution: "@emnapi/wasi-threads@npm:1.1.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/e6d54bf2b1e64cdd83d2916411e44e579b6ae35d5def0dea61a3c452d9921373044dff32a8b8473ae60c80692bdc39323e98b96a3f3d87ba6886b24dd0ef7ca1 - languageName: node - linkType: hard - -"@emnapi/wasi-threads@npm:1.2.1": - version: 1.2.1 - resolution: "@emnapi/wasi-threads@npm:1.2.1" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/32fcfa81ab396533b2ec1f4082b1ff779a05d9c836bbbd3f4398405b0e6814c0d9503b7993130e37bc6941dbc1ded49f55e9700ae9ca4e803bab2b5bc5deb331 - languageName: node - linkType: hard - -"@eslint-community/eslint-utils@npm:^4.8.0": - version: 4.9.0 - resolution: "@eslint-community/eslint-utils@npm:4.9.0" - dependencies: - eslint-visitor-keys: "npm:^3.4.3" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/8881e22d519326e7dba85ea915ac7a143367c805e6ba1374c987aa2fbdd09195cc51183d2da72c0e2ff388f84363e1b220fd0d19bef10c272c63455162176817 - languageName: node - linkType: hard - -"@eslint-community/eslint-utils@npm:^4.9.1": - version: 4.9.1 - resolution: "@eslint-community/eslint-utils@npm:4.9.1" - dependencies: - eslint-visitor-keys: "npm:^3.4.3" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/dc4ab5e3e364ef27e33666b11f4b86e1a6c1d7cbf16f0c6ff87b1619b3562335e9201a3d6ce806221887ff780ec9d828962a290bb910759fd40a674686503f02 - languageName: node - linkType: hard - -"@eslint-community/regexpp@npm:^4.12.1": - version: 4.12.1 - resolution: "@eslint-community/regexpp@npm:4.12.1" - checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 - languageName: node - linkType: hard - -"@eslint-community/regexpp@npm:^4.12.2": - version: 4.12.2 - resolution: "@eslint-community/regexpp@npm:4.12.2" - checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d - languageName: node - linkType: hard - -"@eslint/config-array@npm:^0.21.1": - version: 0.21.1 - resolution: "@eslint/config-array@npm:0.21.1" - dependencies: - "@eslint/object-schema": "npm:^2.1.7" - debug: "npm:^4.3.1" - minimatch: "npm:^3.1.2" - checksum: 10c0/2f657d4edd6ddcb920579b72e7a5b127865d4c3fb4dda24f11d5c4f445a93ca481aebdbd6bf3291c536f5d034458dbcbb298ee3b698bc6c9dd02900fe87eec3c - languageName: node - linkType: hard - -"@eslint/config-helpers@npm:^0.4.2": - version: 0.4.2 - resolution: "@eslint/config-helpers@npm:0.4.2" - dependencies: - "@eslint/core": "npm:^0.17.0" - checksum: 10c0/92efd7a527b2d17eb1a148409d71d80f9ac160b565ac73ee092252e8bf08ecd08670699f46b306b94f13d22e88ac88a612120e7847570dd7cdc72f234d50dcb4 - languageName: node - linkType: hard - -"@eslint/core@npm:^0.17.0": - version: 0.17.0 - resolution: "@eslint/core@npm:0.17.0" - dependencies: - "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/9a580f2246633bc752298e7440dd942ec421860d1946d0801f0423830e67887e4aeba10ab9a23d281727a978eb93d053d1922a587d502942a713607f40ed704e - languageName: node - linkType: hard - -"@eslint/eslintrc@npm:^3, @eslint/eslintrc@npm:^3.3.1": - version: 3.3.1 - resolution: "@eslint/eslintrc@npm:3.3.1" - dependencies: - ajv: "npm:^6.12.4" - debug: "npm:^4.3.2" - espree: "npm:^10.0.1" - globals: "npm:^14.0.0" - ignore: "npm:^5.2.0" - import-fresh: "npm:^3.2.1" - js-yaml: "npm:^4.1.0" - minimatch: "npm:^3.1.2" - strip-json-comments: "npm:^3.1.1" - checksum: 10c0/b0e63f3bc5cce4555f791a4e487bf999173fcf27c65e1ab6e7d63634d8a43b33c3693e79f192cbff486d7df1be8ebb2bd2edc6e70ddd486cbfa84a359a3e3b41 - languageName: node - linkType: hard - -"@eslint/js@npm:9.39.2": - version: 9.39.2 - resolution: "@eslint/js@npm:9.39.2" - checksum: 10c0/00f51c52b04ac79faebfaa65a9652b2093b9c924e945479f1f3945473f78aee83cbc76c8d70bbffbf06f7024626575b16d97b66eab16182e1d0d39daff2f26f5 - languageName: node - linkType: hard - -"@eslint/object-schema@npm:^2.1.7": - version: 2.1.7 - resolution: "@eslint/object-schema@npm:2.1.7" - checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87 - languageName: node - linkType: hard - -"@eslint/plugin-kit@npm:^0.4.1": - version: 0.4.1 - resolution: "@eslint/plugin-kit@npm:0.4.1" - dependencies: - "@eslint/core": "npm:^0.17.0" - levn: "npm:^0.4.1" - checksum: 10c0/51600f78b798f172a9915dffb295e2ffb44840d583427bc732baf12ecb963eb841b253300e657da91d890f4b323d10a1bd12934bf293e3018d8bb66fdce5217b - languageName: node - linkType: hard - -"@floating-ui/core@npm:^1.7.3": - version: 1.7.3 - resolution: "@floating-ui/core@npm:1.7.3" - dependencies: - "@floating-ui/utils": "npm:^0.2.10" - checksum: 10c0/edfc23800122d81df0df0fb780b7328ae6c5f00efbb55bd48ea340f4af8c5b3b121ceb4bb81220966ab0f87b443204d37105abdd93d94846468be3243984144c - languageName: node - linkType: hard - -"@floating-ui/dom@npm:^1.7.4": - version: 1.7.4 - resolution: "@floating-ui/dom@npm:1.7.4" - dependencies: - "@floating-ui/core": "npm:^1.7.3" - "@floating-ui/utils": "npm:^0.2.10" - checksum: 10c0/da6166c25f9b0729caa9f498685a73a0e28251613b35d27db8de8014bc9d045158a23c092b405321a3d67c2064909b6e2a7e6c1c9cc0f62967dca5779f5aef30 - languageName: node - linkType: hard - -"@floating-ui/react-dom@npm:^2.0.0": - version: 2.1.6 - resolution: "@floating-ui/react-dom@npm:2.1.6" - dependencies: - "@floating-ui/dom": "npm:^1.7.4" - peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - checksum: 10c0/6654834a8e73ecbdbc6cad2ad8f7abc698ac7c1800ded4d61113525c591c03d2e3b59d3cf9205859221465ea38c87af4f9e6e204703c5b7a7e85332d1eef2e18 - languageName: node - linkType: hard - -"@floating-ui/utils@npm:^0.2.10": - version: 0.2.10 - resolution: "@floating-ui/utils@npm:0.2.10" - checksum: 10c0/e9bc2a1730ede1ee25843937e911ab6e846a733a4488623cd353f94721b05ec2c9ec6437613a2ac9379a94c2fd40c797a2ba6fa1df2716f5ce4aa6ddb1cf9ea4 - languageName: node - linkType: hard - -"@gar/promise-retry@npm:^1.0.0": - version: 1.0.3 - resolution: "@gar/promise-retry@npm:1.0.3" - checksum: 10c0/885b02c8b0d75b2d215da25f3b639158c4fbe8fefe0d79163304534b9a6d0710db4b7699f7cd3cc1a730792bff04cbe19f4850a62d3e105a663eaeec88f38332 - languageName: node - linkType: hard - -"@humanfs/core@npm:^0.19.1": - version: 0.19.1 - resolution: "@humanfs/core@npm:0.19.1" - checksum: 10c0/aa4e0152171c07879b458d0e8a704b8c3a89a8c0541726c6b65b81e84fd8b7564b5d6c633feadc6598307d34564bd53294b533491424e8e313d7ab6c7bc5dc67 - languageName: node - linkType: hard - -"@humanfs/node@npm:^0.16.6": - version: 0.16.6 - resolution: "@humanfs/node@npm:0.16.6" - dependencies: - "@humanfs/core": "npm:^0.19.1" - "@humanwhocodes/retry": "npm:^0.3.0" - checksum: 10c0/8356359c9f60108ec204cbd249ecd0356667359b2524886b357617c4a7c3b6aace0fd5a369f63747b926a762a88f8a25bc066fa1778508d110195ce7686243e1 - languageName: node - linkType: hard - -"@humanwhocodes/module-importer@npm:^1.0.1": - version: 1.0.1 - resolution: "@humanwhocodes/module-importer@npm:1.0.1" - checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529 - languageName: node - linkType: hard - -"@humanwhocodes/retry@npm:^0.3.0": - version: 0.3.1 - resolution: "@humanwhocodes/retry@npm:0.3.1" - checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b - languageName: node - linkType: hard - -"@humanwhocodes/retry@npm:^0.4.2": - version: 0.4.3 - resolution: "@humanwhocodes/retry@npm:0.4.3" - checksum: 10c0/3775bb30087d4440b3f7406d5a057777d90e4b9f435af488a4923ef249e93615fb78565a85f173a186a076c7706a81d0d57d563a2624e4de2c5c9c66c486ce42 - languageName: node - linkType: hard - -"@iarna/toml@npm:^2.2.5": - version: 2.2.5 - resolution: "@iarna/toml@npm:2.2.5" - checksum: 10c0/d095381ad4554aca233b7cf5a91f243ef619e5e15efd3157bc640feac320545450d14b394aebbf6f02a2047437ced778ae598d5879a995441ab7b6c0b2c2f201 - languageName: node - linkType: hard - -"@img/colour@npm:^1.0.0": - version: 1.1.0 - resolution: "@img/colour@npm:1.1.0" - checksum: 10c0/2ebea2c0bbaee73b99badcefa04e1e71d83f36e5369337d3121dca841f4569533c4e2faddda6d62dd247f0d5cca143711f9446c59bcce81e427ba433a7a94a17 - languageName: node - linkType: hard - -"@img/sharp-darwin-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-darwin-arm64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-darwin-arm64": - optional: true - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@img/sharp-darwin-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-darwin-x64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-darwin-x64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-darwin-x64": - optional: true - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@img/sharp-libvips-darwin-arm64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@img/sharp-libvips-darwin-x64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-arm64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-arm@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-ppc64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-riscv64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-s390x@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-x64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-linux-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-arm64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-arm64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-arm64": - optional: true - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-arm@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-arm@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-arm": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-arm": - optional: true - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-ppc64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-ppc64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-ppc64": - optional: true - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-riscv64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-riscv64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-riscv64": - optional: true - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-s390x@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-s390x@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-s390x": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-s390x": - optional: true - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-x64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-x64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-x64": - optional: true - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linuxmusl-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linuxmusl-arm64": - optional: true - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-linuxmusl-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linuxmusl-x64": - optional: true - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-wasm32@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-wasm32@npm:0.34.5" - dependencies: - "@emnapi/runtime": "npm:^1.7.0" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@img/sharp-win32-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-win32-arm64@npm:0.34.5" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@img/sharp-win32-ia32@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-win32-ia32@npm:0.34.5" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@img/sharp-win32-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-win32-x64@npm:0.34.5" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@isaacs/fs-minipass@npm:^4.0.0": - version: 4.0.1 - resolution: "@isaacs/fs-minipass@npm:4.0.1" - dependencies: - minipass: "npm:^7.0.4" - checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 - languageName: node - linkType: hard - -"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": - version: 0.3.13 - resolution: "@jridgewell/gen-mapping@npm:0.3.13" - dependencies: - "@jridgewell/sourcemap-codec": "npm:^1.5.0" - "@jridgewell/trace-mapping": "npm:^0.3.24" - checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b - languageName: node - linkType: hard - -"@jridgewell/remapping@npm:^2.3.5": - version: 2.3.5 - resolution: "@jridgewell/remapping@npm:2.3.5" - dependencies: - "@jridgewell/gen-mapping": "npm:^0.3.5" - "@jridgewell/trace-mapping": "npm:^0.3.24" - checksum: 10c0/3de494219ffeb2c5c38711d0d7bb128097edf91893090a2dbc8ee0b55d092bb7347b1fd0f478486c5eab010e855c73927b1666f2107516d472d24a73017d1194 - languageName: node - linkType: hard - -"@jridgewell/resolve-uri@npm:^3.1.0": - version: 3.1.2 - resolution: "@jridgewell/resolve-uri@npm:3.1.2" - checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e - languageName: node - linkType: hard - -"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": - version: 1.5.5 - resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" - checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 - languageName: node - linkType: hard - -"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": - version: 0.3.31 - resolution: "@jridgewell/trace-mapping@npm:0.3.31" - dependencies: - "@jridgewell/resolve-uri": "npm:^3.1.0" - "@jridgewell/sourcemap-codec": "npm:^1.4.14" - checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 - languageName: node - linkType: hard - -"@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.4": - version: 3.0.4 - resolution: "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.4" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.4": - version: 3.0.4 - resolution: "@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.4" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.4": - version: 3.0.4 - resolution: "@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.4" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.4": - version: 3.0.4 - resolution: "@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.4" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.4": - version: 3.0.4 - resolution: "@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.4" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.4": - version: 3.0.4 - resolution: "@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.4" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@napi-rs/wasm-runtime@npm:^0.2.11": - version: 0.2.12 - resolution: "@napi-rs/wasm-runtime@npm:0.2.12" - dependencies: - "@emnapi/core": "npm:^1.4.3" - "@emnapi/runtime": "npm:^1.4.3" - "@tybys/wasm-util": "npm:^0.10.0" - checksum: 10c0/6d07922c0613aab30c6a497f4df297ca7c54e5b480e00035e0209b872d5c6aab7162fc49477267556109c2c7ed1eb9c65a174e27e9b87568106a87b0a6e3ca7d - languageName: node - linkType: hard - -"@napi-rs/wasm-runtime@npm:^1.1.1": - version: 1.1.1 - resolution: "@napi-rs/wasm-runtime@npm:1.1.1" - dependencies: - "@emnapi/core": "npm:^1.7.1" - "@emnapi/runtime": "npm:^1.7.1" - "@tybys/wasm-util": "npm:^0.10.1" - checksum: 10c0/04d57b67e80736e41fe44674a011878db0a8ad893f4d44abb9d3608debb7c174224cba2796ed5b0c1d367368159f3ca6be45f1c59222f70e32ddc880f803d447 - languageName: node - linkType: hard - -"@napi-rs/wasm-runtime@npm:^1.1.4": - version: 1.1.4 - resolution: "@napi-rs/wasm-runtime@npm:1.1.4" - dependencies: - "@tybys/wasm-util": "npm:^0.10.1" - peerDependencies: - "@emnapi/core": ^1.7.1 - "@emnapi/runtime": ^1.7.1 - checksum: 10c0/2e88e1955258949ccf2d18c79975821ad38071b465ef126a5e14110977b97868867b016c1ad046e963cccc42c0bd9db6c8ff5fd1ebb61b87bb3487f339041658 - languageName: node - linkType: hard - -"@next/env@npm:16.2.3": - version: 16.2.3 - resolution: "@next/env@npm:16.2.3" - checksum: 10c0/56c3fee8ea226efe59ef065e054380f872c00c45c9fe4475eaa45f80773c3c1adc3ead3ccdd77447d3c1aeb4b3004aaaa033dd4a100d3e572fd01b83f992dde8 - languageName: node - linkType: hard - -"@next/eslint-plugin-next@npm:16.2.3": - version: 16.2.3 - resolution: "@next/eslint-plugin-next@npm:16.2.3" - dependencies: - fast-glob: "npm:3.3.1" - checksum: 10c0/be881aa89e0840ab60455b07a2bb9ec0d686c664a0d91e8ca815797a65ca71d7bd79d186b0df5b6892c2bf57bd07fa05421cd93e2812dfeaedfad5ed9fd1023e - languageName: node - linkType: hard - -"@next/swc-darwin-arm64@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-darwin-arm64@npm:16.2.3" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@next/swc-darwin-x64@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-darwin-x64@npm:16.2.3" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@next/swc-linux-arm64-gnu@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-linux-arm64-gnu@npm:16.2.3" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@next/swc-linux-arm64-musl@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-linux-arm64-musl@npm:16.2.3" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@next/swc-linux-x64-gnu@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-linux-x64-gnu@npm:16.2.3" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@next/swc-linux-x64-musl@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-linux-x64-musl@npm:16.2.3" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@next/swc-win32-arm64-msvc@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-win32-arm64-msvc@npm:16.2.3" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@next/swc-win32-x64-msvc@npm:16.2.3": - version: 16.2.3 - resolution: "@next/swc-win32-x64-msvc@npm:16.2.3" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@noble/curves@npm:1.2.0": - version: 1.2.0 - resolution: "@noble/curves@npm:1.2.0" - dependencies: - "@noble/hashes": "npm:1.3.2" - checksum: 10c0/0bac7d1bbfb3c2286910b02598addd33243cb97c3f36f987ecc927a4be8d7d88e0fcb12b0f0ef8a044e7307d1844dd5c49bb724bfa0a79c8ec50ba60768c97f6 - languageName: node - linkType: hard - -"@noble/hashes@npm:1.3.2": - version: 1.3.2 - resolution: "@noble/hashes@npm:1.3.2" - checksum: 10c0/2482cce3bce6a596626f94ca296e21378e7a5d4c09597cbc46e65ffacc3d64c8df73111f2265444e36a3168208628258bbbaccba2ef24f65f58b2417638a20e7 - languageName: node - linkType: hard - -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" - dependencies: - "@nodelib/fs.stat": "npm:2.0.5" - run-parallel: "npm:^1.1.9" - checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb - languageName: node - linkType: hard - -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d - languageName: node - linkType: hard - -"@nodelib/fs.walk@npm:^1.2.3": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" - dependencies: - "@nodelib/fs.scandir": "npm:2.1.5" - fastq: "npm:^1.6.0" - checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 - languageName: node - linkType: hard - -"@nolyfill/is-core-module@npm:1.0.39": - version: 1.0.39 - resolution: "@nolyfill/is-core-module@npm:1.0.39" - checksum: 10c0/34ab85fdc2e0250879518841f74a30c276bca4f6c3e13526d2d1fe515e1adf6d46c25fcd5989d22ea056d76f7c39210945180b4859fc83b050e2da411aa86289 - languageName: node - linkType: hard - -"@npmcli/agent@npm:^4.0.0": - version: 4.0.0 - resolution: "@npmcli/agent@npm:4.0.0" - dependencies: - agent-base: "npm:^7.1.0" - http-proxy-agent: "npm:^7.0.0" - https-proxy-agent: "npm:^7.0.1" - lru-cache: "npm:^11.2.1" - socks-proxy-agent: "npm:^8.0.3" - checksum: 10c0/f7b5ce0f3dd42c3f8c6546e8433573d8049f67ef11ec22aa4704bc41483122f68bf97752e06302c455ead667af5cb753e6a09bff06632bc465c1cfd4c4b75a53 - languageName: node - linkType: hard - -"@npmcli/fs@npm:^5.0.0": - version: 5.0.0 - resolution: "@npmcli/fs@npm:5.0.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/26e376d780f60ff16e874a0ac9bc3399186846baae0b6e1352286385ac134d900cc5dafaded77f38d77f86898fc923ae1cee9d7399f0275b1aa24878915d722b - languageName: node - linkType: hard - -"@npmcli/redact@npm:^4.0.0": - version: 4.0.0 - resolution: "@npmcli/redact@npm:4.0.0" - checksum: 10c0/a1e9ba9c70a6b40e175bda2c3dd8cfdaf096e6b7f7a132c855c083c8dfe545c3237cd56702e2e6627a580b1d63373599d49a1192c4078a85bf47bbde824df31c - languageName: node - linkType: hard - -"@oxc-parser/binding-android-arm-eabi@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-android-arm-eabi@npm:0.132.0" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@oxc-parser/binding-android-arm64@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-android-arm64@npm:0.132.0" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-parser/binding-darwin-arm64@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-darwin-arm64@npm:0.132.0" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-parser/binding-darwin-x64@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-darwin-x64@npm:0.132.0" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@oxc-parser/binding-freebsd-x64@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-freebsd-x64@npm:0.132.0" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-arm-gnueabihf@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-arm-gnueabihf@npm:0.132.0" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-arm-musleabihf@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-arm-musleabihf@npm:0.132.0" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-arm64-gnu@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-arm64-gnu@npm:0.132.0" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-arm64-musl@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-arm64-musl@npm:0.132.0" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-ppc64-gnu@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-ppc64-gnu@npm:0.132.0" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-riscv64-gnu@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-riscv64-gnu@npm:0.132.0" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-riscv64-musl@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-riscv64-musl@npm:0.132.0" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-s390x-gnu@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-s390x-gnu@npm:0.132.0" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-x64-gnu@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-x64-gnu@npm:0.132.0" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-parser/binding-linux-x64-musl@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-linux-x64-musl@npm:0.132.0" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@oxc-parser/binding-openharmony-arm64@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-openharmony-arm64@npm:0.132.0" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-parser/binding-wasm32-wasi@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-wasm32-wasi@npm:0.132.0" - dependencies: - "@emnapi/core": "npm:1.10.0" - "@emnapi/runtime": "npm:1.10.0" - "@napi-rs/wasm-runtime": "npm:^1.1.4" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@oxc-parser/binding-win32-arm64-msvc@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-win32-arm64-msvc@npm:0.132.0" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-parser/binding-win32-ia32-msvc@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-win32-ia32-msvc@npm:0.132.0" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@oxc-parser/binding-win32-x64-msvc@npm:0.132.0": - version: 0.132.0 - resolution: "@oxc-parser/binding-win32-x64-msvc@npm:0.132.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@oxc-project/types@npm:^0.132.0": - version: 0.132.0 - resolution: "@oxc-project/types@npm:0.132.0" - checksum: 10c0/d0ca5e98be0b873d69e4f0f743eb35026833603dac11db9d55f2b5438251b381b886dc556fe3175a17b673f8e2073c49bde88d7e6e702aa09298c22b8b5504e1 - languageName: node - linkType: hard - -"@oxc-resolver/binding-android-arm-eabi@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-android-arm-eabi@npm:11.19.1" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@oxc-resolver/binding-android-arm64@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-android-arm64@npm:11.19.1" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-resolver/binding-darwin-arm64@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-darwin-arm64@npm:11.19.1" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-resolver/binding-darwin-x64@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-darwin-x64@npm:11.19.1" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@oxc-resolver/binding-freebsd-x64@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-freebsd-x64@npm:11.19.1" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.19.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-arm-musleabihf@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-arm-musleabihf@npm:11.19.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-arm64-gnu@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-arm64-gnu@npm:11.19.1" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-arm64-musl@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-arm64-musl@npm:11.19.1" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-ppc64-gnu@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-ppc64-gnu@npm:11.19.1" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-riscv64-gnu@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-riscv64-gnu@npm:11.19.1" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-riscv64-musl@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-riscv64-musl@npm:11.19.1" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-s390x-gnu@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-s390x-gnu@npm:11.19.1" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-x64-gnu@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-x64-gnu@npm:11.19.1" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-resolver/binding-linux-x64-musl@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-linux-x64-musl@npm:11.19.1" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@oxc-resolver/binding-openharmony-arm64@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-openharmony-arm64@npm:11.19.1" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-resolver/binding-wasm32-wasi@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-wasm32-wasi@npm:11.19.1" - dependencies: - "@napi-rs/wasm-runtime": "npm:^1.1.1" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@oxc-resolver/binding-win32-arm64-msvc@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-win32-arm64-msvc@npm:11.19.1" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-resolver/binding-win32-ia32-msvc@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-win32-ia32-msvc@npm:11.19.1" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@oxc-resolver/binding-win32-x64-msvc@npm:11.19.1": - version: 11.19.1 - resolution: "@oxc-resolver/binding-win32-x64-msvc@npm:11.19.1" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@oxlint/binding-android-arm-eabi@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-android-arm-eabi@npm:1.67.0" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@oxlint/binding-android-arm64@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-android-arm64@npm:1.67.0" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@oxlint/binding-darwin-arm64@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-darwin-arm64@npm:1.67.0" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@oxlint/binding-darwin-x64@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-darwin-x64@npm:1.67.0" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@oxlint/binding-freebsd-x64@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-freebsd-x64@npm:1.67.0" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@oxlint/binding-linux-arm-gnueabihf@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-arm-gnueabihf@npm:1.67.0" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@oxlint/binding-linux-arm-musleabihf@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-arm-musleabihf@npm:1.67.0" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@oxlint/binding-linux-arm64-gnu@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-arm64-gnu@npm:1.67.0" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@oxlint/binding-linux-arm64-musl@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-arm64-musl@npm:1.67.0" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@oxlint/binding-linux-ppc64-gnu@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-ppc64-gnu@npm:1.67.0" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@oxlint/binding-linux-riscv64-gnu@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-riscv64-gnu@npm:1.67.0" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@oxlint/binding-linux-riscv64-musl@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-riscv64-musl@npm:1.67.0" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@oxlint/binding-linux-s390x-gnu@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-s390x-gnu@npm:1.67.0" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@oxlint/binding-linux-x64-gnu@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-x64-gnu@npm:1.67.0" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@oxlint/binding-linux-x64-musl@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-linux-x64-musl@npm:1.67.0" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@oxlint/binding-openharmony-arm64@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-openharmony-arm64@npm:1.67.0" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@oxlint/binding-win32-arm64-msvc@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-win32-arm64-msvc@npm:1.67.0" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@oxlint/binding-win32-ia32-msvc@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-win32-ia32-msvc@npm:1.67.0" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@oxlint/binding-win32-x64-msvc@npm:1.67.0": - version: 1.67.0 - resolution: "@oxlint/binding-win32-x64-msvc@npm:1.67.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@radix-ui/number@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/number@npm:1.1.1" - checksum: 10c0/0570ad92287398e8a7910786d7cee0a998174cdd6637ba61571992897c13204adf70b9ed02d0da2af554119411128e701d9c6b893420612897b438dc91db712b - languageName: node - linkType: hard - -"@radix-ui/primitive@npm:1.1.3": - version: 1.1.3 - resolution: "@radix-ui/primitive@npm:1.1.3" - checksum: 10c0/88860165ee7066fa2c179f32ffcd3ee6d527d9dcdc0e8be85e9cb0e2c84834be8e3c1a976c74ba44b193f709544e12f54455d892b28e32f0708d89deda6b9f1d - languageName: node - linkType: hard - -"@radix-ui/react-arrow@npm:1.1.7": - version: 1.1.7 - resolution: "@radix-ui/react-arrow@npm:1.1.7" - dependencies: - "@radix-ui/react-primitive": "npm:2.1.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/c3b46766238b3ee2a394d8806a5141432361bf1425110c9f0dcf480bda4ebd304453a53f294b5399c6ee3ccfcae6fd544921fd01ddc379cf5942acdd7168664b - languageName: node - linkType: hard - -"@radix-ui/react-collection@npm:1.1.7": - version: 1.1.7 - resolution: "@radix-ui/react-collection@npm:1.1.7" - dependencies: - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-slot": "npm:1.2.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/fa321a7300095508491f75414f02b243f0c3f179dc0728cfd115e2ea9f6f48f1516532b59f526d9ac81bbab63cd98a052074b4703ec0b9428fac945ebabec5fd - languageName: node - linkType: hard - -"@radix-ui/react-compose-refs@npm:1.1.2, @radix-ui/react-compose-refs@npm:^1.1.1": - version: 1.1.2 - resolution: "@radix-ui/react-compose-refs@npm:1.1.2" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/d36a9c589eb75d634b9b139c80f916aadaf8a68a7c1c4b8c6c6b88755af1a92f2e343457042089f04cc3f23073619d08bb65419ced1402e9d4e299576d970771 - languageName: node - linkType: hard - -"@radix-ui/react-context@npm:1.1.2": - version: 1.1.2 - resolution: "@radix-ui/react-context@npm:1.1.2" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/cece731f8cc25d494c6589cc681e5c01a93867d895c75889973afa1a255f163c286e390baa7bc028858eaabe9f6b57270d0ca6377356f652c5557c1c7a41ccce - languageName: node - linkType: hard - -"@radix-ui/react-dialog@npm:^1.1.15, @radix-ui/react-dialog@npm:^1.1.6": - version: 1.1.15 - resolution: "@radix-ui/react-dialog@npm:1.1.15" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-dismissable-layer": "npm:1.1.11" - "@radix-ui/react-focus-guards": "npm:1.1.3" - "@radix-ui/react-focus-scope": "npm:1.1.7" - "@radix-ui/react-id": "npm:1.1.1" - "@radix-ui/react-portal": "npm:1.1.9" - "@radix-ui/react-presence": "npm:1.1.5" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-slot": "npm:1.2.3" - "@radix-ui/react-use-controllable-state": "npm:1.2.2" - aria-hidden: "npm:^1.2.4" - react-remove-scroll: "npm:^2.6.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/2f2c88e3c281acaea2fd9b96fa82132d59177d3aa5da2e7c045596fd4028e84e44ac52ac28f4f236910605dd7d9338c2858ba44a9ced2af2e3e523abbfd33014 - languageName: node - linkType: hard - -"@radix-ui/react-direction@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/react-direction@npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/7a89d9291f846a3105e45f4df98d6b7a08f8d7b30acdcd253005dc9db107ee83cbbebc9e47a9af1e400bcd47697f1511ceab23a399b0da854488fc7220482ac9 - languageName: node - linkType: hard - -"@radix-ui/react-dismissable-layer@npm:1.1.11": - version: 1.1.11 - resolution: "@radix-ui/react-dismissable-layer@npm:1.1.11" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - "@radix-ui/react-use-escape-keydown": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/c825572a64073c4d3853702029979f6658770ffd6a98eabc4984e1dee1b226b4078a2a4dc7003f96475b438985e9b21a58e75f51db74dd06848dcae1f2d395dc - languageName: node - linkType: hard - -"@radix-ui/react-dropdown-menu@npm:^2.1.16": - version: 2.1.16 - resolution: "@radix-ui/react-dropdown-menu@npm:2.1.16" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-id": "npm:1.1.1" - "@radix-ui/react-menu": "npm:2.1.16" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-controllable-state": "npm:1.2.2" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/8caaa8dd791ccb284568720adafa59855e13860aa29eb20e10a04ba671cbbfa519a4c5d3a339a4d9fb08009eeb1065f4a8b5c3c8ef45e9753161cc560106b935 - languageName: node - linkType: hard - -"@radix-ui/react-focus-guards@npm:1.1.3": - version: 1.1.3 - resolution: "@radix-ui/react-focus-guards@npm:1.1.3" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/0bab65eb8d7e4f72f685d63de7fbba2450e3cb15ad6a20a16b42195e9d335c576356f5a47cb58d1ffc115393e46d7b14b12c5d4b10029b0ec090861255866985 - languageName: node - linkType: hard - -"@radix-ui/react-focus-scope@npm:1.1.7": - version: 1.1.7 - resolution: "@radix-ui/react-focus-scope@npm:1.1.7" - dependencies: - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/8a6071331bdeeb79b223463de75caf759b8ad19339cab838e537b8dbb2db236891a1f4df252445c854d375d43d9d315dfcce0a6b01553a2984ec372bb8f1300e - languageName: node - linkType: hard - -"@radix-ui/react-id@npm:1.1.1, @radix-ui/react-id@npm:^1.1.0": - version: 1.1.1 - resolution: "@radix-ui/react-id@npm:1.1.1" - dependencies: - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/7d12e76818763d592c331277ef62b197e2e64945307e650bd058f0090e5ae48bbd07691b23b7e9e977901ef4eadcb3e2d5eaeb17a13859083384be83fc1292c7 - languageName: node - linkType: hard - -"@radix-ui/react-label@npm:^2.1.7": - version: 2.1.7 - resolution: "@radix-ui/react-label@npm:2.1.7" - dependencies: - "@radix-ui/react-primitive": "npm:2.1.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/d8c81411d5327b6db5cbf4b900bfcc52030315539911701cf8d82b4970aed80cbd66df5b62d2242859572c666cf4b0e147a8b39dc3c04bd024a4b4405e1183fe - languageName: node - linkType: hard - -"@radix-ui/react-menu@npm:2.1.16": - version: 2.1.16 - resolution: "@radix-ui/react-menu@npm:2.1.16" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-collection": "npm:1.1.7" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-direction": "npm:1.1.1" - "@radix-ui/react-dismissable-layer": "npm:1.1.11" - "@radix-ui/react-focus-guards": "npm:1.1.3" - "@radix-ui/react-focus-scope": "npm:1.1.7" - "@radix-ui/react-id": "npm:1.1.1" - "@radix-ui/react-popper": "npm:1.2.8" - "@radix-ui/react-portal": "npm:1.1.9" - "@radix-ui/react-presence": "npm:1.1.5" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-roving-focus": "npm:1.1.11" - "@radix-ui/react-slot": "npm:1.2.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - aria-hidden: "npm:^1.2.4" - react-remove-scroll: "npm:^2.6.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/27516b2b987fa9181c4da8645000af8f60691866a349d7a46b9505fa7d2e9d92b9e364db4f7305d08e9e57d0e1afc8df8354f8ee3c12aa05c0100c16b0e76c27 - languageName: node - linkType: hard - -"@radix-ui/react-popover@npm:^1.1.15": - version: 1.1.15 - resolution: "@radix-ui/react-popover@npm:1.1.15" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-dismissable-layer": "npm:1.1.11" - "@radix-ui/react-focus-guards": "npm:1.1.3" - "@radix-ui/react-focus-scope": "npm:1.1.7" - "@radix-ui/react-id": "npm:1.1.1" - "@radix-ui/react-popper": "npm:1.2.8" - "@radix-ui/react-portal": "npm:1.1.9" - "@radix-ui/react-presence": "npm:1.1.5" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-slot": "npm:1.2.3" - "@radix-ui/react-use-controllable-state": "npm:1.2.2" - aria-hidden: "npm:^1.2.4" - react-remove-scroll: "npm:^2.6.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/c1c76b5e5985b128d03b621424fb453f769931d497759a1977734d303007da9f970570cf3ea1f6968ab609ab4a97f384168bff056197bd2d3d422abea0e3614b - languageName: node - linkType: hard - -"@radix-ui/react-popper@npm:1.2.8": - version: 1.2.8 - resolution: "@radix-ui/react-popper@npm:1.2.8" - dependencies: - "@floating-ui/react-dom": "npm:^2.0.0" - "@radix-ui/react-arrow": "npm:1.1.7" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - "@radix-ui/react-use-rect": "npm:1.1.1" - "@radix-ui/react-use-size": "npm:1.1.1" - "@radix-ui/rect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/48e3f13eac3b8c13aca8ded37d74db17e1bb294da8d69f142ab6b8719a06c3f90051668bed64520bf9f3abdd77b382ce7ce209d056bb56137cecc949b69b421c - languageName: node - linkType: hard - -"@radix-ui/react-portal@npm:1.1.9": - version: 1.1.9 - resolution: "@radix-ui/react-portal@npm:1.1.9" - dependencies: - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/45b432497c722720c72c493a29ef6085bc84b50eafe79d48b45c553121b63e94f9cdb77a3a74b9c49126f8feb3feee009fe400d48b7759d3552396356b192cd7 - languageName: node - linkType: hard - -"@radix-ui/react-presence@npm:1.1.5": - version: 1.1.5 - resolution: "@radix-ui/react-presence@npm:1.1.5" - dependencies: - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/d0e61d314250eeaef5369983cb790701d667f51734bafd98cf759072755562018052c594e6cdc5389789f4543cb0a4d98f03ff4e8f37338d6b5bf51a1700c1d1 - languageName: node - linkType: hard - -"@radix-ui/react-primitive@npm:2.1.3, @radix-ui/react-primitive@npm:^2.0.2": - version: 2.1.3 - resolution: "@radix-ui/react-primitive@npm:2.1.3" - dependencies: - "@radix-ui/react-slot": "npm:1.2.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/fdff9b84913bb4172ef6d3af7442fca5f9bba5f2709cba08950071f819d7057aec3a4a2d9ef44cf9cbfb8014d02573c6884a04cff175895823aaef809ebdb034 - languageName: node - linkType: hard - -"@radix-ui/react-roving-focus@npm:1.1.11": - version: 1.1.11 - resolution: "@radix-ui/react-roving-focus@npm:1.1.11" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-collection": "npm:1.1.7" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-direction": "npm:1.1.1" - "@radix-ui/react-id": "npm:1.1.1" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - "@radix-ui/react-use-controllable-state": "npm:1.2.2" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/2cd43339c36e89a3bf1db8aab34b939113dfbde56bf3a33df2d74757c78c9489b847b1962f1e2441c67e41817d120cb6177943e0f655f47bc1ff8e44fd55b1a2 - languageName: node - linkType: hard - -"@radix-ui/react-scroll-area@npm:^1.2.10": - version: 1.2.10 - resolution: "@radix-ui/react-scroll-area@npm:1.2.10" - dependencies: - "@radix-ui/number": "npm:1.1.1" - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-direction": "npm:1.1.1" - "@radix-ui/react-presence": "npm:1.1.5" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/8acdacd255fdfcefe4f72028a13dc554df73327db94c250f54a85b9608aa0313284dbb6c417f28a48e7b7b7bcaa76ddf3297e91ba07d833604d428f869259622 - languageName: node - linkType: hard - -"@radix-ui/react-slot@npm:1.2.3, @radix-ui/react-slot@npm:^1.2.3": - version: 1.2.3 - resolution: "@radix-ui/react-slot@npm:1.2.3" - dependencies: - "@radix-ui/react-compose-refs": "npm:1.1.2" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/5913aa0d760f505905779515e4b1f0f71a422350f077cc8d26d1aafe53c97f177fec0e6d7fbbb50d8b5e498aa9df9f707ca75ae3801540c283b26b0136138eef - languageName: node - linkType: hard - -"@radix-ui/react-toast@npm:^1.2.15": - version: 1.2.15 - resolution: "@radix-ui/react-toast@npm:1.2.15" - dependencies: - "@radix-ui/primitive": "npm:1.1.3" - "@radix-ui/react-collection": "npm:1.1.7" - "@radix-ui/react-compose-refs": "npm:1.1.2" - "@radix-ui/react-context": "npm:1.1.2" - "@radix-ui/react-dismissable-layer": "npm:1.1.11" - "@radix-ui/react-portal": "npm:1.1.9" - "@radix-ui/react-presence": "npm:1.1.5" - "@radix-ui/react-primitive": "npm:2.1.3" - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - "@radix-ui/react-use-controllable-state": "npm:1.2.2" - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - "@radix-ui/react-visually-hidden": "npm:1.2.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/e7050d356719f438e087e8033e47a8854fba001cf71eb4e0c0203d53a4fbbd35aca9f17f73abe4dadc406d2d85badf4e37065865d07835763d0e3cb9d95a518d - languageName: node - linkType: hard - -"@radix-ui/react-use-callback-ref@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/react-use-callback-ref@npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/5f6aff8592dea6a7e46589808912aba3fb3b626cf6edd2b14f01638b61dbbe49eeb9f67cd5601f4c15b2fb547b9a7e825f7c4961acd4dd70176c969ae405f8d8 - languageName: node - linkType: hard - -"@radix-ui/react-use-controllable-state@npm:1.2.2": - version: 1.2.2 - resolution: "@radix-ui/react-use-controllable-state@npm:1.2.2" - dependencies: - "@radix-ui/react-use-effect-event": "npm:0.0.2" - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/f55c4b06e895293aed4b44c9ef26fb24432539f5346fcd6519c7745800535b571058685314e83486a45bf61dc83887e24826490d3068acc317fb0a9010516e63 - languageName: node - linkType: hard - -"@radix-ui/react-use-effect-event@npm:0.0.2": - version: 0.0.2 - resolution: "@radix-ui/react-use-effect-event@npm:0.0.2" - dependencies: - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/e84ff72a3e76c5ae9c94941028bb4b6472f17d4104481b9eab773deab3da640ecea035e54da9d6f4df8d84c18ef6913baf92b7511bee06930dc58bd0c0add417 - languageName: node - linkType: hard - -"@radix-ui/react-use-escape-keydown@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/react-use-escape-keydown@npm:1.1.1" - dependencies: - "@radix-ui/react-use-callback-ref": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/bff53be99e940fef1d3c4df7d560e1d9133182e5a98336255d3063327d1d3dd4ec54a95dc5afe15cca4fb6c184f0a956c70de2815578c318cf995a7f9beabaa1 - languageName: node - linkType: hard - -"@radix-ui/react-use-layout-effect@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/react-use-layout-effect@npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/9f98fdaba008dfc58050de60a77670b885792df473cf82c1cef8daee919a5dd5a77d270209f5f0b0abfaac78cb1627396e3ff56c81b735be550409426fe8b040 - languageName: node - linkType: hard - -"@radix-ui/react-use-rect@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/react-use-rect@npm:1.1.1" - dependencies: - "@radix-ui/rect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/271711404c05c589c8dbdaa748749e7daf44bcc6bffc9ecd910821c3ebca0ee245616cf5b39653ce690f53f875c3836fd3f36f51ab1c628273b6db599eee4864 - languageName: node - linkType: hard - -"@radix-ui/react-use-size@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/react-use-size@npm:1.1.1" - dependencies: - "@radix-ui/react-use-layout-effect": "npm:1.1.1" - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/851d09a816f44282e0e9e2147b1b571410174cc048703a50c4fa54d672de994fd1dfff1da9d480ecfd12c77ae8f48d74f01adaf668f074156b8cd0043c6c21d8 - languageName: node - linkType: hard - -"@radix-ui/react-visually-hidden@npm:1.2.3": - version: 1.2.3 - resolution: "@radix-ui/react-visually-hidden@npm:1.2.3" - dependencies: - "@radix-ui/react-primitive": "npm:2.1.3" - peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/cf86a37f1cbee50a964056f3dc4f6bb1ee79c76daa321f913aa20ff3e1ccdfafbf2b114d7bb616aeefc7c4b895e6ca898523fdb67710d89bd5d8edb739a0d9b6 - languageName: node - linkType: hard - -"@radix-ui/rect@npm:1.1.1": - version: 1.1.1 - resolution: "@radix-ui/rect@npm:1.1.1" - checksum: 10c0/0dac4f0f15691199abe6a0e067821ddd9d0349c0c05f39834e4eafc8403caf724106884035ae91bbc826e10367e6a5672e7bec4d4243860fa7649de246b1f60b - languageName: node - linkType: hard - -"@rtsao/scc@npm:^1.1.0": - version: 1.1.0 - resolution: "@rtsao/scc@npm:1.1.0" - checksum: 10c0/b5bcfb0d87f7d1c1c7c0f7693f53b07866ed9fec4c34a97a8c948fb9a7c0082e416ce4d3b60beb4f5e167cbe04cdeefbf6771320f3ede059b9ce91188c409a5b - languageName: node - linkType: hard - -"@standard-schema/spec@npm:^1.1.0": - version: 1.1.0 - resolution: "@standard-schema/spec@npm:1.1.0" - checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526 - languageName: node - linkType: hard - -"@swc/helpers@npm:0.5.15": - version: 0.5.15 - resolution: "@swc/helpers@npm:0.5.15" - dependencies: - tslib: "npm:^2.8.0" - checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4 - languageName: node - linkType: hard - -"@tybys/wasm-util@npm:^0.10.0": - version: 0.10.0 - resolution: "@tybys/wasm-util@npm:0.10.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/044feba55c1e2af703aa4946139969badb183ce1a659a75ed60bc195a90e73a3f3fc53bcd643497c9954597763ddb051fec62f80962b2ca6fc716ba897dc696e - languageName: node - linkType: hard - -"@tybys/wasm-util@npm:^0.10.1": - version: 0.10.1 - resolution: "@tybys/wasm-util@npm:0.10.1" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/b255094f293794c6d2289300c5fbcafbb5532a3aed3a5ffd2f8dc1828e639b88d75f6a376dd8f94347a44813fd7a7149d8463477a9a49525c8b2dcaa38c2d1e8 - languageName: node - linkType: hard - -"@types/canvas-confetti@npm:^1.9.0": - version: 1.9.0 - resolution: "@types/canvas-confetti@npm:1.9.0" - checksum: 10c0/ffe2c674d466b8e13472c81ab2a97056f3433fd40a3513dbc1bb76764e4e7c3ff0a2a58d37b16ea6a245c831077c553db321b069dda6572eab59f2be61137b2e - languageName: node - linkType: hard - -"@types/esrecurse@npm:^4.3.1": - version: 4.3.1 - resolution: "@types/esrecurse@npm:4.3.1" - checksum: 10c0/90dad74d5da3ad27606d8e8e757322f33171cfeaa15ad558b615cf71bb2a516492d18f55f4816384685a3eb2412142e732bbae9a4a7cd2cf3deb7572aa4ebe03 - languageName: node - linkType: hard - -"@types/estree@npm:^1.0.6": - version: 1.0.8 - resolution: "@types/estree@npm:1.0.8" - checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 - languageName: node - linkType: hard - -"@types/estree@npm:^1.0.8": - version: 1.0.9 - resolution: "@types/estree@npm:1.0.9" - checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 - languageName: node - linkType: hard - -"@types/json-schema@npm:^7.0.15": - version: 7.0.15 - resolution: "@types/json-schema@npm:7.0.15" - checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db - languageName: node - linkType: hard - -"@types/json5@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/json5@npm:0.0.29" - checksum: 10c0/6bf5337bc447b706bb5b4431d37686aa2ea6d07cfd6f79cc31de80170d6ff9b1c7384a9c0ccbc45b3f512bae9e9f75c2e12109806a15331dc94e8a8db6dbb4ac - languageName: node - linkType: hard - -"@types/minimatch@npm:^6.0.0": - version: 6.0.0 - resolution: "@types/minimatch@npm:6.0.0" - dependencies: - minimatch: "npm:*" - checksum: 10c0/901fc4208cf6f5ab0e86bf86504055fb387f76092e06e604a3f2403db1c20e613c42104b235fde05552e0632d8f82e4ceacb83351d8700c7ffc5c31010a80804 - languageName: node - linkType: hard - -"@types/node@npm:*": - version: 25.9.1 - resolution: "@types/node@npm:25.9.1" - dependencies: - undici-types: "npm:>=7.24.0 <7.24.7" - checksum: 10c0/9a04682842bebbcf21a1779dfeab9aa733d7bd7bbc0a0edb641ab3a9a3d43eac543225acf669c334f458f1956443ebc072bc3c72840c543b8b356cab5c82d456 - languageName: node - linkType: hard - -"@types/node@npm:22.7.5": - version: 22.7.5 - resolution: "@types/node@npm:22.7.5" - dependencies: - undici-types: "npm:~6.19.2" - checksum: 10c0/cf11f74f1a26053ec58066616e3a8685b6bcd7259bc569738b8f752009f9f0f7f85a1b2d24908e5b0f752482d1e8b6babdf1fbb25758711ec7bb9500bfcd6e60 - languageName: node - linkType: hard - -"@types/node@npm:^20": - version: 20.19.9 - resolution: "@types/node@npm:20.19.9" - dependencies: - undici-types: "npm:~6.21.0" - checksum: 10c0/c6738131f1698258a5ac1e0185e4fc56977f7f566cd0ee11167f93f2339478470257bd82c5e1908a936a204e0ad7996d741356a1a07c04997a236161ea23a874 - languageName: node - linkType: hard - -"@types/react-dom@npm:^19.2.3": - version: 19.2.3 - resolution: "@types/react-dom@npm:19.2.3" - peerDependencies: - "@types/react": ^19.2.0 - checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 - languageName: node - linkType: hard - -"@types/react@npm:^19.2.7": - version: 19.2.7 - resolution: "@types/react@npm:19.2.7" - dependencies: - csstype: "npm:^3.2.2" - checksum: 10c0/a7b75f1f9fcb34badd6f84098be5e35a0aeca614bc91f93d2698664c0b2ba5ad128422bd470ada598238cebe4f9e604a752aead7dc6f5a92261d0c7f9b27cfd1 - languageName: node - linkType: hard - -"@types/ws@npm:^8.18.1": - version: 8.18.1 - resolution: "@types/ws@npm:8.18.1" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/61aff1129143fcc4312f083bc9e9e168aa3026b7dd6e70796276dcfb2c8211c4292603f9c4864fae702f2ed86e4abd4d38aa421831c2fd7f856c931a481afbab - languageName: node - linkType: hard - -"@typescript-eslint/eslint-plugin@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/eslint-plugin@npm:8.57.1" - dependencies: - "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.57.1" - "@typescript-eslint/type-utils": "npm:8.57.1" - "@typescript-eslint/utils": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" - ignore: "npm:^7.0.5" - natural-compare: "npm:^1.4.0" - ts-api-utils: "npm:^2.4.0" - peerDependencies: - "@typescript-eslint/parser": ^8.57.1 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/5bf9227f5d608d4313c9f898da3a2f6737eca985aa925df9e90b73499b9d552221781d3d09245543c6d09995ab262ea0d6773d2dae4b8bdf319765d46b22d0e1 - languageName: node - linkType: hard - -"@typescript-eslint/parser@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/parser@npm:8.57.1" - dependencies: - "@typescript-eslint/scope-manager": "npm:8.57.1" - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" - debug: "npm:^4.4.3" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/ab624f5ad6f3585ee690d11be36597135779a373e7f07810ed921163de2e879000f6d3213db67413ee630bcf25d5cfaa24b089ee49596cd11b0456372bc17163 - languageName: node - linkType: hard - -"@typescript-eslint/project-service@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/project-service@npm:8.57.1" - dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.57.1" - "@typescript-eslint/types": "npm:^8.57.1" - debug: "npm:^4.4.3" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/7830f61e35364ba77799f4badeaca8bd8914bbcda6afe37b788821f94f4b88b9c49817c50f4bdba497e8e542a705e9d921d36f5e67960ebf33f4f3d3111cdfee - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/scope-manager@npm:8.57.1" - dependencies: - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" - checksum: 10c0/42b0b54981318bf21be6b107df82910718497b7b7b2b60df635aa06d78e313759e4b675830c0e542b6d87104d35b49df41b9fb7739b8ae326eaba2d6f7116166 - languageName: node - linkType: hard - -"@typescript-eslint/tsconfig-utils@npm:8.57.1, @typescript-eslint/tsconfig-utils@npm:^8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.1" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/3d3c8d80621507d31e4656c693534f28a1c04dfb047538cb79b0b6da874ef41875f5df5e814fa3a38812451cff6d5a7ae38d0bf77eb7fec7867f9c80af361b00 - languageName: node - linkType: hard - -"@typescript-eslint/type-utils@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/type-utils@npm:8.57.1" - dependencies: - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" - "@typescript-eslint/utils": "npm:8.57.1" - debug: "npm:^4.4.3" - ts-api-utils: "npm:^2.4.0" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/e8eae4e3b9ca71ad065c307fd3cdefdcc6abc31bda2ef74f0e54b5c9ac0ee6bc0e2d69ec9097899f4d7a99d4a8a72391503b47f4317b3b6b9ba41cea24e6b9e9 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:8.57.1, @typescript-eslint/types@npm:^8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/types@npm:8.57.1" - checksum: 10c0/f447015276a31871440b07e328c2bbcee8337d72dca90ae00ac91e87d09e28a8a9c2fe44726a5226fcaa7db9d5347aafa650d59f7577a074dc65ea1414d24da1 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:^8.59.3": - version: 8.60.0 - resolution: "@typescript-eslint/types@npm:8.60.0" - checksum: 10c0/d2b6d46081a6521f204fda30e8f03712480b788d80b62b311e0f33764752d3db3bd415dd4e1f8d28495931316da1dfb5ee259e40c5de970367fbaa1efe97223f - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.57.1" - dependencies: - "@typescript-eslint/project-service": "npm:8.57.1" - "@typescript-eslint/tsconfig-utils": "npm:8.57.1" - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" - debug: "npm:^4.4.3" - minimatch: "npm:^10.2.2" - semver: "npm:^7.7.3" - tinyglobby: "npm:^0.2.15" - ts-api-utils: "npm:^2.4.0" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/a87e1d920a8fd2231b6a98b279dc7680d10ceac072001e85a72cd43adce288ed471afcaf8f171378f5a3221c500b3cf0ffc10a75fd521fb69fbd8b26d4626677 - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/utils@npm:8.57.1" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.57.1" - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/c85d6e7c618dbf902fda98cc795883388bc512bc2c34c7ac0481ea43acb6dd3cd38d60bdb571b586f392419a17998c89330fd7b0b9a344161f4a595637dd3f55 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.57.1" - dependencies: - "@typescript-eslint/types": "npm:8.57.1" - eslint-visitor-keys: "npm:^5.0.0" - checksum: 10c0/088a545c4aec6d9cabb266e1e40634f5fafa06cb05ef172526555957b0d99ac08822733fb788a09227071fdd6bd8b63f054393a0ecf9d4599c54b57918aa0e57 - languageName: node - linkType: hard - -"@unrs/resolver-binding-android-arm-eabi@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.11.1" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-android-arm64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-android-arm64@npm:1.11.1" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-darwin-arm64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.11.1" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-darwin-x64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-darwin-x64@npm:1.11.1" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-freebsd-x64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.11.1" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.11.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.11.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.11.1" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm64-musl@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.11.1" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.11.1" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.11.1" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-riscv64-musl@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.11.1" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-s390x-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.11.1" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-x64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.11.1" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-x64-musl@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.11.1" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-wasm32-wasi@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.11.1" - dependencies: - "@napi-rs/wasm-runtime": "npm:^0.2.11" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@upstash/core-analytics@npm:^0.0.10": - version: 0.0.10 - resolution: "@upstash/core-analytics@npm:0.0.10" - dependencies: - "@upstash/redis": "npm:^1.28.3" - checksum: 10c0/c90b1bfaf1f54eed8a48f2aa679f8e082083f49931cf08bd90a0a0035b010f65b6e7420268677f15df1d04687f7e5a9093320c591ef4dbd45d804ea83d890f3f - languageName: node - linkType: hard - -"@upstash/ratelimit@npm:2.0.6": - version: 2.0.6 - resolution: "@upstash/ratelimit@npm:2.0.6" - dependencies: - "@upstash/core-analytics": "npm:^0.0.10" - peerDependencies: - "@upstash/redis": ^1.34.3 - checksum: 10c0/cc563f3bf23f6682eca937df7d317c35af945758cbcef5e6c82b4d9ac757725fb7e7885b111a3c3bcdeddb4f1a007ceb12549edbbdb7fb166535947b35bf57ef - languageName: node - linkType: hard - -"@upstash/redis@npm:1.35.3, @upstash/redis@npm:^1.28.3, @upstash/redis@npm:^1.34.0": - version: 1.35.3 - resolution: "@upstash/redis@npm:1.35.3" - dependencies: - uncrypto: "npm:^0.1.3" - checksum: 10c0/e81ae1341ac73dcc497234490524f9bf435b23d321ce460dae26c664440b9257344d1a994de91889bcf64e7ab48fa0f02ac58f8206958b4399ec7f1fff617fe1 - languageName: node - linkType: hard - -"@vercel/analytics@npm:^1.5.0": - version: 1.5.0 - resolution: "@vercel/analytics@npm:1.5.0" - peerDependencies: - "@remix-run/react": ^2 - "@sveltejs/kit": ^1 || ^2 - next: ">= 13" - react: ^18 || ^19 || ^19.0.0-rc - svelte: ">= 4" - vue: ^3 - vue-router: ^4 - peerDependenciesMeta: - "@remix-run/react": - optional: true - "@sveltejs/kit": - optional: true - next: - optional: true - react: - optional: true - svelte: - optional: true - vue: - optional: true - vue-router: - optional: true - checksum: 10c0/43d33ea83b32f5203fec21b7f43c399e398f0c37d2dd341d522969e0e6ee23fd652a2766a4203a3ce573f711beee5ee1ab7d36316f767a4901160e3e96ee31e5 - languageName: node - linkType: hard - -"@vercel/kv@npm:3.0.0": - version: 3.0.0 - resolution: "@vercel/kv@npm:3.0.0" - dependencies: - "@upstash/redis": "npm:^1.34.0" - checksum: 10c0/55c0e197967c21161ba7bbee02be821975d8e900f2ca3946c9bfaefa1cb5ce94f9ce04aef07aca027c8c39a5ab2e9a2b54edc5c6b617c994d2a8fddfd9d55dc8 - languageName: node - linkType: hard - -"abbrev@npm:^4.0.0": - version: 4.0.0 - resolution: "abbrev@npm:4.0.0" - checksum: 10c0/b4cc16935235e80702fc90192e349e32f8ef0ed151ef506aa78c81a7c455ec18375c4125414b99f84b2e055199d66383e787675f0bcd87da7a4dbd59f9eac1d5 - languageName: node - linkType: hard - -"acorn-jsx@npm:^5.3.2": - version: 5.3.2 - resolution: "acorn-jsx@npm:5.3.2" - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1 - languageName: node - linkType: hard - -"acorn@npm:^8.15.0": - version: 8.15.0 - resolution: "acorn@npm:8.15.0" - bin: - acorn: bin/acorn - checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec - languageName: node - linkType: hard - -"aes-js@npm:4.0.0-beta.5": - version: 4.0.0-beta.5 - resolution: "aes-js@npm:4.0.0-beta.5" - checksum: 10c0/444f4eefa1e602cbc4f2a3c644bc990f93fd982b148425fee17634da510586fc09da940dcf8ace1b2d001453c07ff042e55f7a0482b3cc9372bf1ef75479090c - languageName: node - linkType: hard - -"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": - version: 7.1.4 - resolution: "agent-base@npm:7.1.4" - checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe - languageName: node - linkType: hard - -"agent-install@npm:0.0.5": - version: 0.0.5 - resolution: "agent-install@npm:0.0.5" - dependencies: - "@iarna/toml": "npm:^2.2.5" - commander: "npm:^14.0.0" - jsonc-parser: "npm:^3.3.1" - picocolors: "npm:^1.1.1" - prompts: "npm:^2.4.2" - yaml: "npm:^2.8.3" - bin: - agent-install: bin/agent-install.mjs - checksum: 10c0/57b740ff36d08716c5985ee6d0fc973fb59a55100283d62ac8ed58e889bf99c2eff040c971f9e72ed8ec426c70ef2f2c37db7534f02b516d46a3a11a293a9b8a - languageName: node - linkType: hard - -"ajv-formats@npm:^3.0.1": - version: 3.0.1 - resolution: "ajv-formats@npm:3.0.1" - dependencies: - ajv: "npm:^8.0.0" - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - checksum: 10c0/168d6bca1ea9f163b41c8147bae537e67bd963357a5488a1eaf3abe8baa8eec806d4e45f15b10767e6020679315c7e1e5e6803088dfb84efa2b4e9353b83dd0a - languageName: node - linkType: hard - -"ajv@npm:^6.12.4": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" - dependencies: - fast-deep-equal: "npm:^3.1.1" - fast-json-stable-stringify: "npm:^2.0.0" - json-schema-traverse: "npm:^0.4.1" - uri-js: "npm:^4.2.2" - checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 - languageName: node - linkType: hard - -"ajv@npm:^8.0.0, ajv@npm:^8.17.1": - version: 8.20.0 - resolution: "ajv@npm:8.20.0" - dependencies: - fast-deep-equal: "npm:^3.1.3" - fast-uri: "npm:^3.0.1" - json-schema-traverse: "npm:^1.0.0" - require-from-string: "npm:^2.0.2" - checksum: 10c0/5df9a1c8f83863cde1bd3a9ddb426f599718f88e3dc9153616c79fb28e0be455335830d7f21d745576519f057b371352daa31047b6a33d7036fe08777d60cf2a - languageName: node - linkType: hard - -"ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 - languageName: node - linkType: hard - -"any-promise@npm:^1.0.0": - version: 1.3.0 - resolution: "any-promise@npm:1.3.0" - checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 - languageName: node - linkType: hard - -"anymatch@npm:~3.1.2": - version: 3.1.3 - resolution: "anymatch@npm:3.1.3" - dependencies: - normalize-path: "npm:^3.0.0" - picomatch: "npm:^2.0.4" - checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac - languageName: node - linkType: hard - -"arg@npm:^5.0.2": - version: 5.0.2 - resolution: "arg@npm:5.0.2" - checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e - languageName: node - linkType: hard - -"argparse@npm:^2.0.1": - version: 2.0.1 - resolution: "argparse@npm:2.0.1" - checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e - languageName: node - linkType: hard - -"aria-hidden@npm:^1.2.4": - version: 1.2.6 - resolution: "aria-hidden@npm:1.2.6" - dependencies: - tslib: "npm:^2.0.0" - checksum: 10c0/7720cb539497a9f760f68f98a4b30f22c6767aa0e72fa7d58279f7c164e258fc38b2699828f8de881aab0fc8e9c56d1313a3f1a965046fc0381a554dbc72b54a - languageName: node - linkType: hard - -"aria-query@npm:^5.3.2": - version: 5.3.2 - resolution: "aria-query@npm:5.3.2" - checksum: 10c0/003c7e3e2cff5540bf7a7893775fc614de82b0c5dde8ae823d47b7a28a9d4da1f7ed85f340bdb93d5649caa927755f0e31ecc7ab63edfdfc00c8ef07e505e03e - languageName: node - linkType: hard - -"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": - version: 1.0.2 - resolution: "array-buffer-byte-length@npm:1.0.2" - dependencies: - call-bound: "npm:^1.0.3" - is-array-buffer: "npm:^3.0.5" - checksum: 10c0/74e1d2d996941c7a1badda9cabb7caab8c449db9086407cad8a1b71d2604cc8abf105db8ca4e02c04579ec58b7be40279ddb09aea4784832984485499f48432d - languageName: node - linkType: hard - -"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8, array-includes@npm:^3.1.9": - version: 3.1.9 - resolution: "array-includes@npm:3.1.9" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.4" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.24.0" - es-object-atoms: "npm:^1.1.1" - get-intrinsic: "npm:^1.3.0" - is-string: "npm:^1.1.1" - math-intrinsics: "npm:^1.1.0" - checksum: 10c0/0235fa69078abeac05ac4250699c44996bc6f774a9cbe45db48674ce6bd142f09b327d31482ff75cf03344db4ea03eae23edb862d59378b484b47ed842574856 - languageName: node - linkType: hard - -"array.prototype.findlast@npm:^1.2.5": - version: 1.2.5 - resolution: "array.prototype.findlast@npm:1.2.5" - dependencies: - call-bind: "npm:^1.0.7" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.2" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.0.0" - es-shim-unscopables: "npm:^1.0.2" - checksum: 10c0/ddc952b829145ab45411b9d6adcb51a8c17c76bf89c9dd64b52d5dffa65d033da8c076ed2e17091779e83bc892b9848188d7b4b33453c5565e65a92863cb2775 - languageName: node - linkType: hard - -"array.prototype.findlastindex@npm:^1.2.6": - version: 1.2.6 - resolution: "array.prototype.findlastindex@npm:1.2.6" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.4" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.9" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.1.1" - es-shim-unscopables: "npm:^1.1.0" - checksum: 10c0/82559310d2e57ec5f8fc53d7df420e3abf0ba497935de0a5570586035478ba7d07618cb18e2d4ada2da514c8fb98a034aaf5c06caa0a57e2f7f4c4adedef5956 - languageName: node - linkType: hard - -"array.prototype.flat@npm:^1.3.1, array.prototype.flat@npm:^1.3.3": - version: 1.3.3 - resolution: "array.prototype.flat@npm:1.3.3" - dependencies: - call-bind: "npm:^1.0.8" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.5" - es-shim-unscopables: "npm:^1.0.2" - checksum: 10c0/d90e04dfbc43bb96b3d2248576753d1fb2298d2d972e29ca7ad5ec621f0d9e16ff8074dae647eac4f31f4fb7d3f561a7ac005fb01a71f51705a13b5af06a7d8a - languageName: node - linkType: hard - -"array.prototype.flatmap@npm:^1.3.2, array.prototype.flatmap@npm:^1.3.3": - version: 1.3.3 - resolution: "array.prototype.flatmap@npm:1.3.3" - dependencies: - call-bind: "npm:^1.0.8" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.5" - es-shim-unscopables: "npm:^1.0.2" - checksum: 10c0/ba899ea22b9dc9bf276e773e98ac84638ed5e0236de06f13d63a90b18ca9e0ec7c97d622d899796e3773930b946cd2413d098656c0c5d8cc58c6f25c21e6bd54 - languageName: node - linkType: hard - -"array.prototype.tosorted@npm:^1.1.4": - version: 1.1.4 - resolution: "array.prototype.tosorted@npm:1.1.4" - dependencies: - call-bind: "npm:^1.0.7" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.3" - es-errors: "npm:^1.3.0" - es-shim-unscopables: "npm:^1.0.2" - checksum: 10c0/eb3c4c4fc0381b0bf6dba2ea4d48d367c2827a0d4236a5718d97caaccc6b78f11f4cadf090736e86301d295a6aa4967ed45568f92ced51be8cbbacd9ca410943 - languageName: node - linkType: hard - -"arraybuffer.prototype.slice@npm:^1.0.4": - version: 1.0.4 - resolution: "arraybuffer.prototype.slice@npm:1.0.4" - dependencies: - array-buffer-byte-length: "npm:^1.0.1" - call-bind: "npm:^1.0.8" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.5" - es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.6" - is-array-buffer: "npm:^3.0.4" - checksum: 10c0/2f2459caa06ae0f7f615003f9104b01f6435cc803e11bd2a655107d52a1781dc040532dc44d93026b694cc18793993246237423e13a5337e86b43ed604932c06 - languageName: node - linkType: hard - -"ast-types-flow@npm:^0.0.8": - version: 0.0.8 - resolution: "ast-types-flow@npm:0.0.8" - checksum: 10c0/f2a0ba8055353b743c41431974521e5e852a9824870cd6fce2db0e538ac7bf4da406bbd018d109af29ff3f8f0993f6a730c9eddbd0abd031fbcb29ca75c1014e - languageName: node - linkType: hard - -"async-function@npm:^1.0.0": - version: 1.0.0 - resolution: "async-function@npm:1.0.0" - checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73 - languageName: node - linkType: hard - -"atomically@npm:^2.0.3": - version: 2.1.1 - resolution: "atomically@npm:2.1.1" - dependencies: - stubborn-fs: "npm:^2.0.0" - when-exit: "npm:^2.1.4" - checksum: 10c0/8813decdea834eab9b95c63ae3762355e9182e718b49be50153539bb52f727851f5096ef180f84901572dac31c51cb113a3bf3dda12fa633a16bc58f49ba003d - languageName: node - linkType: hard - -"autoprefixer@npm:^10.4.21": - version: 10.4.21 - resolution: "autoprefixer@npm:10.4.21" - dependencies: - browserslist: "npm:^4.24.4" - caniuse-lite: "npm:^1.0.30001702" - fraction.js: "npm:^4.3.7" - normalize-range: "npm:^0.1.2" - picocolors: "npm:^1.1.1" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.1.0 - bin: - autoprefixer: bin/autoprefixer - checksum: 10c0/de5b71d26d0baff4bbfb3d59f7cf7114a6030c9eeb66167acf49a32c5b61c68e308f1e0f869d92334436a221035d08b51cd1b2f2c4689b8d955149423c16d4d4 - languageName: node - linkType: hard - -"available-typed-arrays@npm:^1.0.7": - version: 1.0.7 - resolution: "available-typed-arrays@npm:1.0.7" - dependencies: - possible-typed-array-names: "npm:^1.0.0" - checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 - languageName: node - linkType: hard - -"axe-core@npm:^4.10.0": - version: 4.10.3 - resolution: "axe-core@npm:4.10.3" - checksum: 10c0/1b1c24f435b2ffe89d76eca0001cbfff42dbf012ad9bd37398b70b11f0d614281a38a28bc3069e8972e3c90ec929a8937994bd24b0ebcbaab87b8d1e241ab0c7 - languageName: node - linkType: hard - -"axobject-query@npm:^4.1.0": - version: 4.1.0 - resolution: "axobject-query@npm:4.1.0" - checksum: 10c0/c470e4f95008f232eadd755b018cb55f16c03ccf39c027b941cd8820ac6b68707ce5d7368a46756db4256fbc91bb4ead368f84f7fb034b2b7932f082f6dc0775 - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee - languageName: node - linkType: hard - -"balanced-match@npm:^4.0.2": - version: 4.0.4 - resolution: "balanced-match@npm:4.0.4" - checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b - languageName: node - linkType: hard - -"baseline-browser-mapping@npm:^2.9.0": - version: 2.9.19 - resolution: "baseline-browser-mapping@npm:2.9.19" - bin: - baseline-browser-mapping: dist/cli.js - checksum: 10c0/569928db78bcd081953d7db79e4243a59a579a34b4ae1806b9b42d3b7f84e5bc40e6e82ae4fa06e7bef8291bf747b33b3f9ef5d3c6e1e420cb129d9295536129 - languageName: node - linkType: hard - -"baseline-browser-mapping@npm:^2.9.19": - version: 2.10.8 - resolution: "baseline-browser-mapping@npm:2.10.8" - bin: - baseline-browser-mapping: dist/cli.cjs - checksum: 10c0/a77882e8ac37a900a9a0757b9bf4e50f407829ed17ee7630273ab5da0ae10d9c64ed4c5a1e03afb3490e39713440092417ded4bb856eeb9bd44856eacbd97497 - languageName: node - linkType: hard - -"binary-extensions@npm:^2.0.0": - version: 2.3.0 - resolution: "binary-extensions@npm:2.3.0" - checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 - languageName: node - linkType: hard - -"brace-expansion@npm:1.1.13": - version: 1.1.13 - resolution: "brace-expansion@npm:1.1.13" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 10c0/384c61bb329b6adfdcc0cbbdd108dc19fb5f3e84ae15a02a74f94c6c791b5a9b035aae73b2a51929a8a478e2f0f212a771eb6a8b5b514cccfb8d0c9f2ce8cbd8 - languageName: node - linkType: hard - -"brace-expansion@npm:^5.0.2": - version: 5.0.4 - resolution: "brace-expansion@npm:5.0.4" - dependencies: - balanced-match: "npm:^4.0.2" - checksum: 10c0/359cbcfa80b2eb914ca1f3440e92313fbfe7919ee6b274c35db55bec555aded69dac5ee78f102cec90c35f98c20fa43d10936d0cd9978158823c249257e1643a - languageName: node - linkType: hard - -"brace-expansion@npm:^5.0.5": - version: 5.0.6 - resolution: "brace-expansion@npm:5.0.6" - dependencies: - balanced-match: "npm:^4.0.2" - checksum: 10c0/8c919869b90f61d533b341d3340be5ee4413232ea89b8246cbc2f38eb014f1d8182785c98a006eaf6111d02dc9eeffefdc240d5ac158625b2ed084dccd4bbf9b - languageName: node - linkType: hard - -"braces@npm:^3.0.3, braces@npm:~3.0.2": - version: 3.0.3 - resolution: "braces@npm:3.0.3" - dependencies: - fill-range: "npm:^7.1.1" - checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 - languageName: node - linkType: hard - -"browserslist@npm:^4.24.0": - version: 4.28.1 - resolution: "browserslist@npm:4.28.1" - dependencies: - baseline-browser-mapping: "npm:^2.9.0" - caniuse-lite: "npm:^1.0.30001759" - electron-to-chromium: "npm:^1.5.263" - node-releases: "npm:^2.0.27" - update-browserslist-db: "npm:^1.2.0" - bin: - browserslist: cli.js - checksum: 10c0/545a5fa9d7234e3777a7177ec1e9134bb2ba60a69e6b95683f6982b1473aad347c77c1264ccf2ac5dea609a9731fbfbda6b85782bdca70f80f86e28a402504bd - languageName: node - linkType: hard - -"browserslist@npm:^4.24.4": - version: 4.25.4 - resolution: "browserslist@npm:4.25.4" - dependencies: - caniuse-lite: "npm:^1.0.30001737" - electron-to-chromium: "npm:^1.5.211" - node-releases: "npm:^2.0.19" - update-browserslist-db: "npm:^1.1.3" - bin: - browserslist: cli.js - checksum: 10c0/2b105948990dc2fc0bc2536b4889aadfa15d637e1d857a121611a704cdf539a68f575a391f6bf8b7ff19db36cee1b7834565571f35a7ea691051d2e7fb4f2eb1 - languageName: node - linkType: hard - -"cacache@npm:^20.0.1": - version: 20.0.4 - resolution: "cacache@npm:20.0.4" - dependencies: - "@npmcli/fs": "npm:^5.0.0" - fs-minipass: "npm:^3.0.0" - glob: "npm:^13.0.0" - lru-cache: "npm:^11.1.0" - minipass: "npm:^7.0.3" - minipass-collect: "npm:^2.0.1" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - p-map: "npm:^7.0.2" - ssri: "npm:^13.0.0" - checksum: 10c0/539bf4020e44ba9ca5afc2ec435623ed7e0dd80c020097677e6b4a0545df5cc9d20b473212d01209c8b4aea43c0d095af0bb6da97bcb991642ea6fac0d7c462b - languageName: node - linkType: hard - -"call-bind-apply-helpers@npm:^1.0.0, call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind-apply-helpers@npm:1.0.2" - dependencies: - es-errors: "npm:^1.3.0" - function-bind: "npm:^1.1.2" - checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 - languageName: node - linkType: hard - -"call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": - version: 1.0.8 - resolution: "call-bind@npm:1.0.8" - dependencies: - call-bind-apply-helpers: "npm:^1.0.0" - es-define-property: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.4" - set-function-length: "npm:^1.2.2" - checksum: 10c0/a13819be0681d915144467741b69875ae5f4eba8961eb0bf322aab63ec87f8250eb6d6b0dcbb2e1349876412a56129ca338592b3829ef4343527f5f18a0752d4 - languageName: node - linkType: hard - -"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3, call-bound@npm:^1.0.4": - version: 1.0.4 - resolution: "call-bound@npm:1.0.4" - dependencies: - call-bind-apply-helpers: "npm:^1.0.2" - get-intrinsic: "npm:^1.3.0" - checksum: 10c0/f4796a6a0941e71c766aea672f63b72bc61234c4f4964dc6d7606e3664c307e7d77845328a8f3359ce39ddb377fed67318f9ee203dea1d47e46165dcf2917644 - languageName: node - linkType: hard - -"callsites@npm:^3.0.0": - version: 3.1.0 - resolution: "callsites@npm:3.1.0" - checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 - languageName: node - linkType: hard - -"camelcase-css@npm:^2.0.1": - version: 2.0.1 - resolution: "camelcase-css@npm:2.0.1" - checksum: 10c0/1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001579": - version: 1.0.30001731 - resolution: "caniuse-lite@npm:1.0.30001731" - checksum: 10c0/d8cddf817d5bec8e7c2106affdbf1bfc3923463ca16697c992b2efeb043e6a5d9dcb70cda913bc6acf9112fd66f9e80279316c08e7800359116925066a63fdfa - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001737": - version: 1.0.30001741 - resolution: "caniuse-lite@npm:1.0.30001741" - checksum: 10c0/45746f896205a61a8eeb85a32aeca243ebce640cd6eb80d04949d9389a13f4659c737860300d7b988057599f0958c55eeab74ec02ce9ef137feb7d006e75fec1 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001770 - resolution: "caniuse-lite@npm:1.0.30001770" - checksum: 10c0/02d15a8b723af65318cb4d888a52bb090076898da7b0de99e8676d537f8d1d2ae4797e81518e1e30cbfe84c33b048c322e8bfafc5b23cfee8defb0d2bf271149 - languageName: node - linkType: hard - -"canvas-confetti@npm:^1.9.3": - version: 1.9.3 - resolution: "canvas-confetti@npm:1.9.3" - checksum: 10c0/94c6f16591660d5ed4a48afb8da65902826ce6b38edc7644d62521aa9bf9ef5b950aa4396e980e7fe0a38e5f41a991a6d984721412a54c9fba4de3682c1eead0 - languageName: node - linkType: hard - -"chalk@npm:^4.0.0": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 - languageName: node - linkType: hard - -"chokidar@npm:^3.6.0": - version: 3.6.0 - resolution: "chokidar@npm:3.6.0" - dependencies: - anymatch: "npm:~3.1.2" - braces: "npm:~3.0.2" - fsevents: "npm:~2.3.2" - glob-parent: "npm:~5.1.2" - is-binary-path: "npm:~2.1.0" - is-glob: "npm:~4.0.1" - normalize-path: "npm:~3.0.0" - readdirp: "npm:~3.6.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 - languageName: node - linkType: hard - -"chownr@npm:^3.0.0": - version: 3.0.0 - resolution: "chownr@npm:3.0.0" - checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 - languageName: node - linkType: hard - -"class-variance-authority@npm:^0.7.1": - version: 0.7.1 - resolution: "class-variance-authority@npm:0.7.1" - dependencies: - clsx: "npm:^2.1.1" - checksum: 10c0/0f438cea22131808b99272de0fa933c2532d5659773bfec0c583de7b3f038378996d3350683426b8e9c74a6286699382106d71fbec52f0dd5fbb191792cccb5b - languageName: node - linkType: hard - -"classnames@npm:^2.5.1": - version: 2.5.1 - resolution: "classnames@npm:2.5.1" - checksum: 10c0/afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 - languageName: node - linkType: hard - -"client-only@npm:0.0.1": - version: 0.0.1 - resolution: "client-only@npm:0.0.1" - checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 - languageName: node - linkType: hard - -"clsx@npm:^2.1.1": - version: 2.1.1 - resolution: "clsx@npm:2.1.1" - checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 - languageName: node - linkType: hard - -"cmdk@npm:^1.1.1": - version: 1.1.1 - resolution: "cmdk@npm:1.1.1" - dependencies: - "@radix-ui/react-compose-refs": "npm:^1.1.1" - "@radix-ui/react-dialog": "npm:^1.1.6" - "@radix-ui/react-id": "npm:^1.1.0" - "@radix-ui/react-primitive": "npm:^2.0.2" - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - react-dom: ^18 || ^19 || ^19.0.0-rc - checksum: 10c0/5605ac4396ec9bc65c82f954da19dd89a0636a54026df72780e2470da1381f9d57434a80a53f2d57eaa4e759660a3ebba9232b74258dc09970576591eae03116 - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 - languageName: node - linkType: hard - -"commander@npm:^14.0.0": - version: 14.0.3 - resolution: "commander@npm:14.0.3" - checksum: 10c0/755652564bbf56ff2ff083313912b326450d3f8d8c85f4b71416539c9a05c3c67dbd206821ca72635bf6b160e2afdefcb458e86b317827d5cb333b69ce7f1a24 - languageName: node - linkType: hard - -"commander@npm:^4.0.0": - version: 4.1.1 - resolution: "commander@npm:4.1.1" - checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f - languageName: node - linkType: hard - -"conf@npm:^15.1.0": - version: 15.1.0 - resolution: "conf@npm:15.1.0" - dependencies: - ajv: "npm:^8.17.1" - ajv-formats: "npm:^3.0.1" - atomically: "npm:^2.0.3" - debounce-fn: "npm:^6.0.0" - dot-prop: "npm:^10.0.0" - env-paths: "npm:^3.0.0" - json-schema-typed: "npm:^8.0.1" - semver: "npm:^7.7.2" - uint8array-extras: "npm:^1.5.0" - checksum: 10c0/ba3c74891f48232a7d32e473508c147a431a7bfdd266ebdeaf4e737227568345ea632a42d02e35f13740cb9a54076d79572d31591d6debae3b6bee089fa91c8a - languageName: node - linkType: hard - -"convert-source-map@npm:^2.0.0": - version: 2.0.0 - resolution: "convert-source-map@npm:2.0.0" - checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b - languageName: node - linkType: hard - -"country-flag-icons@npm:^1.5.17": - version: 1.5.19 - resolution: "country-flag-icons@npm:1.5.19" - checksum: 10c0/f6adeadd7a406c593c8cfe4b3f0630d937188f79854755677448bd89024d404307b7a38b03612d5310c77c45ea535e0c409f24dc80d787673f1e3b35ddae43de - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.6": - version: 7.0.6 - resolution: "cross-spawn@npm:7.0.6" - dependencies: - path-key: "npm:^3.1.0" - shebang-command: "npm:^2.0.0" - which: "npm:^2.0.1" - checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 - languageName: node - linkType: hard - -"cssesc@npm:^3.0.0": - version: 3.0.0 - resolution: "cssesc@npm:3.0.0" - bin: - cssesc: bin/cssesc - checksum: 10c0/6bcfd898662671be15ae7827120472c5667afb3d7429f1f917737f3bf84c4176003228131b643ae74543f17a394446247df090c597bb9a728cce298606ed0aa7 - languageName: node - linkType: hard - -"csstype@npm:^3.2.2": - version: 3.2.3 - resolution: "csstype@npm:3.2.3" - checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce - languageName: node - linkType: hard - -"damerau-levenshtein@npm:^1.0.8": - version: 1.0.8 - resolution: "damerau-levenshtein@npm:1.0.8" - checksum: 10c0/4c2647e0f42acaee7d068756c1d396e296c3556f9c8314bac1ac63ffb236217ef0e7e58602b18bb2173deec7ec8e0cac8e27cccf8f5526666b4ff11a13ad54a3 - languageName: node - linkType: hard - -"data-view-buffer@npm:^1.0.2": - version: 1.0.2 - resolution: "data-view-buffer@npm:1.0.2" - dependencies: - call-bound: "npm:^1.0.3" - es-errors: "npm:^1.3.0" - is-data-view: "npm:^1.0.2" - checksum: 10c0/7986d40fc7979e9e6241f85db8d17060dd9a71bd53c894fa29d126061715e322a4cd47a00b0b8c710394854183d4120462b980b8554012acc1c0fa49df7ad38c - languageName: node - linkType: hard - -"data-view-byte-length@npm:^1.0.2": - version: 1.0.2 - resolution: "data-view-byte-length@npm:1.0.2" - dependencies: - call-bound: "npm:^1.0.3" - es-errors: "npm:^1.3.0" - is-data-view: "npm:^1.0.2" - checksum: 10c0/f8a4534b5c69384d95ac18137d381f18a5cfae1f0fc1df0ef6feef51ef0d568606d970b69e02ea186c6c0f0eac77fe4e6ad96fec2569cc86c3afcc7475068c55 - languageName: node - linkType: hard - -"data-view-byte-offset@npm:^1.0.1": - version: 1.0.1 - resolution: "data-view-byte-offset@npm:1.0.1" - dependencies: - call-bound: "npm:^1.0.2" - es-errors: "npm:^1.3.0" - is-data-view: "npm:^1.0.1" - checksum: 10c0/fa7aa40078025b7810dcffc16df02c480573b7b53ef1205aa6a61533011005c1890e5ba17018c692ce7c900212b547262d33279fde801ad9843edc0863bf78c4 - languageName: node - linkType: hard - -"debounce-fn@npm:^6.0.0": - version: 6.0.0 - resolution: "debounce-fn@npm:6.0.0" - dependencies: - mimic-function: "npm:^5.0.0" - checksum: 10c0/ff5c48a7d644e292a653fd602db340b701bddba3973da7f64c6f25bbd9ab0fde9058567fdbe7efc72553561f3393f285413818d58f08614e73e0e85319f1da6e - languageName: node - linkType: hard - -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.4.3": - version: 4.4.3 - resolution: "debug@npm:4.4.3" - dependencies: - ms: "npm:^2.1.3" - peerDependenciesMeta: - supports-color: - optional: true - checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 - languageName: node - linkType: hard - -"debug@npm:^3.2.7": - version: 3.2.7 - resolution: "debug@npm:3.2.7" - dependencies: - ms: "npm:^2.1.1" - checksum: 10c0/37d96ae42cbc71c14844d2ae3ba55adf462ec89fd3a999459dec3833944cd999af6007ff29c780f1c61153bcaaf2c842d1e4ce1ec621e4fc4923244942e4a02a - languageName: node - linkType: hard - -"debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0": - version: 4.4.1 - resolution: "debug@npm:4.4.1" - dependencies: - ms: "npm:^2.1.3" - peerDependenciesMeta: - supports-color: - optional: true - checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55 - languageName: node - linkType: hard - -"deep-is@npm:^0.1.3": - version: 0.1.4 - resolution: "deep-is@npm:0.1.4" - checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c - languageName: node - linkType: hard - -"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": - version: 1.1.4 - resolution: "define-data-property@npm:1.1.4" - dependencies: - es-define-property: "npm:^1.0.0" - es-errors: "npm:^1.3.0" - gopd: "npm:^1.0.1" - checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 - languageName: node - linkType: hard - -"define-properties@npm:^1.1.3, define-properties@npm:^1.2.1": - version: 1.2.1 - resolution: "define-properties@npm:1.2.1" - dependencies: - define-data-property: "npm:^1.0.1" - has-property-descriptors: "npm:^1.0.0" - object-keys: "npm:^1.1.1" - checksum: 10c0/88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 - languageName: node - linkType: hard - -"deslop-js@npm:^0.0.12": - version: 0.0.12 - resolution: "deslop-js@npm:0.0.12" - dependencies: - "@oxc-project/types": "npm:^0.132.0" - fast-glob: "npm:^3.3.3" - minimatch: "npm:^10.2.5" - oxc-parser: "npm:^0.132.0" - oxc-resolver: "npm:^11.19.1" - typescript: "npm:^6.0.3" - checksum: 10c0/2012226bd8ab32678ba6af55f34b5d775b724f674b79d3704626ad636f5016f9a810e2dfe1886212b8f40021c59d79f4b42ae71616805e89d5335b6c0bf1611c - languageName: node - linkType: hard - -"detect-libc@npm:^2.0.1, detect-libc@npm:^2.1.2": - version: 2.1.2 - resolution: "detect-libc@npm:2.1.2" - checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 - languageName: node - linkType: hard - -"detect-node-es@npm:^1.1.0": - version: 1.1.0 - resolution: "detect-node-es@npm:1.1.0" - checksum: 10c0/e562f00de23f10c27d7119e1af0e7388407eb4b06596a25f6d79a360094a109ff285de317f02b090faae093d314cf6e73ac3214f8a5bb3a0def5bece94557fbe - languageName: node - linkType: hard - -"didyoumean@npm:^1.2.2": - version: 1.2.2 - resolution: "didyoumean@npm:1.2.2" - checksum: 10c0/95d0b53d23b851aacff56dfadb7ecfedce49da4232233baecfeecb7710248c4aa03f0aa8995062f0acafaf925adf8536bd7044a2e68316fd7d411477599bc27b - languageName: node - linkType: hard - -"dlv@npm:^1.1.3": - version: 1.1.3 - resolution: "dlv@npm:1.1.3" - checksum: 10c0/03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42 - languageName: node - linkType: hard - -"doctrine@npm:^2.1.0": - version: 2.1.0 - resolution: "doctrine@npm:2.1.0" - dependencies: - esutils: "npm:^2.0.2" - checksum: 10c0/b6416aaff1f380bf56c3b552f31fdf7a69b45689368deca72d28636f41c16bb28ec3ebc40ace97db4c1afc0ceeb8120e8492fe0046841c94c2933b2e30a7d5ac - languageName: node - linkType: hard - -"dot-prop@npm:^10.0.0": - version: 10.1.0 - resolution: "dot-prop@npm:10.1.0" - dependencies: - type-fest: "npm:^5.0.0" - checksum: 10c0/b034a06f017909ed55c6c164ddea962ccdce3d88b9b092f7106a9b738116a4cd003db5d47a0c6e140e93adbf1922b5e3a147e3d4a124c8556862940446ba5f75 - languageName: node - linkType: hard - -"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": - version: 1.0.1 - resolution: "dunder-proto@npm:1.0.1" - dependencies: - call-bind-apply-helpers: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - gopd: "npm:^1.2.0" - checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 - languageName: node - linkType: hard - -"effect@npm:4.0.0-beta.70": - version: 4.0.0-beta.70 - resolution: "effect@npm:4.0.0-beta.70" - dependencies: - "@standard-schema/spec": "npm:^1.1.0" - fast-check: "npm:^4.8.0" - find-my-way-ts: "npm:^0.1.6" - ini: "npm:^7.0.0" - kubernetes-types: "npm:^1.30.0" - msgpackr: "npm:^2.0.1" - multipasta: "npm:^0.2.7" - toml: "npm:^4.1.1" - uuid: "npm:^14.0.0" - yaml: "npm:^2.9.0" - checksum: 10c0/6ba1d7fe3b83ef991a7d1f2e2b98d311dd0e135c5f17c45b69f2a721e1ead5923a55b293a3619bdbc541dc29b91b486f52ea34aef5110f60e38e4d2c0daa0a02 - languageName: node - linkType: hard - -"electron-to-chromium@npm:^1.5.211": - version: 1.5.215 - resolution: "electron-to-chromium@npm:1.5.215" - checksum: 10c0/3a45976d1193e57284533096b3bbec218a5d4d85af4f7c133522aae35b14bbf22734f48ccc3f0e43a451441ebc375fa2f4350390fd729dcedb97543692133e39 - languageName: node - linkType: hard - -"electron-to-chromium@npm:^1.5.263": - version: 1.5.286 - resolution: "electron-to-chromium@npm:1.5.286" - checksum: 10c0/5384510f9682d7e46f98fa48b874c3901d9639de96e9e387afce1fe010fbac31376df0534524edc15f66e9902bfacee54037a5e598004e9c6a617884e379926d - languageName: node - linkType: hard - -"emoji-regex@npm:^9.2.2": - version: 9.2.2 - resolution: "emoji-regex@npm:9.2.2" - checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 - languageName: node - linkType: hard - -"env-paths@npm:^2.2.0": - version: 2.2.1 - resolution: "env-paths@npm:2.2.1" - checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 - languageName: node - linkType: hard - -"env-paths@npm:^3.0.0": - version: 3.0.0 - resolution: "env-paths@npm:3.0.0" - checksum: 10c0/76dec878cee47f841103bacd7fae03283af16f0702dad65102ef0a556f310b98a377885e0f32943831eb08b5ab37842a323d02529f3dfd5d0a40ca71b01b435f - languageName: node - linkType: hard - -"es-abstract@npm:^1.17.5, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.0": - version: 1.24.0 - resolution: "es-abstract@npm:1.24.0" - dependencies: - array-buffer-byte-length: "npm:^1.0.2" - arraybuffer.prototype.slice: "npm:^1.0.4" - available-typed-arrays: "npm:^1.0.7" - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.4" - data-view-buffer: "npm:^1.0.2" - data-view-byte-length: "npm:^1.0.2" - data-view-byte-offset: "npm:^1.0.1" - es-define-property: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.1.1" - es-set-tostringtag: "npm:^2.1.0" - es-to-primitive: "npm:^1.3.0" - function.prototype.name: "npm:^1.1.8" - get-intrinsic: "npm:^1.3.0" - get-proto: "npm:^1.0.1" - get-symbol-description: "npm:^1.1.0" - globalthis: "npm:^1.0.4" - gopd: "npm:^1.2.0" - has-property-descriptors: "npm:^1.0.2" - has-proto: "npm:^1.2.0" - has-symbols: "npm:^1.1.0" - hasown: "npm:^2.0.2" - internal-slot: "npm:^1.1.0" - is-array-buffer: "npm:^3.0.5" - is-callable: "npm:^1.2.7" - is-data-view: "npm:^1.0.2" - is-negative-zero: "npm:^2.0.3" - is-regex: "npm:^1.2.1" - is-set: "npm:^2.0.3" - is-shared-array-buffer: "npm:^1.0.4" - is-string: "npm:^1.1.1" - is-typed-array: "npm:^1.1.15" - is-weakref: "npm:^1.1.1" - math-intrinsics: "npm:^1.1.0" - object-inspect: "npm:^1.13.4" - object-keys: "npm:^1.1.1" - object.assign: "npm:^4.1.7" - own-keys: "npm:^1.0.1" - regexp.prototype.flags: "npm:^1.5.4" - safe-array-concat: "npm:^1.1.3" - safe-push-apply: "npm:^1.0.0" - safe-regex-test: "npm:^1.1.0" - set-proto: "npm:^1.0.0" - stop-iteration-iterator: "npm:^1.1.0" - string.prototype.trim: "npm:^1.2.10" - string.prototype.trimend: "npm:^1.0.9" - string.prototype.trimstart: "npm:^1.0.8" - typed-array-buffer: "npm:^1.0.3" - typed-array-byte-length: "npm:^1.0.3" - typed-array-byte-offset: "npm:^1.0.4" - typed-array-length: "npm:^1.0.7" - unbox-primitive: "npm:^1.1.0" - which-typed-array: "npm:^1.1.19" - checksum: 10c0/b256e897be32df5d382786ce8cce29a1dd8c97efbab77a26609bd70f2ed29fbcfc7a31758cb07488d532e7ccccdfca76c1118f2afe5a424cdc05ca007867c318 - languageName: node - linkType: hard - -"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": - version: 1.0.1 - resolution: "es-define-property@npm:1.0.1" - checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c - languageName: node - linkType: hard - -"es-errors@npm:^1.3.0": - version: 1.3.0 - resolution: "es-errors@npm:1.3.0" - checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 - languageName: node - linkType: hard - -"es-iterator-helpers@npm:^1.2.1": - version: 1.2.1 - resolution: "es-iterator-helpers@npm:1.2.1" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.6" - es-errors: "npm:^1.3.0" - es-set-tostringtag: "npm:^2.0.3" - function-bind: "npm:^1.1.2" - get-intrinsic: "npm:^1.2.6" - globalthis: "npm:^1.0.4" - gopd: "npm:^1.2.0" - has-property-descriptors: "npm:^1.0.2" - has-proto: "npm:^1.2.0" - has-symbols: "npm:^1.1.0" - internal-slot: "npm:^1.1.0" - iterator.prototype: "npm:^1.1.4" - safe-array-concat: "npm:^1.1.3" - checksum: 10c0/97e3125ca472d82d8aceea11b790397648b52c26d8768ea1c1ee6309ef45a8755bb63225a43f3150c7591cffc17caf5752459f1e70d583b4184370a8f04ebd2f - languageName: node - linkType: hard - -"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": - version: 1.1.1 - resolution: "es-object-atoms@npm:1.1.1" - dependencies: - es-errors: "npm:^1.3.0" - checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c - languageName: node - linkType: hard - -"es-set-tostringtag@npm:^2.0.3, es-set-tostringtag@npm:^2.1.0": - version: 2.1.0 - resolution: "es-set-tostringtag@npm:2.1.0" - dependencies: - es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.6" - has-tostringtag: "npm:^1.0.2" - hasown: "npm:^2.0.2" - checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af - languageName: node - linkType: hard - -"es-shim-unscopables@npm:^1.0.2, es-shim-unscopables@npm:^1.1.0": - version: 1.1.0 - resolution: "es-shim-unscopables@npm:1.1.0" - dependencies: - hasown: "npm:^2.0.2" - checksum: 10c0/1b9702c8a1823fc3ef39035a4e958802cf294dd21e917397c561d0b3e195f383b978359816b1732d02b255ccf63e1e4815da0065b95db8d7c992037be3bbbcdb - languageName: node - linkType: hard - -"es-to-primitive@npm:^1.3.0": - version: 1.3.0 - resolution: "es-to-primitive@npm:1.3.0" - dependencies: - is-callable: "npm:^1.2.7" - is-date-object: "npm:^1.0.5" - is-symbol: "npm:^1.0.4" - checksum: 10c0/c7e87467abb0b438639baa8139f701a06537d2b9bc758f23e8622c3b42fd0fdb5bde0f535686119e446dd9d5e4c0f238af4e14960f4771877cf818d023f6730b - languageName: node - linkType: hard - -"escalade@npm:^3.2.0": - version: 3.2.0 - resolution: "escalade@npm:3.2.0" - checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 - languageName: node - linkType: hard - -"escape-string-regexp@npm:^4.0.0": - version: 4.0.0 - resolution: "escape-string-regexp@npm:4.0.0" - checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 - languageName: node - linkType: hard - -"eslint-config-next@npm:16.2.3": - version: 16.2.3 - resolution: "eslint-config-next@npm:16.2.3" - dependencies: - "@next/eslint-plugin-next": "npm:16.2.3" - eslint-import-resolver-node: "npm:^0.3.6" - eslint-import-resolver-typescript: "npm:^3.5.2" - eslint-plugin-import: "npm:^2.32.0" - eslint-plugin-jsx-a11y: "npm:^6.10.0" - eslint-plugin-react: "npm:^7.37.0" - eslint-plugin-react-hooks: "npm:^7.0.0" - globals: "npm:16.4.0" - typescript-eslint: "npm:^8.46.0" - peerDependencies: - eslint: ">=9.0.0" - typescript: ">=3.3.1" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/c6fd3accadb53c636f034baf4363d22847bf824c8ca1ecfa8047a4eee7882d156e75f60f37098357c7ae07e646dfaa23a176336abd3c74aa9a2df61aee984653 - languageName: node - linkType: hard - -"eslint-import-resolver-node@npm:^0.3.6, eslint-import-resolver-node@npm:^0.3.9": - version: 0.3.9 - resolution: "eslint-import-resolver-node@npm:0.3.9" - dependencies: - debug: "npm:^3.2.7" - is-core-module: "npm:^2.13.0" - resolve: "npm:^1.22.4" - checksum: 10c0/0ea8a24a72328a51fd95aa8f660dcca74c1429806737cf10261ab90cfcaaf62fd1eff664b76a44270868e0a932711a81b250053942595bcd00a93b1c1575dd61 - languageName: node - linkType: hard - -"eslint-import-resolver-typescript@npm:^3.5.2": - version: 3.10.1 - resolution: "eslint-import-resolver-typescript@npm:3.10.1" - dependencies: - "@nolyfill/is-core-module": "npm:1.0.39" - debug: "npm:^4.4.0" - get-tsconfig: "npm:^4.10.0" - is-bun-module: "npm:^2.0.0" - stable-hash: "npm:^0.0.5" - tinyglobby: "npm:^0.2.13" - unrs-resolver: "npm:^1.6.2" - peerDependencies: - eslint: "*" - eslint-plugin-import: "*" - eslint-plugin-import-x: "*" - peerDependenciesMeta: - eslint-plugin-import: - optional: true - eslint-plugin-import-x: - optional: true - checksum: 10c0/02ba72cf757753ab9250806c066d09082e00807b7b6525d7687e1c0710bc3f6947e39120227fe1f93dabea3510776d86fb3fd769466ba3c46ce67e9f874cb702 - languageName: node - linkType: hard - -"eslint-module-utils@npm:^2.12.1": - version: 2.12.1 - resolution: "eslint-module-utils@npm:2.12.1" - dependencies: - debug: "npm:^3.2.7" - peerDependenciesMeta: - eslint: - optional: true - checksum: 10c0/6f4efbe7a91ae49bf67b4ab3644cb60bc5bd7db4cb5521de1b65be0847ffd3fb6bce0dd68f0995e1b312d137f768e2a1f842ee26fe73621afa05f850628fdc40 - languageName: node - linkType: hard - -"eslint-plugin-import@npm:^2.32.0": - version: 2.32.0 - resolution: "eslint-plugin-import@npm:2.32.0" - dependencies: - "@rtsao/scc": "npm:^1.1.0" - array-includes: "npm:^3.1.9" - array.prototype.findlastindex: "npm:^1.2.6" - array.prototype.flat: "npm:^1.3.3" - array.prototype.flatmap: "npm:^1.3.3" - debug: "npm:^3.2.7" - doctrine: "npm:^2.1.0" - eslint-import-resolver-node: "npm:^0.3.9" - eslint-module-utils: "npm:^2.12.1" - hasown: "npm:^2.0.2" - is-core-module: "npm:^2.16.1" - is-glob: "npm:^4.0.3" - minimatch: "npm:^3.1.2" - object.fromentries: "npm:^2.0.8" - object.groupby: "npm:^1.0.3" - object.values: "npm:^1.2.1" - semver: "npm:^6.3.1" - string.prototype.trimend: "npm:^1.0.9" - tsconfig-paths: "npm:^3.15.0" - peerDependencies: - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - checksum: 10c0/bfb1b8fc8800398e62ddfefbf3638d185286edfed26dfe00875cc2846d954491b4f5112457831588b757fa789384e1ae585f812614c4797f0499fa234fd4a48b - languageName: node - linkType: hard - -"eslint-plugin-jsx-a11y@npm:^6.10.0": - version: 6.10.2 - resolution: "eslint-plugin-jsx-a11y@npm:6.10.2" - dependencies: - aria-query: "npm:^5.3.2" - array-includes: "npm:^3.1.8" - array.prototype.flatmap: "npm:^1.3.2" - ast-types-flow: "npm:^0.0.8" - axe-core: "npm:^4.10.0" - axobject-query: "npm:^4.1.0" - damerau-levenshtein: "npm:^1.0.8" - emoji-regex: "npm:^9.2.2" - hasown: "npm:^2.0.2" - jsx-ast-utils: "npm:^3.3.5" - language-tags: "npm:^1.0.9" - minimatch: "npm:^3.1.2" - object.fromentries: "npm:^2.0.8" - safe-regex-test: "npm:^1.0.3" - string.prototype.includes: "npm:^2.0.1" - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - checksum: 10c0/d93354e03b0cf66f018d5c50964e074dffe4ddf1f9b535fa020d19c4ae45f89c1a16e9391ca61ac3b19f7042c751ac0d361a056a65cbd1de24718a53ff8daa6e - languageName: node - linkType: hard - -"eslint-plugin-react-hooks@npm:^7.0.0": - version: 7.0.1 - resolution: "eslint-plugin-react-hooks@npm:7.0.1" - dependencies: - "@babel/core": "npm:^7.24.4" - "@babel/parser": "npm:^7.24.4" - hermes-parser: "npm:^0.25.1" - zod: "npm:^3.25.0 || ^4.0.0" - zod-validation-error: "npm:^3.5.0 || ^4.0.0" - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - checksum: 10c0/1e711d1a9d1fa9cfc51fa1572500656577201199c70c795c6a27adfc1df39e5c598f69aab6aa91117753d23cc1f11388579a2bed14921cf9a4efe60ae8618496 - languageName: node - linkType: hard - -"eslint-plugin-react@npm:^7.37.0": - version: 7.37.5 - resolution: "eslint-plugin-react@npm:7.37.5" - dependencies: - array-includes: "npm:^3.1.8" - array.prototype.findlast: "npm:^1.2.5" - array.prototype.flatmap: "npm:^1.3.3" - array.prototype.tosorted: "npm:^1.1.4" - doctrine: "npm:^2.1.0" - es-iterator-helpers: "npm:^1.2.1" - estraverse: "npm:^5.3.0" - hasown: "npm:^2.0.2" - jsx-ast-utils: "npm:^2.4.1 || ^3.0.0" - minimatch: "npm:^3.1.2" - object.entries: "npm:^1.1.9" - object.fromentries: "npm:^2.0.8" - object.values: "npm:^1.2.1" - prop-types: "npm:^15.8.1" - resolve: "npm:^2.0.0-next.5" - semver: "npm:^6.3.1" - string.prototype.matchall: "npm:^4.0.12" - string.prototype.repeat: "npm:^1.0.0" - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - checksum: 10c0/c850bfd556291d4d9234f5ca38db1436924a1013627c8ab1853f77cac73ec19b020e861e6c7b783436a48b6ffcdfba4547598235a37ad4611b6739f65fd8ad57 - languageName: node - linkType: hard - -"eslint-scope@npm:^8.4.0": - version: 8.4.0 - resolution: "eslint-scope@npm:8.4.0" - dependencies: - esrecurse: "npm:^4.3.0" - estraverse: "npm:^5.2.0" - checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 - languageName: node - linkType: hard - -"eslint-scope@npm:^9.1.2": - version: 9.1.2 - resolution: "eslint-scope@npm:9.1.2" - dependencies: - "@types/esrecurse": "npm:^4.3.1" - "@types/estree": "npm:^1.0.8" - esrecurse: "npm:^4.3.0" - estraverse: "npm:^5.2.0" - checksum: 10c0/9fb8bca5a73e5741efb6cec84467027b6cb6f4203ff9b43a938e272c5cd30800bde46a5c20dfd1609f840225f0b62b7673be391b20acadf8658ca9fa4729b3dd - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.4.3": - version: 3.4.3 - resolution: "eslint-visitor-keys@npm:3.4.3" - checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^4.2.1": - version: 4.2.1 - resolution: "eslint-visitor-keys@npm:4.2.1" - checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1": - version: 5.0.1 - resolution: "eslint-visitor-keys@npm:5.0.1" - checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 - languageName: node - linkType: hard - -"eslint@npm:^9.39.2": - version: 9.39.2 - resolution: "eslint@npm:9.39.2" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.8.0" - "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.1" - "@eslint/config-helpers": "npm:^0.4.2" - "@eslint/core": "npm:^0.17.0" - "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.39.2" - "@eslint/plugin-kit": "npm:^0.4.1" - "@humanfs/node": "npm:^0.16.6" - "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.4.2" - "@types/estree": "npm:^1.0.6" - ajv: "npm:^6.12.4" - chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.6" - debug: "npm:^4.3.2" - escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.4.0" - eslint-visitor-keys: "npm:^4.2.1" - espree: "npm:^10.4.0" - esquery: "npm:^1.5.0" - esutils: "npm:^2.0.2" - fast-deep-equal: "npm:^3.1.3" - file-entry-cache: "npm:^8.0.0" - find-up: "npm:^5.0.0" - glob-parent: "npm:^6.0.2" - ignore: "npm:^5.2.0" - imurmurhash: "npm:^0.1.4" - is-glob: "npm:^4.0.0" - json-stable-stringify-without-jsonify: "npm:^1.0.1" - lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" - natural-compare: "npm:^1.4.0" - optionator: "npm:^0.9.3" - peerDependencies: - jiti: "*" - peerDependenciesMeta: - jiti: - optional: true - bin: - eslint: bin/eslint.js - checksum: 10c0/bb88ca8fd16bb7e1ac3e13804c54d41c583214460c0faa7b3e7c574e69c5600c7122295500fb4b0c06067831111db740931e98da1340329527658e1cf80073d3 - languageName: node - linkType: hard - -"espree@npm:^10.0.1, espree@npm:^10.4.0": - version: 10.4.0 - resolution: "espree@npm:10.4.0" - dependencies: - acorn: "npm:^8.15.0" - acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^4.2.1" - checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b - languageName: node - linkType: hard - -"esquery@npm:^1.5.0": - version: 1.6.0 - resolution: "esquery@npm:1.6.0" - dependencies: - estraverse: "npm:^5.1.0" - checksum: 10c0/cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2 - languageName: node - linkType: hard - -"esrecurse@npm:^4.3.0": - version: 4.3.0 - resolution: "esrecurse@npm:4.3.0" - dependencies: - estraverse: "npm:^5.2.0" - checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5 - languageName: node - linkType: hard - -"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0, estraverse@npm:^5.3.0": - version: 5.3.0 - resolution: "estraverse@npm:5.3.0" - checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107 - languageName: node - linkType: hard - -"esutils@npm:^2.0.2": - version: 2.0.3 - resolution: "esutils@npm:2.0.3" - checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 - languageName: node - linkType: hard - -"ethers@npm:6.13.4": - version: 6.13.4 - resolution: "ethers@npm:6.13.4" - dependencies: - "@adraffy/ens-normalize": "npm:1.10.1" - "@noble/curves": "npm:1.2.0" - "@noble/hashes": "npm:1.3.2" - "@types/node": "npm:22.7.5" - aes-js: "npm:4.0.0-beta.5" - tslib: "npm:2.7.0" - ws: "npm:8.17.1" - checksum: 10c0/efcf9f39f841e38af68ec23cdbd745432c239c256aac4929842d1af04e55d7be0ff65e462f1cf3e93586f43f7bdcc0098fd56f2f7234f36d73e466521a5766ce - languageName: node - linkType: hard - -"exponential-backoff@npm:^3.1.1": - version: 3.1.3 - resolution: "exponential-backoff@npm:3.1.3" - checksum: 10c0/77e3ae682b7b1f4972f563c6dbcd2b0d54ac679e62d5d32f3e5085feba20483cf28bd505543f520e287a56d4d55a28d7874299941faf637e779a1aa5994d1267 - languageName: node - linkType: hard - -"fast-check@npm:^4.8.0": - version: 4.8.0 - resolution: "fast-check@npm:4.8.0" - dependencies: - pure-rand: "npm:^8.0.0" - checksum: 10c0/f72556a29db4ff386a8b6e50d420b06c7e5eaafff7db5560a99136c57d8d4777998155eb02d1bbeff396f575cc0b1442c8a1c4ddb798c4a919b542de1a1904ff - languageName: node - linkType: hard - -"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": - version: 3.1.3 - resolution: "fast-deep-equal@npm:3.1.3" - checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 - languageName: node - linkType: hard - -"fast-glob@npm:3.3.1": - version: 3.3.1 - resolution: "fast-glob@npm:3.3.1" - dependencies: - "@nodelib/fs.stat": "npm:^2.0.2" - "@nodelib/fs.walk": "npm:^1.2.3" - glob-parent: "npm:^5.1.2" - merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10c0/b68431128fb6ce4b804c5f9622628426d990b66c75b21c0d16e3d80e2d1398bf33f7e1724e66a2e3f299285dcf5b8d745b122d0304e7dd66f5231081f33ec67c - languageName: node - linkType: hard - -"fast-glob@npm:^3.3.2, fast-glob@npm:^3.3.3": - version: 3.3.3 - resolution: "fast-glob@npm:3.3.3" - dependencies: - "@nodelib/fs.stat": "npm:^2.0.2" - "@nodelib/fs.walk": "npm:^1.2.3" - glob-parent: "npm:^5.1.2" - merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.8" - checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe - languageName: node - linkType: hard - -"fast-json-stable-stringify@npm:^2.0.0": - version: 2.1.0 - resolution: "fast-json-stable-stringify@npm:2.1.0" - checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b - languageName: node - linkType: hard - -"fast-levenshtein@npm:^2.0.6": - version: 2.0.6 - resolution: "fast-levenshtein@npm:2.0.6" - checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 - languageName: node - linkType: hard - -"fast-uri@npm:^3.0.1": - version: 3.1.2 - resolution: "fast-uri@npm:3.1.2" - checksum: 10c0/5b35641895959f3f7ab7a7b1b5542bded159346f25ec9f256817b206d50b64eda5828e90d605a2e2fc645c90519a7259c2bab2c942ee728c88b88e5be21b090d - languageName: node - linkType: hard - -"fastq@npm:^1.6.0": - version: 1.19.1 - resolution: "fastq@npm:1.19.1" - dependencies: - reusify: "npm:^1.0.4" - checksum: 10c0/ebc6e50ac7048daaeb8e64522a1ea7a26e92b3cee5cd1c7f2316cdca81ba543aa40a136b53891446ea5c3a67ec215fbaca87ad405f102dd97012f62916905630 - languageName: node - linkType: hard - -"fdir@npm:^6.4.4": - version: 6.4.6 - resolution: "fdir@npm:6.4.6" - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9 - languageName: node - linkType: hard - -"fdir@npm:^6.5.0": - version: 6.5.0 - resolution: "fdir@npm:6.5.0" - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f - languageName: node - linkType: hard - -"file-entry-cache@npm:^8.0.0": - version: 8.0.0 - resolution: "file-entry-cache@npm:8.0.0" - dependencies: - flat-cache: "npm:^4.0.0" - checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 - languageName: node - linkType: hard - -"fill-range@npm:^7.1.1": - version: 7.1.1 - resolution: "fill-range@npm:7.1.1" - dependencies: - to-regex-range: "npm:^5.0.1" - checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 - languageName: node - linkType: hard - -"find-my-way-ts@npm:^0.1.6": - version: 0.1.6 - resolution: "find-my-way-ts@npm:0.1.6" - checksum: 10c0/16ad4b15275b56ee0ec361d0c61afbdff4c75bd0ac04112f6910f188cb1058096ba63529c2363914da6bb60266aa4def1025af04af26368ff87eb0df52f2862f - languageName: node - linkType: hard - -"find-up@npm:^5.0.0": - version: 5.0.0 - resolution: "find-up@npm:5.0.0" - dependencies: - locate-path: "npm:^6.0.0" - path-exists: "npm:^4.0.0" - checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a - languageName: node - linkType: hard - -"flat-cache@npm:^4.0.0": - version: 4.0.1 - resolution: "flat-cache@npm:4.0.1" - dependencies: - flatted: "npm:^3.2.9" - keyv: "npm:^4.5.4" - checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc - languageName: node - linkType: hard - -"flatted@npm:3.4.2": - version: 3.4.2 - resolution: "flatted@npm:3.4.2" - checksum: 10c0/a65b67aae7172d6cdf63691be7de6c5cd5adbdfdfe2e9da1a09b617c9512ed794037741ee53d93114276bff3f93cd3b0d97d54f9b316e1e4885dde6e9ffdf7ed - languageName: node - linkType: hard - -"for-each@npm:^0.3.3, for-each@npm:^0.3.5": - version: 0.3.5 - resolution: "for-each@npm:0.3.5" - dependencies: - is-callable: "npm:^1.2.7" - checksum: 10c0/0e0b50f6a843a282637d43674d1fb278dda1dd85f4f99b640024cfb10b85058aac0cc781bf689d5fe50b4b7f638e91e548560723a4e76e04fe96ae35ef039cee - languageName: node - linkType: hard - -"fraction.js@npm:^4.3.7": - version: 4.3.7 - resolution: "fraction.js@npm:4.3.7" - checksum: 10c0/df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 - languageName: node - linkType: hard - -"fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 - languageName: node - linkType: hard - -"fsevents@npm:~2.3.2": - version: 2.3.3 - resolution: "fsevents@npm:2.3.3" - dependencies: - node-gyp: "npm:latest" - checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 - conditions: os=darwin - languageName: node - linkType: hard - -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": - version: 2.3.3 - resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" - dependencies: - node-gyp: "npm:latest" - conditions: os=darwin - languageName: node - linkType: hard - -"function-bind@npm:^1.1.2": - version: 1.1.2 - resolution: "function-bind@npm:1.1.2" - checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 - languageName: node - linkType: hard - -"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": - version: 1.1.8 - resolution: "function.prototype.name@npm:1.1.8" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" - functions-have-names: "npm:^1.2.3" - hasown: "npm:^2.0.2" - is-callable: "npm:^1.2.7" - checksum: 10c0/e920a2ab52663005f3cbe7ee3373e3c71c1fb5558b0b0548648cdf3e51961085032458e26c71ff1a8c8c20e7ee7caeb03d43a5d1fa8610c459333323a2e71253 - languageName: node - linkType: hard - -"functions-have-names@npm:^1.2.3": - version: 1.2.3 - resolution: "functions-have-names@npm:1.2.3" - checksum: 10c0/33e77fd29bddc2d9bb78ab3eb854c165909201f88c75faa8272e35899e2d35a8a642a15e7420ef945e1f64a9670d6aa3ec744106b2aa42be68ca5114025954ca - languageName: node - linkType: hard - -"gensync@npm:^1.0.0-beta.2": - version: 1.0.0-beta.2 - resolution: "gensync@npm:1.0.0-beta.2" - checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 - languageName: node - linkType: hard - -"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": - version: 1.3.0 - resolution: "get-intrinsic@npm:1.3.0" - dependencies: - call-bind-apply-helpers: "npm:^1.0.2" - es-define-property: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.1.1" - function-bind: "npm:^1.1.2" - get-proto: "npm:^1.0.1" - gopd: "npm:^1.2.0" - has-symbols: "npm:^1.1.0" - hasown: "npm:^2.0.2" - math-intrinsics: "npm:^1.1.0" - checksum: 10c0/52c81808af9a8130f581e6a6a83e1ba4a9f703359e7a438d1369a5267a25412322f03dcbd7c549edaef0b6214a0630a28511d7df0130c93cfd380f4fa0b5b66a - languageName: node - linkType: hard - -"get-nonce@npm:^1.0.0": - version: 1.0.1 - resolution: "get-nonce@npm:1.0.1" - checksum: 10c0/2d7df55279060bf0568549e1ffc9b84bc32a32b7541675ca092dce56317cdd1a59a98dcc4072c9f6a980779440139a3221d7486f52c488e69dc0fd27b1efb162 - languageName: node - linkType: hard - -"get-proto@npm:^1.0.0, get-proto@npm:^1.0.1": - version: 1.0.1 - resolution: "get-proto@npm:1.0.1" - dependencies: - dunder-proto: "npm:^1.0.1" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c - languageName: node - linkType: hard - -"get-symbol-description@npm:^1.1.0": - version: 1.1.0 - resolution: "get-symbol-description@npm:1.1.0" - dependencies: - call-bound: "npm:^1.0.3" - es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.6" - checksum: 10c0/d6a7d6afca375779a4b307738c9e80dbf7afc0bdbe5948768d54ab9653c865523d8920e670991a925936eb524b7cb6a6361d199a760b21d0ca7620194455aa4b - languageName: node - linkType: hard - -"get-tsconfig@npm:^4.10.0": - version: 4.10.1 - resolution: "get-tsconfig@npm:4.10.1" - dependencies: - resolve-pkg-maps: "npm:^1.0.0" - checksum: 10c0/7f8e3dabc6a49b747920a800fb88e1952fef871cdf51b79e98db48275a5de6cdaf499c55ee67df5fa6fe7ce65f0063e26de0f2e53049b408c585aa74d39ffa21 - languageName: node - linkType: hard - -"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": - version: 5.1.2 - resolution: "glob-parent@npm:5.1.2" - dependencies: - is-glob: "npm:^4.0.1" - checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee - languageName: node - linkType: hard - -"glob-parent@npm:^6.0.2": - version: 6.0.2 - resolution: "glob-parent@npm:6.0.2" - dependencies: - is-glob: "npm:^4.0.3" - checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 - languageName: node - linkType: hard - -"glob@npm:^13.0.0": - version: 13.0.6 - resolution: "glob@npm:13.0.6" - dependencies: - minimatch: "npm:^10.2.2" - minipass: "npm:^7.1.3" - path-scurry: "npm:^2.0.2" - checksum: 10c0/269c236f11a9b50357fe7a8c6aadac667e01deb5242b19c84975628f05f4438d8ee1354bb62c5d6c10f37fd59911b54d7799730633a2786660d8c69f1d18120a - languageName: node - linkType: hard - -"globals@npm:16.4.0": - version: 16.4.0 - resolution: "globals@npm:16.4.0" - checksum: 10c0/a14b447a78b664b42f6d324e8675fcae6fe5e57924fecc1f6328dce08af9b2ca3a3138501e1b1f244a49814a732dc60cfc1aa24e714e0b64ac8bd18910bfac90 - languageName: node - linkType: hard - -"globals@npm:^14.0.0": - version: 14.0.0 - resolution: "globals@npm:14.0.0" - checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d - languageName: node - linkType: hard - -"globalthis@npm:^1.0.4": - version: 1.0.4 - resolution: "globalthis@npm:1.0.4" - dependencies: - define-properties: "npm:^1.2.1" - gopd: "npm:^1.0.1" - checksum: 10c0/9d156f313af79d80b1566b93e19285f481c591ad6d0d319b4be5e03750d004dde40a39a0f26f7e635f9007a3600802f53ecd85a759b86f109e80a5f705e01846 - languageName: node - linkType: hard - -"gopd@npm:^1.0.1, gopd@npm:^1.2.0": - version: 1.2.0 - resolution: "gopd@npm:1.2.0" - checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": - version: 4.2.11 - resolution: "graceful-fs@npm:4.2.11" - checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 - languageName: node - linkType: hard - -"has-bigints@npm:^1.0.2": - version: 1.1.0 - resolution: "has-bigints@npm:1.1.0" - checksum: 10c0/2de0cdc4a1ccf7a1e75ffede1876994525ac03cc6f5ae7392d3415dd475cd9eee5bceec63669ab61aa997ff6cceebb50ef75561c7002bed8988de2b9d1b40788 - languageName: node - linkType: hard - -"has-flag@npm:^4.0.0": - version: 4.0.0 - resolution: "has-flag@npm:4.0.0" - checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 - languageName: node - linkType: hard - -"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": - version: 1.0.2 - resolution: "has-property-descriptors@npm:1.0.2" - dependencies: - es-define-property: "npm:^1.0.0" - checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 - languageName: node - linkType: hard - -"has-proto@npm:^1.2.0": - version: 1.2.0 - resolution: "has-proto@npm:1.2.0" - dependencies: - dunder-proto: "npm:^1.0.0" - checksum: 10c0/46538dddab297ec2f43923c3d35237df45d8c55a6fc1067031e04c13ed8a9a8f94954460632fd4da84c31a1721eefee16d901cbb1ae9602bab93bb6e08f93b95 - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": - version: 1.1.0 - resolution: "has-symbols@npm:1.1.0" - checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e - languageName: node - linkType: hard - -"has-tostringtag@npm:^1.0.2": - version: 1.0.2 - resolution: "has-tostringtag@npm:1.0.2" - dependencies: - has-symbols: "npm:^1.0.3" - checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c - languageName: node - linkType: hard - -"hasown@npm:^2.0.2": - version: 2.0.2 - resolution: "hasown@npm:2.0.2" - dependencies: - function-bind: "npm:^1.1.2" - checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 - languageName: node - linkType: hard - -"hermes-estree@npm:0.25.1": - version: 0.25.1 - resolution: "hermes-estree@npm:0.25.1" - checksum: 10c0/48be3b2fa37a0cbc77a112a89096fa212f25d06de92781b163d67853d210a8a5c3784fac23d7d48335058f7ed283115c87b4332c2a2abaaccc76d0ead1a282ac - languageName: node - linkType: hard - -"hermes-parser@npm:^0.25.1": - version: 0.25.1 - resolution: "hermes-parser@npm:0.25.1" - dependencies: - hermes-estree: "npm:0.25.1" - checksum: 10c0/3abaa4c6f1bcc25273f267297a89a4904963ea29af19b8e4f6eabe04f1c2c7e9abd7bfc4730ddb1d58f2ea04b6fee74053d8bddb5656ec6ebf6c79cc8d14202c - languageName: node - linkType: hard - -"http-cache-semantics@npm:^4.1.1": - version: 4.2.0 - resolution: "http-cache-semantics@npm:4.2.0" - checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37 - languageName: node - linkType: hard - -"http-proxy-agent@npm:^7.0.0": - version: 7.0.2 - resolution: "http-proxy-agent@npm:7.0.2" - dependencies: - agent-base: "npm:^7.1.0" - debug: "npm:^4.3.4" - checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.1": - version: 7.0.6 - resolution: "https-proxy-agent@npm:7.0.6" - dependencies: - agent-base: "npm:^7.1.2" - debug: "npm:4" - checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac - languageName: node - linkType: hard - -"iconv-lite@npm:^0.7.2": - version: 0.7.2 - resolution: "iconv-lite@npm:0.7.2" - dependencies: - safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10c0/3c228920f3bd307f56bf8363706a776f4a060eb042f131cd23855ceca962951b264d0997ab38a1ad340e1c5df8499ed26e1f4f0db6b2a2ad9befaff22f14b722 - languageName: node - linkType: hard - -"ignore@npm:^5.2.0": - version: 5.3.2 - resolution: "ignore@npm:5.3.2" - checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 - languageName: node - linkType: hard - -"ignore@npm:^7.0.5": - version: 7.0.5 - resolution: "ignore@npm:7.0.5" - checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d - languageName: node - linkType: hard - -"import-fresh@npm:^3.2.1": - version: 3.3.1 - resolution: "import-fresh@npm:3.3.1" - dependencies: - parent-module: "npm:^1.0.0" - resolve-from: "npm:^4.0.0" - checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 - languageName: node - linkType: hard - -"ini@npm:^7.0.0": - version: 7.0.0 - resolution: "ini@npm:7.0.0" - checksum: 10c0/7520cae38bd5587e1cbca4637bb5fc0e157f38e0816522756456010ef703772d0018f9f4987cb9977bf3b10c1feccaad7800744dd1f5d85fb435e58a1baa9754 - languageName: node - linkType: hard - -"input-format@npm:^0.3.10": - version: 0.3.14 - resolution: "input-format@npm:0.3.14" - dependencies: - prop-types: "npm:^15.8.1" - peerDependencies: - react: ">=18.1.0" - react-dom: ">=18.1.0" - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - checksum: 10c0/37bb23e0dd15223b2ac8a25b09bc26e4d613cbd93c040dcdb490a1661dd0782c2742ad819b9dbd4b154a9e09ce1a69046a78e4ee943171b7ec86b07b50324ad6 - languageName: node - linkType: hard - -"input-otp@npm:^1.4.2": - version: 1.4.2 - resolution: "input-otp@npm:1.4.2" - 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 - checksum: 10c0/d3a3216a75ed832993f3f2852edd7a85c5bae30ea6d251182119120488bbf9fed7cfdd91819bcee6daff57b3cfcbca94fd16d6a7c92cee4d806c0d4fa6ff1128 - languageName: node - linkType: hard - -"internal-slot@npm:^1.1.0": - version: 1.1.0 - resolution: "internal-slot@npm:1.1.0" - dependencies: - es-errors: "npm:^1.3.0" - hasown: "npm:^2.0.2" - side-channel: "npm:^1.1.0" - checksum: 10c0/03966f5e259b009a9bf1a78d60da920df198af4318ec004f57b8aef1dd3fe377fbc8cce63a96e8c810010302654de89f9e19de1cd8ad0061d15be28a695465c7 - languageName: node - linkType: hard - -"ip-address@npm:^10.0.1": - version: 10.1.0 - resolution: "ip-address@npm:10.1.0" - checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 - languageName: node - linkType: hard - -"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": - version: 3.0.5 - resolution: "is-array-buffer@npm:3.0.5" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - get-intrinsic: "npm:^1.2.6" - checksum: 10c0/c5c9f25606e86dbb12e756694afbbff64bc8b348d1bc989324c037e1068695131930199d6ad381952715dad3a9569333817f0b1a72ce5af7f883ce802e49c83d - languageName: node - linkType: hard - -"is-async-function@npm:^2.0.0": - version: 2.1.1 - resolution: "is-async-function@npm:2.1.1" - dependencies: - async-function: "npm:^1.0.0" - call-bound: "npm:^1.0.3" - get-proto: "npm:^1.0.1" - has-tostringtag: "npm:^1.0.2" - safe-regex-test: "npm:^1.1.0" - checksum: 10c0/d70c236a5e82de6fc4d44368ffd0c2fee2b088b893511ce21e679da275a5ecc6015ff59a7d7e1bdd7ca39f71a8dbdd253cf8cce5c6b3c91cdd5b42b5ce677298 - languageName: node - linkType: hard - -"is-bigint@npm:^1.1.0": - version: 1.1.0 - resolution: "is-bigint@npm:1.1.0" - dependencies: - has-bigints: "npm:^1.0.2" - checksum: 10c0/f4f4b905ceb195be90a6ea7f34323bf1c18e3793f18922e3e9a73c684c29eeeeff5175605c3a3a74cc38185fe27758f07efba3dbae812e5c5afbc0d2316b40e4 - languageName: node - linkType: hard - -"is-binary-path@npm:~2.1.0": - version: 2.1.0 - resolution: "is-binary-path@npm:2.1.0" - dependencies: - binary-extensions: "npm:^2.0.0" - checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 - languageName: node - linkType: hard - -"is-boolean-object@npm:^1.2.1": - version: 1.2.2 - resolution: "is-boolean-object@npm:1.2.2" - dependencies: - call-bound: "npm:^1.0.3" - has-tostringtag: "npm:^1.0.2" - checksum: 10c0/36ff6baf6bd18b3130186990026f5a95c709345c39cd368468e6c1b6ab52201e9fd26d8e1f4c066357b4938b0f0401e1a5000e08257787c1a02f3a719457001e - languageName: node - linkType: hard - -"is-bun-module@npm:^2.0.0": - version: 2.0.0 - resolution: "is-bun-module@npm:2.0.0" - dependencies: - semver: "npm:^7.7.1" - checksum: 10c0/7d27a0679cfa5be1f5052650391f9b11040cd70c48d45112e312c56bc6b6ca9c9aea70dcce6cc40b1e8947bfff8567a5c5715d3b066fb478522dab46ea379240 - languageName: node - linkType: hard - -"is-callable@npm:^1.2.7": - version: 1.2.7 - resolution: "is-callable@npm:1.2.7" - checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f - languageName: node - linkType: hard - -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.16.0, is-core-module@npm:^2.16.1": - version: 2.16.1 - resolution: "is-core-module@npm:2.16.1" - dependencies: - hasown: "npm:^2.0.2" - checksum: 10c0/898443c14780a577e807618aaae2b6f745c8538eca5c7bc11388a3f2dc6de82b9902bcc7eb74f07be672b11bbe82dd6a6edded44a00cb3d8f933d0459905eedd - languageName: node - linkType: hard - -"is-data-view@npm:^1.0.1, is-data-view@npm:^1.0.2": - version: 1.0.2 - resolution: "is-data-view@npm:1.0.2" - dependencies: - call-bound: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.6" - is-typed-array: "npm:^1.1.13" - checksum: 10c0/ef3548a99d7e7f1370ce21006baca6d40c73e9f15c941f89f0049c79714c873d03b02dae1c64b3f861f55163ecc16da06506c5b8a1d4f16650b3d9351c380153 - languageName: node - linkType: hard - -"is-date-object@npm:^1.0.5, is-date-object@npm:^1.1.0": - version: 1.1.0 - resolution: "is-date-object@npm:1.1.0" - dependencies: - call-bound: "npm:^1.0.2" - has-tostringtag: "npm:^1.0.2" - checksum: 10c0/1a4d199c8e9e9cac5128d32e6626fa7805175af9df015620ac0d5d45854ccf348ba494679d872d37301032e35a54fc7978fba1687e8721b2139aea7870cafa2f - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 - languageName: node - linkType: hard - -"is-finalizationregistry@npm:^1.1.0": - version: 1.1.1 - resolution: "is-finalizationregistry@npm:1.1.1" - dependencies: - call-bound: "npm:^1.0.3" - checksum: 10c0/818dff679b64f19e228a8205a1e2d09989a98e98def3a817f889208cfcbf918d321b251aadf2c05918194803ebd2eb01b14fc9d0b2bea53d984f4137bfca5e97 - languageName: node - linkType: hard - -"is-generator-function@npm:^1.0.10": - version: 1.1.0 - resolution: "is-generator-function@npm:1.1.0" - dependencies: - call-bound: "npm:^1.0.3" - get-proto: "npm:^1.0.0" - has-tostringtag: "npm:^1.0.2" - safe-regex-test: "npm:^1.1.0" - checksum: 10c0/fdfa96c8087bf36fc4cd514b474ba2ff404219a4dd4cfa6cf5426404a1eed259bdcdb98f082a71029a48d01f27733e3436ecc6690129a7ec09cb0434bee03a2a - languageName: node - linkType: hard - -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" - dependencies: - is-extglob: "npm:^2.1.1" - checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a - languageName: node - linkType: hard - -"is-map@npm:^2.0.3": - version: 2.0.3 - resolution: "is-map@npm:2.0.3" - checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc - languageName: node - linkType: hard - -"is-negative-zero@npm:^2.0.3": - version: 2.0.3 - resolution: "is-negative-zero@npm:2.0.3" - checksum: 10c0/bcdcf6b8b9714063ffcfa9929c575ac69bfdabb8f4574ff557dfc086df2836cf07e3906f5bbc4f2a5c12f8f3ba56af640c843cdfc74da8caed86c7c7d66fd08e - languageName: node - linkType: hard - -"is-number-object@npm:^1.1.1": - version: 1.1.1 - resolution: "is-number-object@npm:1.1.1" - dependencies: - call-bound: "npm:^1.0.3" - has-tostringtag: "npm:^1.0.2" - checksum: 10c0/97b451b41f25135ff021d85c436ff0100d84a039bb87ffd799cbcdbea81ef30c464ced38258cdd34f080be08fc3b076ca1f472086286d2aa43521d6ec6a79f53 - languageName: node - linkType: hard - -"is-number@npm:^7.0.0": - version: 7.0.0 - resolution: "is-number@npm:7.0.0" - checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 - languageName: node - linkType: hard - -"is-regex@npm:^1.2.1": - version: 1.2.1 - resolution: "is-regex@npm:1.2.1" - dependencies: - call-bound: "npm:^1.0.2" - gopd: "npm:^1.2.0" - has-tostringtag: "npm:^1.0.2" - hasown: "npm:^2.0.2" - checksum: 10c0/1d3715d2b7889932349241680032e85d0b492cfcb045acb75ffc2c3085e8d561184f1f7e84b6f8321935b4aea39bc9c6ba74ed595b57ce4881a51dfdbc214e04 - languageName: node - linkType: hard - -"is-set@npm:^2.0.3": - version: 2.0.3 - resolution: "is-set@npm:2.0.3" - checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 - languageName: node - linkType: hard - -"is-shared-array-buffer@npm:^1.0.4": - version: 1.0.4 - resolution: "is-shared-array-buffer@npm:1.0.4" - dependencies: - call-bound: "npm:^1.0.3" - checksum: 10c0/65158c2feb41ff1edd6bbd6fd8403a69861cf273ff36077982b5d4d68e1d59278c71691216a4a64632bd76d4792d4d1d2553901b6666d84ade13bba5ea7bc7db - languageName: node - linkType: hard - -"is-string@npm:^1.1.1": - version: 1.1.1 - resolution: "is-string@npm:1.1.1" - dependencies: - call-bound: "npm:^1.0.3" - has-tostringtag: "npm:^1.0.2" - checksum: 10c0/2f518b4e47886bb81567faba6ffd0d8a8333cf84336e2e78bf160693972e32ad00fe84b0926491cc598dee576fdc55642c92e62d0cbe96bf36f643b6f956f94d - languageName: node - linkType: hard - -"is-symbol@npm:^1.0.4, is-symbol@npm:^1.1.1": - version: 1.1.1 - resolution: "is-symbol@npm:1.1.1" - dependencies: - call-bound: "npm:^1.0.2" - has-symbols: "npm:^1.1.0" - safe-regex-test: "npm:^1.1.0" - checksum: 10c0/f08f3e255c12442e833f75a9e2b84b2d4882fdfd920513cf2a4a2324f0a5b076c8fd913778e3ea5d258d5183e9d92c0cd20e04b03ab3df05316b049b2670af1e - languageName: node - linkType: hard - -"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15": - version: 1.1.15 - resolution: "is-typed-array@npm:1.1.15" - dependencies: - which-typed-array: "npm:^1.1.16" - checksum: 10c0/415511da3669e36e002820584e264997ffe277ff136643a3126cc949197e6ca3334d0f12d084e83b1994af2e9c8141275c741cf2b7da5a2ff62dd0cac26f76c4 - languageName: node - linkType: hard - -"is-weakmap@npm:^2.0.2": - version: 2.0.2 - resolution: "is-weakmap@npm:2.0.2" - checksum: 10c0/443c35bb86d5e6cc5929cd9c75a4024bb0fff9586ed50b092f94e700b89c43a33b186b76dbc6d54f3d3d09ece689ab38dcdc1af6a482cbe79c0f2da0a17f1299 - languageName: node - linkType: hard - -"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.1": - version: 1.1.1 - resolution: "is-weakref@npm:1.1.1" - dependencies: - call-bound: "npm:^1.0.3" - checksum: 10c0/8e0a9c07b0c780949a100e2cab2b5560a48ecd4c61726923c1a9b77b6ab0aa0046c9e7fb2206042296817045376dee2c8ab1dabe08c7c3dfbf195b01275a085b - languageName: node - linkType: hard - -"is-weakset@npm:^2.0.3": - version: 2.0.4 - resolution: "is-weakset@npm:2.0.4" - dependencies: - call-bound: "npm:^1.0.3" - get-intrinsic: "npm:^1.2.6" - checksum: 10c0/6491eba08acb8dc9532da23cb226b7d0192ede0b88f16199e592e4769db0a077119c1f5d2283d1e0d16d739115f70046e887e477eb0e66cd90e1bb29f28ba647 - languageName: node - linkType: hard - -"isarray@npm:^2.0.5": - version: 2.0.5 - resolution: "isarray@npm:2.0.5" - checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d - languageName: node - linkType: hard - -"isexe@npm:^4.0.0": - version: 4.0.0 - resolution: "isexe@npm:4.0.0" - checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce - languageName: node - linkType: hard - -"iterator.prototype@npm:^1.1.4": - version: 1.1.5 - resolution: "iterator.prototype@npm:1.1.5" - dependencies: - define-data-property: "npm:^1.1.4" - es-object-atoms: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.6" - get-proto: "npm:^1.0.0" - has-symbols: "npm:^1.1.0" - set-function-name: "npm:^2.0.2" - checksum: 10c0/f7a262808e1b41049ab55f1e9c29af7ec1025a000d243b83edf34ce2416eedd56079b117fa59376bb4a724110690f13aa8427f2ee29a09eec63a7e72367626d0 - languageName: node - linkType: hard - -"jiti@npm:^1.21.7": - version: 1.21.7 - resolution: "jiti@npm:1.21.7" - bin: - jiti: bin/jiti.js - checksum: 10c0/77b61989c758ff32407cdae8ddc77f85e18e1a13fc4977110dbd2e05fc761842f5f71bce684d9a01316e1c4263971315a111385759951080bbfe17cbb5de8f7a - languageName: node - linkType: hard - -"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": - version: 4.0.0 - resolution: "js-tokens@npm:4.0.0" - checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed - languageName: node - linkType: hard - -"js-yaml@npm:>=4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" - dependencies: - argparse: "npm:^2.0.1" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 - languageName: node - linkType: hard - -"jsesc@npm:^3.0.2": - version: 3.1.0 - resolution: "jsesc@npm:3.1.0" - bin: - jsesc: bin/jsesc - checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 - languageName: node - linkType: hard - -"json-buffer@npm:3.0.1": - version: 3.0.1 - resolution: "json-buffer@npm:3.0.1" - checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7 - languageName: node - linkType: hard - -"json-schema-traverse@npm:^0.4.1": - version: 0.4.1 - resolution: "json-schema-traverse@npm:0.4.1" - checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce - languageName: node - linkType: hard - -"json-schema-traverse@npm:^1.0.0": - version: 1.0.0 - resolution: "json-schema-traverse@npm:1.0.0" - checksum: 10c0/71e30015d7f3d6dc1c316d6298047c8ef98a06d31ad064919976583eb61e1018a60a0067338f0f79cabc00d84af3fcc489bd48ce8a46ea165d9541ba17fb30c6 - languageName: node - linkType: hard - -"json-schema-typed@npm:^8.0.1": - version: 8.0.2 - resolution: "json-schema-typed@npm:8.0.2" - checksum: 10c0/89f5e2fb1495483b705c027203c07277ee6bf2665165ad25a9cb55de5af7f72570326d13d32565180781e4083ad5c9688102f222baed7b353c2f39c1e02b0428 - languageName: node - linkType: hard - -"json-stable-stringify-without-jsonify@npm:^1.0.1": - version: 1.0.1 - resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" - checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5 - languageName: node - linkType: hard - -"json5@npm:^1.0.2": - version: 1.0.2 - resolution: "json5@npm:1.0.2" - dependencies: - minimist: "npm:^1.2.0" - bin: - json5: lib/cli.js - checksum: 10c0/9ee316bf21f000b00752e6c2a3b79ecf5324515a5c60ee88983a1910a45426b643a4f3461657586e8aeca87aaf96f0a519b0516d2ae527a6c3e7eed80f68717f - languageName: node - linkType: hard - -"json5@npm:^2.2.3": - version: 2.2.3 - resolution: "json5@npm:2.2.3" - bin: - json5: lib/cli.js - checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c - languageName: node - linkType: hard - -"jsonc-parser@npm:^3.3.1": - version: 3.3.1 - resolution: "jsonc-parser@npm:3.3.1" - checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 - languageName: node - linkType: hard - -"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.5": - version: 3.3.5 - resolution: "jsx-ast-utils@npm:3.3.5" - dependencies: - array-includes: "npm:^3.1.6" - array.prototype.flat: "npm:^1.3.1" - object.assign: "npm:^4.1.4" - object.values: "npm:^1.1.6" - checksum: 10c0/a32679e9cb55469cb6d8bbc863f7d631b2c98b7fc7bf172629261751a6e7bc8da6ae374ddb74d5fbd8b06cf0eb4572287b259813d92b36e384024ed35e4c13e1 - languageName: node - linkType: hard - -"keyv@npm:^4.5.4": - version: 4.5.4 - resolution: "keyv@npm:4.5.4" - dependencies: - json-buffer: "npm:3.0.1" - checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e - languageName: node - linkType: hard - -"kleur@npm:^3.0.3": - version: 3.0.3 - resolution: "kleur@npm:3.0.3" - checksum: 10c0/cd3a0b8878e7d6d3799e54340efe3591ca787d9f95f109f28129bdd2915e37807bf8918bb295ab86afb8c82196beec5a1adcaf29042ce3f2bd932b038fe3aa4b - languageName: node - linkType: hard - -"kubernetes-types@npm:^1.30.0": - version: 1.30.0 - resolution: "kubernetes-types@npm:1.30.0" - checksum: 10c0/de3641e4f50cfc123c4102a73c12932e1db8e51783c7cae4ea8ad3561bd56fab0f1c2346801f84a4c36aae8cea0b25d21e9514cc0fcecd4d64b1314043263076 - languageName: node - linkType: hard - -"language-subtag-registry@npm:^0.3.20": - version: 0.3.23 - resolution: "language-subtag-registry@npm:0.3.23" - checksum: 10c0/e9b05190421d2cd36dd6c95c28673019c927947cb6d94f40ba7e77a838629ee9675c94accf897fbebb07923187deb843b8fbb8935762df6edafe6c28dcb0b86c - languageName: node - linkType: hard - -"language-tags@npm:^1.0.9": - version: 1.0.9 - resolution: "language-tags@npm:1.0.9" - dependencies: - language-subtag-registry: "npm:^0.3.20" - checksum: 10c0/9ab911213c4bd8bd583c850201c17794e52cb0660d1ab6e32558aadc8324abebf6844e46f92b80a5d600d0fbba7eface2c207bfaf270a1c7fd539e4c3a880bff - languageName: node - linkType: hard - -"levn@npm:^0.4.1": - version: 0.4.1 - resolution: "levn@npm:0.4.1" - dependencies: - prelude-ls: "npm:^1.2.1" - type-check: "npm:~0.4.0" - checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e - languageName: node - linkType: hard - -"libphonenumber-js@npm:^1.11.20": - version: 1.12.15 - resolution: "libphonenumber-js@npm:1.12.15" - checksum: 10c0/d715c938f3e9a331b35f0dd93f5c194f7b19321b9e190b05ca76fab88df5c851936752f4354bea812df5d1d4f432a9133dc2c7199e444c7dbf89bb5b9cc75c3a - languageName: node - linkType: hard - -"lilconfig@npm:^3.1.1, lilconfig@npm:^3.1.3": - version: 3.1.3 - resolution: "lilconfig@npm:3.1.3" - checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc - languageName: node - linkType: hard - -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d - languageName: node - linkType: hard - -"locate-path@npm:^6.0.0": - version: 6.0.0 - resolution: "locate-path@npm:6.0.0" - dependencies: - p-locate: "npm:^5.0.0" - checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 - languageName: node - linkType: hard - -"lodash.merge@npm:^4.6.2": - version: 4.6.2 - resolution: "lodash.merge@npm:4.6.2" - checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 - languageName: node - linkType: hard - -"loose-envify@npm:^1.4.0": - version: 1.4.0 - resolution: "loose-envify@npm:1.4.0" - dependencies: - js-tokens: "npm:^3.0.0 || ^4.0.0" - bin: - loose-envify: cli.js - checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e - languageName: node - linkType: hard - -"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": - version: 11.2.7 - resolution: "lru-cache@npm:11.2.7" - checksum: 10c0/549cdb59488baa617135fc12159cafb1a97f91079f35093bb3bcad72e849fc64ace636d244212c181dfdf1a99bbfa90757ff303f98561958ee4d0f885d9bd5f7 - languageName: node - linkType: hard - -"lru-cache@npm:^5.1.1": - version: 5.1.1 - resolution: "lru-cache@npm:5.1.1" - dependencies: - yallist: "npm:^3.0.2" - checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 - languageName: node - linkType: hard - -"lucide-react@npm:^0.543.0": - version: 0.543.0 - resolution: "lucide-react@npm:0.543.0" - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/76bb54f9c602ff5d44ec66f2786c035d92aaabca1e1abb380b0c8101dfb3290e52c82fa4d630e286ef07c05b63696e187f0ca3ae186535d9c5b4de7aa8825bd3 - languageName: node - linkType: hard - -"make-fetch-happen@npm:^15.0.0": - version: 15.0.5 - resolution: "make-fetch-happen@npm:15.0.5" - dependencies: - "@gar/promise-retry": "npm:^1.0.0" - "@npmcli/agent": "npm:^4.0.0" - "@npmcli/redact": "npm:^4.0.0" - cacache: "npm:^20.0.1" - http-cache-semantics: "npm:^4.1.1" - minipass: "npm:^7.0.2" - minipass-fetch: "npm:^5.0.0" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - negotiator: "npm:^1.0.0" - proc-log: "npm:^6.0.0" - ssri: "npm:^13.0.0" - checksum: 10c0/527580eb5e5476e6ad07a4e3bd017d13e935f4be815674b442081ae5a721c13d3af5715006619e6be79a85723067e047f83a0c9e699f41d8cec43609a8de4f7b - languageName: node - linkType: hard - -"math-intrinsics@npm:^1.1.0": - version: 1.1.0 - resolution: "math-intrinsics@npm:1.1.0" - checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f - languageName: node - linkType: hard - -"merge2@npm:^1.3.0": - version: 1.4.1 - resolution: "merge2@npm:1.4.1" - checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb - languageName: node - linkType: hard - -"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": - version: 4.0.8 - resolution: "micromatch@npm:4.0.8" - dependencies: - braces: "npm:^3.0.3" - picomatch: "npm:^2.3.1" - checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 - languageName: node - linkType: hard - -"mimic-function@npm:^5.0.0": - version: 5.0.1 - resolution: "mimic-function@npm:5.0.1" - checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d - languageName: node - linkType: hard - -"minimatch@npm:10.2.4, minimatch@npm:^10.2.2": - version: 10.2.4 - resolution: "minimatch@npm:10.2.4" - dependencies: - brace-expansion: "npm:^5.0.2" - checksum: 10c0/35f3dfb7b99b51efd46afd378486889f590e7efb10e0f6a10ba6800428cf65c9a8dedb74427d0570b318d749b543dc4e85f06d46d2858bc8cac7e1eb49a95945 - languageName: node - linkType: hard - -"minimatch@npm:3.1.4": - version: 3.1.4 - resolution: "minimatch@npm:3.1.4" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/868aab7e5f52570107eb283f021383be111cfeee0817a615f2a9ffe61fdc8fb86d535b9bf169fb8882261e7cb9da22b4d7b6f8b3402037f63558bab173f82212 - languageName: node - linkType: hard - -"minimatch@npm:^10.2.5": - version: 10.2.5 - resolution: "minimatch@npm:10.2.5" - dependencies: - brace-expansion: "npm:^5.0.5" - checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd - languageName: node - linkType: hard - -"minimist@npm:^1.2.0, minimist@npm:^1.2.6": - version: 1.2.8 - resolution: "minimist@npm:1.2.8" - checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 - languageName: node - linkType: hard - -"minipass-collect@npm:^2.0.1": - version: 2.0.1 - resolution: "minipass-collect@npm:2.0.1" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e - languageName: node - linkType: hard - -"minipass-fetch@npm:^5.0.0": - version: 5.0.2 - resolution: "minipass-fetch@npm:5.0.2" - dependencies: - iconv-lite: "npm:^0.7.2" - minipass: "npm:^7.0.3" - minipass-sized: "npm:^2.0.0" - minizlib: "npm:^3.0.1" - dependenciesMeta: - iconv-lite: - optional: true - checksum: 10c0/ce4ab9f21cfabaead2097d95dd33f485af8072fbc6b19611bce694965393453a1639d641c2bcf1c48f2ea7d41ea7fab8278373f1d0bee4e63b0a5b2cdd0ef649 - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 - languageName: node - linkType: hard - -"minipass-sized@npm:^2.0.0": - version: 2.0.0 - resolution: "minipass-sized@npm:2.0.0" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10c0/f9201696a6f6d68610d04c9c83e3d2e5cb9c026aae1c8cbf7e17f386105cb79c1bb088dbc21bf0b1eb4f3fb5df384fd1e7aa3bf1f33868c416ae8c8a92679db8 - languageName: node - linkType: hard - -"minipass@npm:^3.0.0": - version: 3.3.6 - resolution: "minipass@npm:3.3.6" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c - languageName: node - linkType: hard - -"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3": - version: 7.1.3 - resolution: "minipass@npm:7.1.3" - checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb - languageName: node - linkType: hard - -"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": - version: 3.1.0 - resolution: "minizlib@npm:3.1.0" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec - languageName: node - linkType: hard - -"ms@npm:^2.1.1, ms@npm:^2.1.3": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 - languageName: node - linkType: hard - -"msgpackr-extract@npm:^3.0.4": - version: 3.0.4 - resolution: "msgpackr-extract@npm:3.0.4" - dependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "npm:3.0.4" - "@msgpackr-extract/msgpackr-extract-darwin-x64": "npm:3.0.4" - "@msgpackr-extract/msgpackr-extract-linux-arm": "npm:3.0.4" - "@msgpackr-extract/msgpackr-extract-linux-arm64": "npm:3.0.4" - "@msgpackr-extract/msgpackr-extract-linux-x64": "npm:3.0.4" - "@msgpackr-extract/msgpackr-extract-win32-x64": "npm:3.0.4" - node-gyp: "npm:latest" - node-gyp-build-optional-packages: "npm:5.2.2" - dependenciesMeta: - "@msgpackr-extract/msgpackr-extract-darwin-arm64": - optional: true - "@msgpackr-extract/msgpackr-extract-darwin-x64": - optional: true - "@msgpackr-extract/msgpackr-extract-linux-arm": - optional: true - "@msgpackr-extract/msgpackr-extract-linux-arm64": - optional: true - "@msgpackr-extract/msgpackr-extract-linux-x64": - optional: true - "@msgpackr-extract/msgpackr-extract-win32-x64": - optional: true - bin: - download-msgpackr-prebuilds: bin/download-prebuilds.js - checksum: 10c0/582a9d17abbf3019e600e948736695056280ce401fd0235ee2474e95f9952208b9f6cce4d0e355b03b7d3c5630e6c3d11fe5fc27fdedb2311cce48de464338d8 - languageName: node - linkType: hard - -"msgpackr@npm:^2.0.1": - version: 2.0.2 - resolution: "msgpackr@npm:2.0.2" - dependencies: - msgpackr-extract: "npm:^3.0.4" - dependenciesMeta: - msgpackr-extract: - optional: true - checksum: 10c0/158f37b1adc263c8351292d3d3513fc09831b8f15e8c3c634e6670cdf656d2c1ccbdcbf62c2c84f040b230cadace26d72c63f6898d7e39e86ae2f4c13fd05642 - languageName: node - linkType: hard - -"multipasta@npm:^0.2.7": - version: 0.2.7 - resolution: "multipasta@npm:0.2.7" - checksum: 10c0/15917ac88aeefa5b8afac44b90d1e9d0d0ec7148b51e0766f07a69a220ecebcb6404539a856c45aa85a3d7fe517bc58febe81437146705f17ecd2961dc0b9fa5 - languageName: node - linkType: hard - -"mz@npm:^2.7.0": - version: 2.7.0 - resolution: "mz@npm:2.7.0" - dependencies: - any-promise: "npm:^1.0.0" - object-assign: "npm:^4.0.1" - thenify-all: "npm:^1.0.0" - checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 - languageName: node - linkType: hard - -"nanoid@npm:^3.3.11, nanoid@npm:^3.3.6": - version: 3.3.11 - resolution: "nanoid@npm:3.3.11" - bin: - nanoid: bin/nanoid.cjs - checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b - languageName: node - linkType: hard - -"napi-postinstall@npm:^0.3.0": - version: 0.3.2 - resolution: "napi-postinstall@npm:0.3.2" - bin: - napi-postinstall: lib/cli.js - checksum: 10c0/77c67eb9871d24afe7bad30e6115c441d099d6a0e42dc1c49c4a722ff682425e08dc6dd2b03eca10db9b547e724c38fb51325c35039e7ac10dcb714bb88d7326 - languageName: node - linkType: hard - -"natural-compare@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare@npm:1.4.0" - checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 - languageName: node - linkType: hard - -"negotiator@npm:^1.0.0": - version: 1.0.0 - resolution: "negotiator@npm:1.0.0" - checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b - languageName: node - linkType: hard - -"next-themes@npm:^0.4.6": - version: 0.4.6 - resolution: "next-themes@npm:0.4.6" - peerDependencies: - react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - checksum: 10c0/83590c11d359ce7e4ced14f6ea9dd7a691d5ce6843fe2dc520fc27e29ae1c535118478d03e7f172609c41b1ef1b8da6b8dd2d2acd6cd79cac1abbdbd5b99f2c4 - languageName: node - linkType: hard - -"next@npm:16.2.3": - version: 16.2.3 - resolution: "next@npm:16.2.3" - dependencies: - "@next/env": "npm:16.2.3" - "@next/swc-darwin-arm64": "npm:16.2.3" - "@next/swc-darwin-x64": "npm:16.2.3" - "@next/swc-linux-arm64-gnu": "npm:16.2.3" - "@next/swc-linux-arm64-musl": "npm:16.2.3" - "@next/swc-linux-x64-gnu": "npm:16.2.3" - "@next/swc-linux-x64-musl": "npm:16.2.3" - "@next/swc-win32-arm64-msvc": "npm:16.2.3" - "@next/swc-win32-x64-msvc": "npm:16.2.3" - "@swc/helpers": "npm:0.5.15" - baseline-browser-mapping: "npm:^2.9.19" - caniuse-lite: "npm:^1.0.30001579" - postcss: "npm:8.4.31" - sharp: "npm:^0.34.5" - styled-jsx: "npm:5.1.6" - peerDependencies: - "@opentelemetry/api": ^1.1.0 - "@playwright/test": ^1.51.1 - babel-plugin-react-compiler: "*" - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - dependenciesMeta: - "@next/swc-darwin-arm64": - optional: true - "@next/swc-darwin-x64": - optional: true - "@next/swc-linux-arm64-gnu": - optional: true - "@next/swc-linux-arm64-musl": - optional: true - "@next/swc-linux-x64-gnu": - optional: true - "@next/swc-linux-x64-musl": - optional: true - "@next/swc-win32-arm64-msvc": - optional: true - "@next/swc-win32-x64-msvc": - optional: true - sharp: - optional: true - peerDependenciesMeta: - "@opentelemetry/api": - optional: true - "@playwright/test": - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - bin: - next: dist/bin/next - checksum: 10c0/8a9d27fc773d69f7f471cf1a23bde2ab2950e0411ef3e0d5c1664ed9654e94c3304eae1c4283ec0fa4e70e7b3f4416913350e118e0c18e8b055693dc5d021883 - languageName: node - linkType: hard - -"node-gyp-build-optional-packages@npm:5.2.2": - version: 5.2.2 - resolution: "node-gyp-build-optional-packages@npm:5.2.2" - dependencies: - detect-libc: "npm:^2.0.1" - bin: - node-gyp-build-optional-packages: bin.js - node-gyp-build-optional-packages-optional: optional.js - node-gyp-build-optional-packages-test: build-test.js - checksum: 10c0/c81128c6f91873381be178c5eddcbdf66a148a6a89a427ce2bcd457593ce69baf2a8662b6d22cac092d24aa9c43c230dec4e69b3a0da604503f4777cd77e282b - languageName: node - linkType: hard - -"node-gyp@npm:latest": - version: 12.2.0 - resolution: "node-gyp@npm:12.2.0" - dependencies: - env-paths: "npm:^2.2.0" - exponential-backoff: "npm:^3.1.1" - graceful-fs: "npm:^4.2.6" - make-fetch-happen: "npm:^15.0.0" - nopt: "npm:^9.0.0" - proc-log: "npm:^6.0.0" - semver: "npm:^7.3.5" - tar: "npm:^7.5.4" - tinyglobby: "npm:^0.2.12" - which: "npm:^6.0.0" - bin: - node-gyp: bin/node-gyp.js - checksum: 10c0/3ed046746a5a7d90950cd8b0547332b06598443f31fe213ef4332a7174c7b7d259e1704835feda79b87d3f02e59d7791842aac60642ede4396ab25fdf0f8f759 - languageName: node - linkType: hard - -"node-releases@npm:^2.0.19": - version: 2.0.20 - resolution: "node-releases@npm:2.0.20" - checksum: 10c0/24c5b1f5aa16d042c47a651ca2e022ca27320f95e4d2b76b9e543cc470eadd01032646383212ec373f1a3dd15cccce83d77c318ee99585366dbd25db4366abd8 - languageName: node - linkType: hard - -"node-releases@npm:^2.0.27": - version: 2.0.27 - resolution: "node-releases@npm:2.0.27" - checksum: 10c0/f1e6583b7833ea81880627748d28a3a7ff5703d5409328c216ae57befbced10ce2c991bea86434e8ec39003bd017f70481e2e5f8c1f7e0a7663241f81d6e00e2 - languageName: node - linkType: hard - -"nopt@npm:^9.0.0": - version: 9.0.0 - resolution: "nopt@npm:9.0.0" - dependencies: - abbrev: "npm:^4.0.0" - bin: - nopt: bin/nopt.js - checksum: 10c0/1822eb6f9b020ef6f7a7516d7b64a8036e09666ea55ac40416c36e4b2b343122c3cff0e2f085675f53de1d2db99a2a89a60ccea1d120bcd6a5347bf6ceb4a7fd - languageName: node - linkType: hard - -"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": - version: 3.0.0 - resolution: "normalize-path@npm:3.0.0" - checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 - languageName: node - linkType: hard - -"normalize-range@npm:^0.1.2": - version: 0.1.2 - resolution: "normalize-range@npm:0.1.2" - checksum: 10c0/bf39b73a63e0a42ad1a48c2bd1bda5a07ede64a7e2567307a407674e595bcff0fa0d57e8e5f1e7fa5e91000797c7615e13613227aaaa4d6d6e87f5bd5cc95de6 - languageName: node - linkType: hard - -"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 - languageName: node - linkType: hard - -"object-hash@npm:^3.0.0": - version: 3.0.0 - resolution: "object-hash@npm:3.0.0" - checksum: 10c0/a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47 - languageName: node - linkType: hard - -"object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": - version: 1.13.4 - resolution: "object-inspect@npm:1.13.4" - checksum: 10c0/d7f8711e803b96ea3191c745d6f8056ce1f2496e530e6a19a0e92d89b0fa3c76d910c31f0aa270432db6bd3b2f85500a376a83aaba849a8d518c8845b3211692 - languageName: node - linkType: hard - -"object-keys@npm:^1.1.1": - version: 1.1.1 - resolution: "object-keys@npm:1.1.1" - checksum: 10c0/b11f7ccdbc6d406d1f186cdadb9d54738e347b2692a14439ca5ac70c225fa6db46db809711b78589866d47b25fc3e8dee0b4c722ac751e11180f9380e3d8601d - languageName: node - linkType: hard - -"object.assign@npm:^4.1.4, object.assign@npm:^4.1.7": - version: 4.1.7 - resolution: "object.assign@npm:4.1.7" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" - es-object-atoms: "npm:^1.0.0" - has-symbols: "npm:^1.1.0" - object-keys: "npm:^1.1.1" - checksum: 10c0/3b2732bd860567ea2579d1567525168de925a8d852638612846bd8082b3a1602b7b89b67b09913cbb5b9bd6e95923b2ae73580baa9d99cb4e990564e8cbf5ddc - languageName: node - linkType: hard - -"object.entries@npm:^1.1.9": - version: 1.1.9 - resolution: "object.entries@npm:1.1.9" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.4" - define-properties: "npm:^1.2.1" - es-object-atoms: "npm:^1.1.1" - checksum: 10c0/d4b8c1e586650407da03370845f029aa14076caca4e4d4afadbc69cfb5b78035fd3ee7be417141abdb0258fa142e59b11923b4c44d8b1255b28f5ffcc50da7db - languageName: node - linkType: hard - -"object.fromentries@npm:^2.0.8": - version: 2.0.8 - resolution: "object.fromentries@npm:2.0.8" - dependencies: - call-bind: "npm:^1.0.7" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.2" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/cd4327e6c3369cfa805deb4cbbe919bfb7d3aeebf0bcaba291bb568ea7169f8f8cdbcabe2f00b40db0c20cd20f08e11b5f3a5a36fb7dd3fe04850c50db3bf83b - languageName: node - linkType: hard - -"object.groupby@npm:^1.0.3": - version: 1.0.3 - resolution: "object.groupby@npm:1.0.3" - dependencies: - call-bind: "npm:^1.0.7" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.2" - checksum: 10c0/60d0455c85c736fbfeda0217d1a77525956f76f7b2495edeca9e9bbf8168a45783199e77b894d30638837c654d0cc410e0e02cbfcf445bc8de71c3da1ede6a9c - languageName: node - linkType: hard - -"object.values@npm:^1.1.6, object.values@npm:^1.2.1": - version: 1.2.1 - resolution: "object.values@npm:1.2.1" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/3c47814fdc64842ae3d5a74bc9d06bdd8d21563c04d9939bf6716a9c00596a4ebc342552f8934013d1ec991c74e3671b26710a0c51815f0b603795605ab6b2c9 - languageName: node - linkType: hard - -"optionator@npm:^0.9.3": - version: 0.9.4 - resolution: "optionator@npm:0.9.4" - dependencies: - deep-is: "npm:^0.1.3" - fast-levenshtein: "npm:^2.0.6" - levn: "npm:^0.4.1" - prelude-ls: "npm:^1.2.1" - type-check: "npm:^0.4.0" - word-wrap: "npm:^1.2.5" - checksum: 10c0/4afb687a059ee65b61df74dfe87d8d6815cd6883cb8b3d5883a910df72d0f5d029821f37025e4bccf4048873dbdb09acc6d303d27b8f76b1a80dd5a7d5334675 - languageName: node - linkType: hard - -"own-keys@npm:^1.0.1": - version: 1.0.1 - resolution: "own-keys@npm:1.0.1" - dependencies: - get-intrinsic: "npm:^1.2.6" - object-keys: "npm:^1.1.1" - safe-push-apply: "npm:^1.0.0" - checksum: 10c0/6dfeb3455bff92ec3f16a982d4e3e65676345f6902d9f5ded1d8265a6318d0200ce461956d6d1c70053c7fe9f9fe65e552faac03f8140d37ef0fdd108e67013a - languageName: node - linkType: hard - -"oxc-parser@npm:^0.132.0": - version: 0.132.0 - resolution: "oxc-parser@npm:0.132.0" - dependencies: - "@oxc-parser/binding-android-arm-eabi": "npm:0.132.0" - "@oxc-parser/binding-android-arm64": "npm:0.132.0" - "@oxc-parser/binding-darwin-arm64": "npm:0.132.0" - "@oxc-parser/binding-darwin-x64": "npm:0.132.0" - "@oxc-parser/binding-freebsd-x64": "npm:0.132.0" - "@oxc-parser/binding-linux-arm-gnueabihf": "npm:0.132.0" - "@oxc-parser/binding-linux-arm-musleabihf": "npm:0.132.0" - "@oxc-parser/binding-linux-arm64-gnu": "npm:0.132.0" - "@oxc-parser/binding-linux-arm64-musl": "npm:0.132.0" - "@oxc-parser/binding-linux-ppc64-gnu": "npm:0.132.0" - "@oxc-parser/binding-linux-riscv64-gnu": "npm:0.132.0" - "@oxc-parser/binding-linux-riscv64-musl": "npm:0.132.0" - "@oxc-parser/binding-linux-s390x-gnu": "npm:0.132.0" - "@oxc-parser/binding-linux-x64-gnu": "npm:0.132.0" - "@oxc-parser/binding-linux-x64-musl": "npm:0.132.0" - "@oxc-parser/binding-openharmony-arm64": "npm:0.132.0" - "@oxc-parser/binding-wasm32-wasi": "npm:0.132.0" - "@oxc-parser/binding-win32-arm64-msvc": "npm:0.132.0" - "@oxc-parser/binding-win32-ia32-msvc": "npm:0.132.0" - "@oxc-parser/binding-win32-x64-msvc": "npm:0.132.0" - "@oxc-project/types": "npm:^0.132.0" - dependenciesMeta: - "@oxc-parser/binding-android-arm-eabi": - optional: true - "@oxc-parser/binding-android-arm64": - optional: true - "@oxc-parser/binding-darwin-arm64": - optional: true - "@oxc-parser/binding-darwin-x64": - optional: true - "@oxc-parser/binding-freebsd-x64": - optional: true - "@oxc-parser/binding-linux-arm-gnueabihf": - optional: true - "@oxc-parser/binding-linux-arm-musleabihf": - optional: true - "@oxc-parser/binding-linux-arm64-gnu": - optional: true - "@oxc-parser/binding-linux-arm64-musl": - optional: true - "@oxc-parser/binding-linux-ppc64-gnu": - optional: true - "@oxc-parser/binding-linux-riscv64-gnu": - optional: true - "@oxc-parser/binding-linux-riscv64-musl": - optional: true - "@oxc-parser/binding-linux-s390x-gnu": - optional: true - "@oxc-parser/binding-linux-x64-gnu": - optional: true - "@oxc-parser/binding-linux-x64-musl": - optional: true - "@oxc-parser/binding-openharmony-arm64": - optional: true - "@oxc-parser/binding-wasm32-wasi": - optional: true - "@oxc-parser/binding-win32-arm64-msvc": - optional: true - "@oxc-parser/binding-win32-ia32-msvc": - optional: true - "@oxc-parser/binding-win32-x64-msvc": - optional: true - checksum: 10c0/f3a4c5b8e19bed6a7dac19f19c0d94e84f9969f7c93f57e8b7713351a68c7d0378fc9e29c6afbecd41ce93cf5c18bb216959ba495c2d13bd53b82007eaa58b70 - languageName: node - linkType: hard - -"oxc-resolver@npm:^11.19.1": - version: 11.19.1 - resolution: "oxc-resolver@npm:11.19.1" - dependencies: - "@oxc-resolver/binding-android-arm-eabi": "npm:11.19.1" - "@oxc-resolver/binding-android-arm64": "npm:11.19.1" - "@oxc-resolver/binding-darwin-arm64": "npm:11.19.1" - "@oxc-resolver/binding-darwin-x64": "npm:11.19.1" - "@oxc-resolver/binding-freebsd-x64": "npm:11.19.1" - "@oxc-resolver/binding-linux-arm-gnueabihf": "npm:11.19.1" - "@oxc-resolver/binding-linux-arm-musleabihf": "npm:11.19.1" - "@oxc-resolver/binding-linux-arm64-gnu": "npm:11.19.1" - "@oxc-resolver/binding-linux-arm64-musl": "npm:11.19.1" - "@oxc-resolver/binding-linux-ppc64-gnu": "npm:11.19.1" - "@oxc-resolver/binding-linux-riscv64-gnu": "npm:11.19.1" - "@oxc-resolver/binding-linux-riscv64-musl": "npm:11.19.1" - "@oxc-resolver/binding-linux-s390x-gnu": "npm:11.19.1" - "@oxc-resolver/binding-linux-x64-gnu": "npm:11.19.1" - "@oxc-resolver/binding-linux-x64-musl": "npm:11.19.1" - "@oxc-resolver/binding-openharmony-arm64": "npm:11.19.1" - "@oxc-resolver/binding-wasm32-wasi": "npm:11.19.1" - "@oxc-resolver/binding-win32-arm64-msvc": "npm:11.19.1" - "@oxc-resolver/binding-win32-ia32-msvc": "npm:11.19.1" - "@oxc-resolver/binding-win32-x64-msvc": "npm:11.19.1" - dependenciesMeta: - "@oxc-resolver/binding-android-arm-eabi": - optional: true - "@oxc-resolver/binding-android-arm64": - optional: true - "@oxc-resolver/binding-darwin-arm64": - optional: true - "@oxc-resolver/binding-darwin-x64": - optional: true - "@oxc-resolver/binding-freebsd-x64": - optional: true - "@oxc-resolver/binding-linux-arm-gnueabihf": - optional: true - "@oxc-resolver/binding-linux-arm-musleabihf": - optional: true - "@oxc-resolver/binding-linux-arm64-gnu": - optional: true - "@oxc-resolver/binding-linux-arm64-musl": - optional: true - "@oxc-resolver/binding-linux-ppc64-gnu": - optional: true - "@oxc-resolver/binding-linux-riscv64-gnu": - optional: true - "@oxc-resolver/binding-linux-riscv64-musl": - optional: true - "@oxc-resolver/binding-linux-s390x-gnu": - optional: true - "@oxc-resolver/binding-linux-x64-gnu": - optional: true - "@oxc-resolver/binding-linux-x64-musl": - optional: true - "@oxc-resolver/binding-openharmony-arm64": - optional: true - "@oxc-resolver/binding-wasm32-wasi": - optional: true - "@oxc-resolver/binding-win32-arm64-msvc": - optional: true - "@oxc-resolver/binding-win32-ia32-msvc": - optional: true - "@oxc-resolver/binding-win32-x64-msvc": - optional: true - checksum: 10c0/8ac4eaffa9c0bcbb9f4f4a2b43786457ec5a68684d8776cb78b5a15ce3d1a79d3e67262aa3c635f98a0c1cd6cd56a31fcb05bffb9a286100056e4ab06b928833 - languageName: node - linkType: hard - -"oxlint-plugin-react-doctor@npm:0.2.6": - version: 0.2.6 - resolution: "oxlint-plugin-react-doctor@npm:0.2.6" - dependencies: - "@typescript-eslint/types": "npm:^8.59.3" - eslint-scope: "npm:^9.1.2" - eslint-visitor-keys: "npm:^5.0.1" - checksum: 10c0/53ff5b80bc9a42e8578de9d6bac23a78e2304b39b9d31d576a7e7efa7dec04a45bb74babaf8c058197eced59e7b72c8490b39b6e637158562750eaedf89cf9f8 - languageName: node - linkType: hard - -"oxlint@npm:^1.66.0": - version: 1.67.0 - resolution: "oxlint@npm:1.67.0" - dependencies: - "@oxlint/binding-android-arm-eabi": "npm:1.67.0" - "@oxlint/binding-android-arm64": "npm:1.67.0" - "@oxlint/binding-darwin-arm64": "npm:1.67.0" - "@oxlint/binding-darwin-x64": "npm:1.67.0" - "@oxlint/binding-freebsd-x64": "npm:1.67.0" - "@oxlint/binding-linux-arm-gnueabihf": "npm:1.67.0" - "@oxlint/binding-linux-arm-musleabihf": "npm:1.67.0" - "@oxlint/binding-linux-arm64-gnu": "npm:1.67.0" - "@oxlint/binding-linux-arm64-musl": "npm:1.67.0" - "@oxlint/binding-linux-ppc64-gnu": "npm:1.67.0" - "@oxlint/binding-linux-riscv64-gnu": "npm:1.67.0" - "@oxlint/binding-linux-riscv64-musl": "npm:1.67.0" - "@oxlint/binding-linux-s390x-gnu": "npm:1.67.0" - "@oxlint/binding-linux-x64-gnu": "npm:1.67.0" - "@oxlint/binding-linux-x64-musl": "npm:1.67.0" - "@oxlint/binding-openharmony-arm64": "npm:1.67.0" - "@oxlint/binding-win32-arm64-msvc": "npm:1.67.0" - "@oxlint/binding-win32-ia32-msvc": "npm:1.67.0" - "@oxlint/binding-win32-x64-msvc": "npm:1.67.0" - peerDependencies: - oxlint-tsgolint: ">=0.22.1" - vite-plus: "*" - dependenciesMeta: - "@oxlint/binding-android-arm-eabi": - optional: true - "@oxlint/binding-android-arm64": - optional: true - "@oxlint/binding-darwin-arm64": - optional: true - "@oxlint/binding-darwin-x64": - optional: true - "@oxlint/binding-freebsd-x64": - optional: true - "@oxlint/binding-linux-arm-gnueabihf": - optional: true - "@oxlint/binding-linux-arm-musleabihf": - optional: true - "@oxlint/binding-linux-arm64-gnu": - optional: true - "@oxlint/binding-linux-arm64-musl": - optional: true - "@oxlint/binding-linux-ppc64-gnu": - optional: true - "@oxlint/binding-linux-riscv64-gnu": - optional: true - "@oxlint/binding-linux-riscv64-musl": - optional: true - "@oxlint/binding-linux-s390x-gnu": - optional: true - "@oxlint/binding-linux-x64-gnu": - optional: true - "@oxlint/binding-linux-x64-musl": - optional: true - "@oxlint/binding-openharmony-arm64": - optional: true - "@oxlint/binding-win32-arm64-msvc": - optional: true - "@oxlint/binding-win32-ia32-msvc": - optional: true - "@oxlint/binding-win32-x64-msvc": - optional: true - peerDependenciesMeta: - oxlint-tsgolint: - optional: true - vite-plus: - optional: true - bin: - oxlint: bin/oxlint - checksum: 10c0/2532bebc3ed26f04e14bad2932763d1b71990c9b0bc103ab830eda990a97b9cc195b286ca5f174034684f9b1c842cbdcfd47c99adfb9519193ef23d0d3892507 - languageName: node - linkType: hard - -"p-limit@npm:^3.0.2": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: "npm:^0.1.0" - checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a - languageName: node - linkType: hard - -"p-locate@npm:^5.0.0": - version: 5.0.0 - resolution: "p-locate@npm:5.0.0" - dependencies: - p-limit: "npm:^3.0.2" - checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a - languageName: node - linkType: hard - -"p-map@npm:^7.0.2": - version: 7.0.4 - resolution: "p-map@npm:7.0.4" - checksum: 10c0/a5030935d3cb2919d7e89454d1ce82141e6f9955413658b8c9403cfe379283770ed3048146b44cde168aa9e8c716505f196d5689db0ae3ce9a71521a2fef3abd - languageName: node - linkType: hard - -"parent-module@npm:^1.0.0": - version: 1.0.1 - resolution: "parent-module@npm:1.0.1" - dependencies: - callsites: "npm:^3.0.0" - checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 - languageName: node - linkType: hard - -"path-exists@npm:^4.0.0": - version: 4.0.0 - resolution: "path-exists@npm:4.0.0" - checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b - languageName: node - linkType: hard - -"path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c - languageName: node - linkType: hard - -"path-parse@npm:^1.0.7": - version: 1.0.7 - resolution: "path-parse@npm:1.0.7" - checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 - languageName: node - linkType: hard - -"path-scurry@npm:^2.0.2": - version: 2.0.2 - resolution: "path-scurry@npm:2.0.2" - dependencies: - lru-cache: "npm:^11.0.0" - minipass: "npm:^7.1.2" - checksum: 10c0/b35ad37cf6557a87fd057121ce2be7695380c9138d93e87ae928609da259ea0a170fac6f3ef1eb3ece8a068e8b7f2f3adf5bb2374cf4d4a57fe484954fcc9482 - languageName: node - linkType: hard - -"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": - version: 1.1.1 - resolution: "picocolors@npm:1.1.1" - checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 - languageName: node - linkType: hard - -"picomatch@npm:2.3.2": - version: 2.3.2 - resolution: "picomatch@npm:2.3.2" - checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 - languageName: node - linkType: hard - -"picomatch@npm:4.0.4": - version: 4.0.4 - resolution: "picomatch@npm:4.0.4" - checksum: 10c0/e2c6023372cc7b5764719a5ffb9da0f8e781212fa7ca4bd0562db929df8e117460f00dff3cb7509dacfc06b86de924b247f504d0ce1806a37fac4633081466b0 - languageName: node - linkType: hard - -"pify@npm:^2.3.0": - version: 2.3.0 - resolution: "pify@npm:2.3.0" - checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc - languageName: node - linkType: hard - -"pirates@npm:^4.0.1": - version: 4.0.7 - resolution: "pirates@npm:4.0.7" - checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a - languageName: node - linkType: hard - -"portless@npm:0.11.1": - version: 0.11.1 - resolution: "portless@npm:0.11.1" - bin: - portless: dist/cli.js - checksum: 10c0/eeb50eba54a8b3120a5f240845fcb58f928af7c883c23f6daa576446423b8c459fa1b91ced6789ed90496b2ce4aa988f83934fbdd619fc3be7a5c8777e836866 - conditions: (os=darwin | os=linux | os=win32) - languageName: node - linkType: hard - -"possible-typed-array-names@npm:^1.0.0": - version: 1.1.0 - resolution: "possible-typed-array-names@npm:1.1.0" - checksum: 10c0/c810983414142071da1d644662ce4caebce890203eb2bc7bf119f37f3fe5796226e117e6cca146b521921fa6531072674174a3325066ac66fce089a53e1e5196 - languageName: node - linkType: hard - -"postcss-import@npm:^15.1.0": - version: 15.1.0 - resolution: "postcss-import@npm:15.1.0" - dependencies: - postcss-value-parser: "npm:^4.0.0" - read-cache: "npm:^1.0.0" - resolve: "npm:^1.1.7" - peerDependencies: - postcss: ^8.0.0 - checksum: 10c0/518aee5c83ea6940e890b0be675a2588db68b2582319f48c3b4e06535a50ea6ee45f7e63e4309f8754473245c47a0372632378d1d73d901310f295a92f26f17b - languageName: node - linkType: hard - -"postcss-js@npm:^4.0.1": - version: 4.1.0 - resolution: "postcss-js@npm:4.1.0" - dependencies: - camelcase-css: "npm:^2.0.1" - peerDependencies: - postcss: ^8.4.21 - checksum: 10c0/a3cf6e725f3e9ecd7209732f8844a0063a1380b718ccbcf93832b6ec2cd7e63ff70dd2fed49eb2483c7482296860a0f7badd3115b5d0fa05ea648eb6d9dfc9c6 - languageName: node - linkType: hard - -"postcss-load-config@npm:^4.0.2 || ^5.0 || ^6.0": - version: 6.0.1 - resolution: "postcss-load-config@npm:6.0.1" - dependencies: - lilconfig: "npm:^3.1.1" - peerDependencies: - jiti: ">=1.21.0" - postcss: ">=8.0.9" - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - jiti: - optional: true - postcss: - optional: true - tsx: - optional: true - yaml: - optional: true - checksum: 10c0/74173a58816dac84e44853f7afbd283f4ef13ca0b6baeba27701214beec33f9e309b128f8102e2b173e8d45ecba45d279a9be94b46bf48d219626aa9b5730848 - languageName: node - linkType: hard - -"postcss-nested@npm:^6.2.0": - version: 6.2.0 - resolution: "postcss-nested@npm:6.2.0" - dependencies: - postcss-selector-parser: "npm:^6.1.1" - peerDependencies: - postcss: ^8.2.14 - checksum: 10c0/7f9c3f2d764191a39364cbdcec350f26a312431a569c9ef17408021424726b0d67995ff5288405e3724bb7152a4c92f73c027e580ec91e798800ed3c52e2bc6e - languageName: node - linkType: hard - -"postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": - version: 6.1.2 - resolution: "postcss-selector-parser@npm:6.1.2" - dependencies: - cssesc: "npm:^3.0.0" - util-deprecate: "npm:^1.0.2" - checksum: 10c0/523196a6bd8cf660bdf537ad95abd79e546d54180f9afb165a4ab3e651ac705d0f8b8ce6b3164fb9e3279ce482c5f751a69eb2d3a1e8eb0fd5e82294fb3ef13e - languageName: node - linkType: hard - -"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.2.0": - version: 4.2.0 - resolution: "postcss-value-parser@npm:4.2.0" - checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 - languageName: node - linkType: hard - -"postcss@npm:8.4.31": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" - dependencies: - nanoid: "npm:^3.3.6" - picocolors: "npm:^1.0.0" - source-map-js: "npm:^1.0.2" - checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf - languageName: node - linkType: hard - -"postcss@npm:^8.4.47, postcss@npm:^8.5.6": - version: 8.5.6 - resolution: "postcss@npm:8.5.6" - dependencies: - nanoid: "npm:^3.3.11" - picocolors: "npm:^1.1.1" - source-map-js: "npm:^1.2.1" - checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024 - languageName: node - linkType: hard - -"prelude-ls@npm:^1.2.1": - version: 1.2.1 - resolution: "prelude-ls@npm:1.2.1" - checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd - languageName: node - linkType: hard - -"proc-log@npm:^6.0.0": - version: 6.1.0 - resolution: "proc-log@npm:6.1.0" - checksum: 10c0/4f178d4062733ead9d71a9b1ab24ebcecdfe2250916a5b1555f04fe2eda972a0ec76fbaa8df1ad9c02707add6749219d118a4fc46dc56bdfe4dde4b47d80bb82 - languageName: node - linkType: hard - -"prompts@npm:^2.4.2": - version: 2.4.2 - resolution: "prompts@npm:2.4.2" - dependencies: - kleur: "npm:^3.0.3" - sisteransi: "npm:^1.0.5" - checksum: 10c0/16f1ac2977b19fe2cf53f8411cc98db7a3c8b115c479b2ca5c82b5527cd937aa405fa04f9a5960abeb9daef53191b53b4d13e35c1f5d50e8718c76917c5f1ea4 - languageName: node - linkType: hard - -"prop-types@npm:^15.8.1": - version: 15.8.1 - resolution: "prop-types@npm:15.8.1" - dependencies: - loose-envify: "npm:^1.4.0" - object-assign: "npm:^4.1.1" - react-is: "npm:^16.13.1" - checksum: 10c0/59ece7ca2fb9838031d73a48d4becb9a7cc1ed10e610517c7d8f19a1e02fa47f7c27d557d8a5702bec3cfeccddc853579832b43f449e54635803f277b1c78077 - languageName: node - linkType: hard - -"punycode@npm:^2.1.0": - version: 2.3.1 - resolution: "punycode@npm:2.3.1" - checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 - languageName: node - linkType: hard - -"pure-rand@npm:^8.0.0": - version: 8.4.0 - resolution: "pure-rand@npm:8.4.0" - checksum: 10c0/6414bbc1c6f45fb774173431c7205e79783b77cfae0e2145e741b6999363554dbd2f4210d2a5bc08683e0b2f6823198c9308766b1d0911e1dccd7beb8842f860 - languageName: node - linkType: hard - -"queue-microtask@npm:^1.2.2": - version: 1.2.3 - resolution: "queue-microtask@npm:1.2.3" - checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 - languageName: node - linkType: hard - -"react-doctor@npm:0.2.6": - version: 0.2.6 - resolution: "react-doctor@npm:0.2.6" - dependencies: - "@effect/platform-node-shared": "npm:4.0.0-beta.70" - agent-install: "npm:0.0.5" - conf: "npm:^15.1.0" - deslop-js: "npm:^0.0.12" - effect: "npm:4.0.0-beta.70" - oxlint: "npm:^1.66.0" - oxlint-plugin-react-doctor: "npm:0.2.6" - prompts: "npm:^2.4.2" - typescript: "npm:>=5.0.4 <7" - peerDependencies: - eslint-plugin-react-hooks: ^6 || ^7 - peerDependenciesMeta: - eslint-plugin-react-hooks: - optional: true - bin: - react-doctor: bin/react-doctor.js - checksum: 10c0/80804f5dd8252fd12c30a272bf0c2d5bc7db57be8f1de684f5840a816053efdf701ab5dff64b0e275c2129a1f086980287e4d5fd279c009d4f03b0fb6d5e8fdc - languageName: node - linkType: hard - -"react-dom@npm:19.2.3": - version: 19.2.3 - resolution: "react-dom@npm:19.2.3" - dependencies: - scheduler: "npm:^0.27.0" - peerDependencies: - react: ^19.2.3 - checksum: 10c0/dc43f7ede06f46f3acc16ee83107c925530de9b91d1d0b3824583814746ff4c498ea64fd65cd83aba363205268adff52e2827c582634ae7b15069deaeabc4892 - languageName: node - linkType: hard - -"react-is@npm:^16.13.1": - version: 16.13.1 - resolution: "react-is@npm:16.13.1" - checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 - languageName: node - linkType: hard - -"react-phone-number-input@npm:^3.4.12": - version: 3.4.12 - resolution: "react-phone-number-input@npm:3.4.12" - dependencies: - classnames: "npm:^2.5.1" - country-flag-icons: "npm:^1.5.17" - input-format: "npm:^0.3.10" - libphonenumber-js: "npm:^1.11.20" - prop-types: "npm:^15.8.1" - peerDependencies: - react: ">=16.8" - react-dom: ">=16.8" - checksum: 10c0/96724e10c18c410602a1d03a93dfa9688b72a1f242891a1d565c4f77718f37463603b390360b8499320d5c5853feddb489bc17129c804a548f0c050f1bdef7e2 - languageName: node - linkType: hard - -"react-remove-scroll-bar@npm:^2.3.7": - version: 2.3.8 - resolution: "react-remove-scroll-bar@npm:2.3.8" - dependencies: - react-style-singleton: "npm:^2.2.2" - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/9a0675c66cbb52c325bdbfaed80987a829c4504cefd8ff2dd3b6b3afc9a1500b8ec57b212e92c1fb654396d07bbe18830a8146fe77677d2a29ce40b5e1f78654 - languageName: node - linkType: hard - -"react-remove-scroll@npm:^2.6.3": - version: 2.7.1 - resolution: "react-remove-scroll@npm:2.7.1" - dependencies: - react-remove-scroll-bar: "npm:^2.3.7" - react-style-singleton: "npm:^2.2.3" - tslib: "npm:^2.1.0" - use-callback-ref: "npm:^1.3.3" - use-sidecar: "npm:^1.1.3" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/7ad8f6ffd3e2aedf9b3d79f0c9088a9a3d7c5332d80c923427a6d97fe0626fb4cb33a6d9174d19fad57d860be69c96f68497a0619c3a8af0e8a5332e49bdde31 - languageName: node - linkType: hard - -"react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": - version: 2.2.3 - resolution: "react-style-singleton@npm:2.2.3" - dependencies: - get-nonce: "npm:^1.0.0" - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/841938ff16d16a6b76895f4cb2e1fea957e5fe3b30febbf03a54892dae1c9153f2383e231dea0b3ba41192ad2f2849448fa859caccd288943bce32639e971bee - languageName: node - linkType: hard - -"react@npm:19.2.3": - version: 19.2.3 - resolution: "react@npm:19.2.3" - checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 - languageName: node - linkType: hard - -"read-cache@npm:^1.0.0": - version: 1.0.0 - resolution: "read-cache@npm:1.0.0" - dependencies: - pify: "npm:^2.3.0" - checksum: 10c0/90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814 - languageName: node - linkType: hard - -"readdirp@npm:~3.6.0": - version: 3.6.0 - resolution: "readdirp@npm:3.6.0" - dependencies: - picomatch: "npm:^2.2.1" - checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b - languageName: node - linkType: hard - -"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9": - version: 1.0.10 - resolution: "reflect.getprototypeof@npm:1.0.10" - dependencies: - call-bind: "npm:^1.0.8" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.9" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.7" - get-proto: "npm:^1.0.1" - which-builtin-type: "npm:^1.2.1" - checksum: 10c0/7facec28c8008876f8ab98e80b7b9cb4b1e9224353fd4756dda5f2a4ab0d30fa0a5074777c6df24e1e0af463a2697513b0a11e548d99cf52f21f7bc6ba48d3ac - languageName: node - linkType: hard - -"regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": - version: 1.5.4 - resolution: "regexp.prototype.flags@npm:1.5.4" - dependencies: - call-bind: "npm:^1.0.8" - define-properties: "npm:^1.2.1" - es-errors: "npm:^1.3.0" - get-proto: "npm:^1.0.1" - gopd: "npm:^1.2.0" - set-function-name: "npm:^2.0.2" - checksum: 10c0/83b88e6115b4af1c537f8dabf5c3744032cb875d63bc05c288b1b8c0ef37cbe55353f95d8ca817e8843806e3e150b118bc624e4279b24b4776b4198232735a77 - languageName: node - linkType: hard - -"require-from-string@npm:^2.0.2": - version: 2.0.2 - resolution: "require-from-string@npm:2.0.2" - checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2 - languageName: node - linkType: hard - -"resolve-from@npm:^4.0.0": - version: 4.0.0 - resolution: "resolve-from@npm:4.0.0" - checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 - languageName: node - linkType: hard - -"resolve-pkg-maps@npm:^1.0.0": - version: 1.0.0 - resolution: "resolve-pkg-maps@npm:1.0.0" - checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab - languageName: node - linkType: hard - -"resolve@npm:^1.1.7, resolve@npm:^1.22.8": - version: 1.22.11 - resolution: "resolve@npm:1.22.11" - dependencies: - is-core-module: "npm:^2.16.1" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/f657191507530f2cbecb5815b1ee99b20741ea6ee02a59c57028e9ec4c2c8d7681afcc35febbd554ac0ded459db6f2d8153382c53a2f266cee2575e512674409 - languageName: node - linkType: hard - -"resolve@npm:^1.22.4": - version: 1.22.10 - resolution: "resolve@npm:1.22.10" - dependencies: - is-core-module: "npm:^2.16.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/8967e1f4e2cc40f79b7e080b4582b9a8c5ee36ffb46041dccb20e6461161adf69f843b43067b4a375de926a2cd669157e29a29578191def399dd5ef89a1b5203 - languageName: node - linkType: hard - -"resolve@npm:^2.0.0-next.5": - version: 2.0.0-next.5 - resolution: "resolve@npm:2.0.0-next.5" - dependencies: - is-core-module: "npm:^2.13.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/a6c33555e3482ea2ec4c6e3d3bf0d78128abf69dca99ae468e64f1e30acaa318fd267fb66c8836b04d558d3e2d6ed875fe388067e7d8e0de647d3c21af21c43a - languageName: node - linkType: hard - -"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": - version: 1.22.11 - resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" - dependencies: - is-core-module: "npm:^2.16.1" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/ee5b182f2e37cb1165465e58c6abc797fec0a80b5ba3231607beb4677db0c9291ac010c47cf092b6daa2b7f518d69a0e21888e7e2b633f68d501a874212a8c63 - languageName: node - linkType: hard - -"resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": - version: 1.22.10 - resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" - dependencies: - is-core-module: "npm:^2.16.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/52a4e505bbfc7925ac8f4cd91fd8c4e096b6a89728b9f46861d3b405ac9a1ccf4dcbf8befb4e89a2e11370dacd0160918163885cbc669369590f2f31f4c58939 - languageName: node - linkType: hard - -"resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin": - version: 2.0.0-next.5 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin::version=2.0.0-next.5&hash=c3c19d" - dependencies: - is-core-module: "npm:^2.13.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/78ad6edb8309a2bfb720c2c1898f7907a37f858866ce11a5974643af1203a6a6e05b2fa9c53d8064a673a447b83d42569260c306d43628bff5bb101969708355 - languageName: node - linkType: hard - -"reusify@npm:^1.0.4": - version: 1.1.0 - resolution: "reusify@npm:1.1.0" - checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa - languageName: node - linkType: hard - -"run-parallel@npm:^1.1.9": - version: 1.2.0 - resolution: "run-parallel@npm:1.2.0" - dependencies: - queue-microtask: "npm:^1.2.2" - checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 - languageName: node - linkType: hard - -"safe-array-concat@npm:^1.1.3": - version: 1.1.3 - resolution: "safe-array-concat@npm:1.1.3" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.6" - has-symbols: "npm:^1.1.0" - isarray: "npm:^2.0.5" - checksum: 10c0/43c86ffdddc461fb17ff8a17c5324f392f4868f3c7dd2c6a5d9f5971713bc5fd755667212c80eab9567595f9a7509cc2f83e590ddaebd1bd19b780f9c79f9a8d - languageName: node - linkType: hard - -"safe-push-apply@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-push-apply@npm:1.0.0" - dependencies: - es-errors: "npm:^1.3.0" - isarray: "npm:^2.0.5" - checksum: 10c0/831f1c9aae7436429e7862c7e46f847dfe490afac20d0ee61bae06108dbf5c745a0de3568ada30ccdd3eeb0864ca8331b2eef703abd69bfea0745b21fd320750 - languageName: node - linkType: hard - -"safe-regex-test@npm:^1.0.3, safe-regex-test@npm:^1.1.0": - version: 1.1.0 - resolution: "safe-regex-test@npm:1.1.0" - dependencies: - call-bound: "npm:^1.0.2" - es-errors: "npm:^1.3.0" - is-regex: "npm:^1.2.1" - checksum: 10c0/f2c25281bbe5d39cddbbce7f86fca5ea9b3ce3354ea6cd7c81c31b006a5a9fff4286acc5450a3b9122c56c33eba69c56b9131ad751457b2b4a585825e6a10665 - languageName: node - linkType: hard - -"safer-buffer@npm:>= 2.1.2 < 3.0.0": - version: 2.1.2 - resolution: "safer-buffer@npm:2.1.2" - checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 - languageName: node - linkType: hard - -"scheduler@npm:^0.27.0": - version: 0.27.0 - resolution: "scheduler@npm:0.27.0" - checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 - languageName: node - linkType: hard - -"semver@npm:^6.3.1": - version: 6.3.1 - resolution: "semver@npm:6.3.1" - bin: - semver: bin/semver.js - checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d - languageName: node - linkType: hard - -"semver@npm:^7.3.5, semver@npm:^7.7.3": - version: 7.7.4 - resolution: "semver@npm:7.7.4" - bin: - semver: bin/semver.js - checksum: 10c0/5215ad0234e2845d4ea5bb9d836d42b03499546ddafb12075566899fc617f68794bb6f146076b6881d755de17d6c6cc73372555879ec7dce2c2feee947866ad2 - languageName: node - linkType: hard - -"semver@npm:^7.7.1": - version: 7.7.2 - resolution: "semver@npm:7.7.2" - bin: - semver: bin/semver.js - checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea - languageName: node - linkType: hard - -"semver@npm:^7.7.2": - version: 7.8.1 - resolution: "semver@npm:7.8.1" - bin: - semver: bin/semver.js - checksum: 10c0/92d6871d6347e1f99d0ba396a70f2545ccf2a032cda3d378fa0699edf7506b5c6d266aed55c8b88e72bd91a30d2351e4f39db479375374430fcdc4b58f4e3c1a - languageName: node - linkType: hard - -"set-function-length@npm:^1.2.2": - version: 1.2.2 - resolution: "set-function-length@npm:1.2.2" - dependencies: - define-data-property: "npm:^1.1.4" - es-errors: "npm:^1.3.0" - function-bind: "npm:^1.1.2" - get-intrinsic: "npm:^1.2.4" - gopd: "npm:^1.0.1" - has-property-descriptors: "npm:^1.0.2" - checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c - languageName: node - linkType: hard - -"set-function-name@npm:^2.0.2": - version: 2.0.2 - resolution: "set-function-name@npm:2.0.2" - dependencies: - define-data-property: "npm:^1.1.4" - es-errors: "npm:^1.3.0" - functions-have-names: "npm:^1.2.3" - has-property-descriptors: "npm:^1.0.2" - checksum: 10c0/fce59f90696c450a8523e754abb305e2b8c73586452619c2bad5f7bf38c7b6b4651895c9db895679c5bef9554339cf3ef1c329b66ece3eda7255785fbe299316 - languageName: node - linkType: hard - -"set-proto@npm:^1.0.0": - version: 1.0.0 - resolution: "set-proto@npm:1.0.0" - dependencies: - dunder-proto: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/ca5c3ccbba479d07c30460e367e66337cec825560b11e8ba9c5ebe13a2a0d6021ae34eddf94ff3dfe17a3104dc1f191519cb6c48378b503e5c3f36393938776a - languageName: node - linkType: hard - -"sharp@npm:^0.34.5": - version: 0.34.5 - resolution: "sharp@npm:0.34.5" - dependencies: - "@img/colour": "npm:^1.0.0" - "@img/sharp-darwin-arm64": "npm:0.34.5" - "@img/sharp-darwin-x64": "npm:0.34.5" - "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" - "@img/sharp-libvips-darwin-x64": "npm:1.2.4" - "@img/sharp-libvips-linux-arm": "npm:1.2.4" - "@img/sharp-libvips-linux-arm64": "npm:1.2.4" - "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" - "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" - "@img/sharp-libvips-linux-s390x": "npm:1.2.4" - "@img/sharp-libvips-linux-x64": "npm:1.2.4" - "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" - "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" - "@img/sharp-linux-arm": "npm:0.34.5" - "@img/sharp-linux-arm64": "npm:0.34.5" - "@img/sharp-linux-ppc64": "npm:0.34.5" - "@img/sharp-linux-riscv64": "npm:0.34.5" - "@img/sharp-linux-s390x": "npm:0.34.5" - "@img/sharp-linux-x64": "npm:0.34.5" - "@img/sharp-linuxmusl-arm64": "npm:0.34.5" - "@img/sharp-linuxmusl-x64": "npm:0.34.5" - "@img/sharp-wasm32": "npm:0.34.5" - "@img/sharp-win32-arm64": "npm:0.34.5" - "@img/sharp-win32-ia32": "npm:0.34.5" - "@img/sharp-win32-x64": "npm:0.34.5" - detect-libc: "npm:^2.1.2" - semver: "npm:^7.7.3" - dependenciesMeta: - "@img/sharp-darwin-arm64": - optional: true - "@img/sharp-darwin-x64": - optional: true - "@img/sharp-libvips-darwin-arm64": - optional: true - "@img/sharp-libvips-darwin-x64": - optional: true - "@img/sharp-libvips-linux-arm": - optional: true - "@img/sharp-libvips-linux-arm64": - optional: true - "@img/sharp-libvips-linux-ppc64": - optional: true - "@img/sharp-libvips-linux-riscv64": - optional: true - "@img/sharp-libvips-linux-s390x": - optional: true - "@img/sharp-libvips-linux-x64": - optional: true - "@img/sharp-libvips-linuxmusl-arm64": - optional: true - "@img/sharp-libvips-linuxmusl-x64": - optional: true - "@img/sharp-linux-arm": - optional: true - "@img/sharp-linux-arm64": - optional: true - "@img/sharp-linux-ppc64": - optional: true - "@img/sharp-linux-riscv64": - optional: true - "@img/sharp-linux-s390x": - optional: true - "@img/sharp-linux-x64": - optional: true - "@img/sharp-linuxmusl-arm64": - optional: true - "@img/sharp-linuxmusl-x64": - optional: true - "@img/sharp-wasm32": - optional: true - "@img/sharp-win32-arm64": - optional: true - "@img/sharp-win32-ia32": - optional: true - "@img/sharp-win32-x64": - optional: true - checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: "npm:^3.0.0" - checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 - languageName: node - linkType: hard - -"side-channel-list@npm:^1.0.0": - version: 1.0.0 - resolution: "side-channel-list@npm:1.0.0" - dependencies: - es-errors: "npm:^1.3.0" - object-inspect: "npm:^1.13.3" - checksum: 10c0/644f4ac893456c9490ff388bf78aea9d333d5e5bfc64cfb84be8f04bf31ddc111a8d4b83b85d7e7e8a7b845bc185a9ad02c052d20e086983cf59f0be517d9b3d - languageName: node - linkType: hard - -"side-channel-map@npm:^1.0.1": - version: 1.0.1 - resolution: "side-channel-map@npm:1.0.1" - dependencies: - call-bound: "npm:^1.0.2" - es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.5" - object-inspect: "npm:^1.13.3" - checksum: 10c0/010584e6444dd8a20b85bc926d934424bd809e1a3af941cace229f7fdcb751aada0fb7164f60c2e22292b7fa3c0ff0bce237081fd4cdbc80de1dc68e95430672 - languageName: node - linkType: hard - -"side-channel-weakmap@npm:^1.0.2": - version: 1.0.2 - resolution: "side-channel-weakmap@npm:1.0.2" - dependencies: - call-bound: "npm:^1.0.2" - es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.5" - object-inspect: "npm:^1.13.3" - side-channel-map: "npm:^1.0.1" - checksum: 10c0/71362709ac233e08807ccd980101c3e2d7efe849edc51455030327b059f6c4d292c237f94dc0685031dd11c07dd17a68afde235d6cf2102d949567f98ab58185 - languageName: node - linkType: hard - -"side-channel@npm:^1.1.0": - version: 1.1.0 - resolution: "side-channel@npm:1.1.0" - dependencies: - es-errors: "npm:^1.3.0" - object-inspect: "npm:^1.13.3" - side-channel-list: "npm:^1.0.0" - side-channel-map: "npm:^1.0.1" - side-channel-weakmap: "npm:^1.0.2" - checksum: 10c0/cb20dad41eb032e6c24c0982e1e5a24963a28aa6122b4f05b3f3d6bf8ae7fd5474ef382c8f54a6a3ab86e0cac4d41a23bd64ede3970e5bfb50326ba02a7996e6 - languageName: node - linkType: hard - -"sisteransi@npm:^1.0.5": - version: 1.0.5 - resolution: "sisteransi@npm:1.0.5" - checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 - languageName: node - linkType: hard - -"smart-buffer@npm:^4.2.0": - version: 4.2.0 - resolution: "smart-buffer@npm:4.2.0" - checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^8.0.3": - version: 8.0.5 - resolution: "socks-proxy-agent@npm:8.0.5" - dependencies: - agent-base: "npm:^7.1.2" - debug: "npm:^4.3.4" - socks: "npm:^2.8.3" - checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6 - languageName: node - linkType: hard - -"socks@npm:^2.8.3": - version: 2.8.7 - resolution: "socks@npm:2.8.7" - dependencies: - ip-address: "npm:^10.0.1" - smart-buffer: "npm:^4.2.0" - checksum: 10c0/2805a43a1c4bcf9ebf6e018268d87b32b32b06fbbc1f9282573583acc155860dc361500f89c73bfbb157caa1b4ac78059eac0ef15d1811eb0ca75e0bdadbc9d2 - languageName: node - linkType: hard - -"source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.1": - version: 1.2.1 - resolution: "source-map-js@npm:1.2.1" - checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf - languageName: node - linkType: hard - -"ssri@npm:^13.0.0": - version: 13.0.1 - resolution: "ssri@npm:13.0.1" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/cf6408a18676c57ff2ed06b8a20dc64bb3e748e5c7e095332e6aecaa2b8422b1e94a739a8453bf65156a8a47afe23757ba4ab52d3ea3b62322dc40875763e17a - languageName: node - linkType: hard - -"stable-hash@npm:^0.0.5": - version: 0.0.5 - resolution: "stable-hash@npm:0.0.5" - checksum: 10c0/ca670cb6d172f1c834950e4ec661e2055885df32fee3ebf3647c5df94993b7c2666a5dbc1c9a62ee11fc5c24928579ec5e81bb5ad31971d355d5a341aab493b3 - languageName: node - linkType: hard - -"stop-iteration-iterator@npm:^1.1.0": - version: 1.1.0 - resolution: "stop-iteration-iterator@npm:1.1.0" - dependencies: - es-errors: "npm:^1.3.0" - internal-slot: "npm:^1.1.0" - checksum: 10c0/de4e45706bb4c0354a4b1122a2b8cc45a639e86206807ce0baf390ee9218d3ef181923fa4d2b67443367c491aa255c5fbaa64bb74648e3c5b48299928af86c09 - languageName: node - linkType: hard - -"string.prototype.includes@npm:^2.0.1": - version: 2.0.1 - resolution: "string.prototype.includes@npm:2.0.1" - dependencies: - call-bind: "npm:^1.0.7" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.3" - checksum: 10c0/25ce9c9b49128352a2618fbe8758b46f945817a58a4420f4799419e40a8d28f116e176c7590d767d5327a61e75c8f32c86171063f48e389b9fdd325f1bd04ee5 - languageName: node - linkType: hard - -"string.prototype.matchall@npm:^4.0.12": - version: 4.0.12 - resolution: "string.prototype.matchall@npm:4.0.12" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.6" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.6" - gopd: "npm:^1.2.0" - has-symbols: "npm:^1.1.0" - internal-slot: "npm:^1.1.0" - regexp.prototype.flags: "npm:^1.5.3" - set-function-name: "npm:^2.0.2" - side-channel: "npm:^1.1.0" - checksum: 10c0/1a53328ada73f4a77f1fdf1c79414700cf718d0a8ef6672af5603e709d26a24f2181208144aed7e858b1bcc1a0d08567a570abfb45567db4ae47637ed2c2f85c - languageName: node - linkType: hard - -"string.prototype.repeat@npm:^1.0.0": - version: 1.0.0 - resolution: "string.prototype.repeat@npm:1.0.0" - dependencies: - define-properties: "npm:^1.1.3" - es-abstract: "npm:^1.17.5" - checksum: 10c0/94c7978566cffa1327d470fd924366438af9b04b497c43a9805e476e2e908aa37a1fd34cc0911156c17556dab62159d12c7b92b3cc304c3e1281fe4c8e668f40 - languageName: node - linkType: hard - -"string.prototype.trim@npm:^1.2.10": - version: 1.2.10 - resolution: "string.prototype.trim@npm:1.2.10" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.2" - define-data-property: "npm:^1.1.4" - define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.5" - es-object-atoms: "npm:^1.0.0" - has-property-descriptors: "npm:^1.0.2" - checksum: 10c0/8a8854241c4b54a948e992eb7dd6b8b3a97185112deb0037a134f5ba57541d8248dd610c966311887b6c2fd1181a3877bffb14d873ce937a344535dabcc648f8 - languageName: node - linkType: hard - -"string.prototype.trimend@npm:^1.0.9": - version: 1.0.9 - resolution: "string.prototype.trimend@npm:1.0.9" - dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.2" - define-properties: "npm:^1.2.1" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/59e1a70bf9414cb4c536a6e31bef5553c8ceb0cf44d8b4d0ed65c9653358d1c64dd0ec203b100df83d0413bbcde38b8c5d49e14bc4b86737d74adc593a0d35b6 - languageName: node - linkType: hard - -"string.prototype.trimstart@npm:^1.0.8": - version: 1.0.8 - resolution: "string.prototype.trimstart@npm:1.0.8" - dependencies: - call-bind: "npm:^1.0.7" - define-properties: "npm:^1.2.1" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/d53af1899959e53c83b64a5fd120be93e067da740e7e75acb433849aa640782fb6c7d4cd5b84c954c84413745a3764df135a8afeb22908b86a835290788d8366 - languageName: node - linkType: hard - -"strip-bom@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-bom@npm:3.0.0" - checksum: 10c0/51201f50e021ef16672593d7434ca239441b7b760e905d9f33df6e4f3954ff54ec0e0a06f100d028af0982d6f25c35cd5cda2ce34eaebccd0250b8befb90d8f1 - languageName: node - linkType: hard - -"strip-json-comments@npm:^3.1.1": - version: 3.1.1 - resolution: "strip-json-comments@npm:3.1.1" - checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd - languageName: node - linkType: hard - -"stubborn-fs@npm:^2.0.0": - version: 2.0.0 - resolution: "stubborn-fs@npm:2.0.0" - dependencies: - stubborn-utils: "npm:^1.0.1" - checksum: 10c0/31a60c9b2a61ce380b688f2649acaeff63cb0f2503bb6820c3ccd4f3af584c6310a48efa41b40c16b1717f1728572ed887c2c88650955c776a088228797e8d0e - languageName: node - linkType: hard - -"stubborn-utils@npm:^1.0.1": - version: 1.0.2 - resolution: "stubborn-utils@npm:1.0.2" - checksum: 10c0/e65c5820d02c993df55c88e938796c2fb2f3a6d3dc247c961d1e4be4d6d88c355283f4b74157e89d1a1a761d66b5ae79560a416384241a7d3b4e8ba8f1ff5a78 - languageName: node - linkType: hard - -"styled-jsx@npm:5.1.6": - version: 5.1.6 - resolution: "styled-jsx@npm:5.1.6" - dependencies: - client-only: "npm:0.0.1" - peerDependencies: - react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - peerDependenciesMeta: - "@babel/core": - optional: true - babel-plugin-macros: - optional: true - checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5 - languageName: node - linkType: hard - -"sucrase@npm:^3.35.0": - version: 3.35.1 - resolution: "sucrase@npm:3.35.1" - dependencies: - "@jridgewell/gen-mapping": "npm:^0.3.2" - commander: "npm:^4.0.0" - lines-and-columns: "npm:^1.1.6" - mz: "npm:^2.7.0" - pirates: "npm:^4.0.1" - tinyglobby: "npm:^0.2.11" - ts-interface-checker: "npm:^0.1.9" - bin: - sucrase: bin/sucrase - sucrase-node: bin/sucrase-node - checksum: 10c0/6fa22329c261371feb9560630d961ad0d0b9c87dce21ea74557c5f3ffbe5c1ee970ea8bcce9962ae9c90c3c47165ffa7dd41865c7414f5d8ea7a40755d612c5c - languageName: node - linkType: hard - -"supports-color@npm:^7.1.0": - version: 7.2.0 - resolution: "supports-color@npm:7.2.0" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 - languageName: node - linkType: hard - -"supports-preserve-symlinks-flag@npm:^1.0.0": - version: 1.0.0 - resolution: "supports-preserve-symlinks-flag@npm:1.0.0" - checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 - languageName: node - linkType: hard - -"tagged-tag@npm:^1.0.0": - version: 1.0.0 - resolution: "tagged-tag@npm:1.0.0" - checksum: 10c0/91d25c9ffb86a91f20522cefb2cbec9b64caa1febe27ad0df52f08993ff60888022d771e868e6416cf2e72dab68449d2139e8709ba009b74c6c7ecd4000048d1 - languageName: node - linkType: hard - -"tailwind-merge@npm:^3.3.1": - version: 3.3.1 - resolution: "tailwind-merge@npm:3.3.1" - checksum: 10c0/b84c6a78d4669fa12bf5ab8f0cdc4400a3ce0a7c006511af4af4be70bb664a27466dbe13ee9e3b31f50ddf6c51d380e8192ce0ec9effce23ca729d71a9f63818 - languageName: node - linkType: hard - -"tailwindcss@npm:^3.4.19": - version: 3.4.19 - resolution: "tailwindcss@npm:3.4.19" - dependencies: - "@alloc/quick-lru": "npm:^5.2.0" - arg: "npm:^5.0.2" - chokidar: "npm:^3.6.0" - didyoumean: "npm:^1.2.2" - dlv: "npm:^1.1.3" - fast-glob: "npm:^3.3.2" - glob-parent: "npm:^6.0.2" - is-glob: "npm:^4.0.3" - jiti: "npm:^1.21.7" - lilconfig: "npm:^3.1.3" - micromatch: "npm:^4.0.8" - normalize-path: "npm:^3.0.0" - object-hash: "npm:^3.0.0" - picocolors: "npm:^1.1.1" - postcss: "npm:^8.4.47" - postcss-import: "npm:^15.1.0" - postcss-js: "npm:^4.0.1" - postcss-load-config: "npm:^4.0.2 || ^5.0 || ^6.0" - postcss-nested: "npm:^6.2.0" - postcss-selector-parser: "npm:^6.1.2" - resolve: "npm:^1.22.8" - sucrase: "npm:^3.35.0" - bin: - tailwind: lib/cli.js - tailwindcss: lib/cli.js - checksum: 10c0/e1063daccb9e5a508b357ec73b0011354204b2366b56496d6f0cc822733a55a0551502cb85856a2257ef9b676d0026616daaaa176d391f3216df57fbd693c581 - languageName: node - linkType: hard - -"tar@npm:^7.5.4": - version: 7.5.11 - resolution: "tar@npm:7.5.11" - dependencies: - "@isaacs/fs-minipass": "npm:^4.0.0" - chownr: "npm:^3.0.0" - minipass: "npm:^7.1.2" - minizlib: "npm:^3.1.0" - yallist: "npm:^5.0.0" - checksum: 10c0/b6bb420550ef50ef23356018155e956cd83282c97b6128d8d5cfe5740c57582d806a244b2ef0bf686a74ce526babe8b8b9061527623e935e850008d86d838929 - languageName: node - linkType: hard - -"thenify-all@npm:^1.0.0": - version: 1.6.0 - resolution: "thenify-all@npm:1.6.0" - dependencies: - thenify: "npm:>= 3.1.0 < 4" - checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b - languageName: node - linkType: hard - -"thenify@npm:>= 3.1.0 < 4": - version: 3.3.1 - resolution: "thenify@npm:3.3.1" - dependencies: - any-promise: "npm:^1.0.0" - checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 - languageName: node - linkType: hard - -"tinyglobby@npm:^0.2.11, tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15": - version: 0.2.15 - resolution: "tinyglobby@npm:0.2.15" - dependencies: - fdir: "npm:^6.5.0" - picomatch: "npm:^4.0.3" - checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844 - languageName: node - linkType: hard - -"tinyglobby@npm:^0.2.13": - version: 0.2.14 - resolution: "tinyglobby@npm:0.2.14" - dependencies: - fdir: "npm:^6.4.4" - picomatch: "npm:^4.0.2" - checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6 - languageName: node - linkType: hard - -"to-regex-range@npm:^5.0.1": - version: 5.0.1 - resolution: "to-regex-range@npm:5.0.1" - dependencies: - is-number: "npm:^7.0.0" - checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 - languageName: node - linkType: hard - -"toml@npm:^4.1.1": - version: 4.1.1 - resolution: "toml@npm:4.1.1" - checksum: 10c0/077bc02ac1ce82091ea073f675d7e2a1df487d1b18bbc7e653daba4956d545954b7095e979b8792f0837339b901ee190ad4464342e5e377c36bbdeca8903e079 - languageName: node - linkType: hard - -"ts-api-utils@npm:^2.4.0": - version: 2.5.0 - resolution: "ts-api-utils@npm:2.5.0" - peerDependencies: - typescript: ">=4.8.4" - checksum: 10c0/767849383c114e7f1971fa976b20e73ac28fd0c70d8d65c0004790bf4d8f89888c7e4cf6d5949f9c1beae9bc3c64835bef77bbe27fddf45a3c7b60cebcf85c8c - languageName: node - linkType: hard - -"ts-interface-checker@npm:^0.1.9": - version: 0.1.13 - resolution: "ts-interface-checker@npm:0.1.13" - checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 - languageName: node - linkType: hard - -"tsconfig-paths@npm:^3.15.0": - version: 3.15.0 - resolution: "tsconfig-paths@npm:3.15.0" - dependencies: - "@types/json5": "npm:^0.0.29" - json5: "npm:^1.0.2" - minimist: "npm:^1.2.6" - strip-bom: "npm:^3.0.0" - checksum: 10c0/5b4f301a2b7a3766a986baf8fc0e177eb80bdba6e396792ff92dc23b5bca8bb279fc96517dcaaef63a3b49bebc6c4c833653ec58155780bc906bdbcf7dda0ef5 - languageName: node - linkType: hard - -"tslib@npm:2.7.0": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 10c0/469e1d5bf1af585742128827000711efa61010b699cb040ab1800bcd3ccdd37f63ec30642c9e07c4439c1db6e46345582614275daca3e0f4abae29b0083f04a6 - languageName: node - linkType: hard - -"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0": - version: 2.8.1 - resolution: "tslib@npm:2.8.1" - checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 - languageName: node - linkType: hard - -"type-check@npm:^0.4.0, type-check@npm:~0.4.0": - version: 0.4.0 - resolution: "type-check@npm:0.4.0" - dependencies: - prelude-ls: "npm:^1.2.1" - checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58 - languageName: node - linkType: hard - -"type-fest@npm:^5.0.0": - version: 5.6.0 - resolution: "type-fest@npm:5.6.0" - dependencies: - tagged-tag: "npm:^1.0.0" - checksum: 10c0/5468a8ffda7f3904e6f7bbd8069eb8b6dd4bd9156e206df7a01d09a73e28cd1afedf74ead9d0fc12841c8c90074194859feca240511c50800962fde1bd9ddcbc - languageName: node - linkType: hard - -"typed-array-buffer@npm:^1.0.3": - version: 1.0.3 - resolution: "typed-array-buffer@npm:1.0.3" - dependencies: - call-bound: "npm:^1.0.3" - es-errors: "npm:^1.3.0" - is-typed-array: "npm:^1.1.14" - checksum: 10c0/1105071756eb248774bc71646bfe45b682efcad93b55532c6ffa4518969fb6241354e4aa62af679ae83899ec296d69ef88f1f3763657cdb3a4d29321f7b83079 - languageName: node - linkType: hard - -"typed-array-byte-length@npm:^1.0.3": - version: 1.0.3 - resolution: "typed-array-byte-length@npm:1.0.3" - dependencies: - call-bind: "npm:^1.0.8" - for-each: "npm:^0.3.3" - gopd: "npm:^1.2.0" - has-proto: "npm:^1.2.0" - is-typed-array: "npm:^1.1.14" - checksum: 10c0/6ae083c6f0354f1fce18b90b243343b9982affd8d839c57bbd2c174a5d5dc71be9eb7019ffd12628a96a4815e7afa85d718d6f1e758615151d5f35df841ffb3e - languageName: node - linkType: hard - -"typed-array-byte-offset@npm:^1.0.4": - version: 1.0.4 - resolution: "typed-array-byte-offset@npm:1.0.4" - dependencies: - available-typed-arrays: "npm:^1.0.7" - call-bind: "npm:^1.0.8" - for-each: "npm:^0.3.3" - gopd: "npm:^1.2.0" - has-proto: "npm:^1.2.0" - is-typed-array: "npm:^1.1.15" - reflect.getprototypeof: "npm:^1.0.9" - checksum: 10c0/3d805b050c0c33b51719ee52de17c1cd8e6a571abdf0fffb110e45e8dd87a657e8b56eee94b776b13006d3d347a0c18a730b903cf05293ab6d92e99ff8f77e53 - languageName: node - linkType: hard - -"typed-array-length@npm:^1.0.7": - version: 1.0.7 - resolution: "typed-array-length@npm:1.0.7" - dependencies: - call-bind: "npm:^1.0.7" - for-each: "npm:^0.3.3" - gopd: "npm:^1.0.1" - is-typed-array: "npm:^1.1.13" - possible-typed-array-names: "npm:^1.0.0" - reflect.getprototypeof: "npm:^1.0.6" - checksum: 10c0/e38f2ae3779584c138a2d8adfa8ecf749f494af3cd3cdafe4e688ce51418c7d2c5c88df1bd6be2bbea099c3f7cea58c02ca02ed438119e91f162a9de23f61295 - languageName: node - linkType: hard - -"typescript-eslint@npm:^8.46.0": - version: 8.57.1 - resolution: "typescript-eslint@npm:8.57.1" - dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.57.1" - "@typescript-eslint/parser": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" - "@typescript-eslint/utils": "npm:8.57.1" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/be5a19738a785a2695e01874cbedbddbb63ea0a1c2eac331be7d251bda35116505f4d4d8de5a25a77a09392396247af4b89d2a793580217af4891e9e5036a716 - languageName: node - linkType: hard - -"typescript@npm:>=5.0.4 <7": - version: 5.9.3 - resolution: "typescript@npm:5.9.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 - languageName: node - linkType: hard - -"typescript@npm:^6.0.2": - version: 6.0.2 - resolution: "typescript@npm:6.0.2" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/4b860b0bf87cc0fee0f66d8ef2640b5a8a8a8c74d1129adb82e389e5f97124383823c47946bef8a73ede371461143a3aa8544399d2133c7b2e4f07e81860af7f - languageName: node - linkType: hard - -"typescript@npm:^6.0.3": - version: 6.0.3 - resolution: "typescript@npm:6.0.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/4a25ff5045b984370f48f196b3a0120779b1b343d40b9a68d114ea5e5fff099809b2bb777576991a63a5cd59cf7bffd96ff6fe10afcefbcb8bd6fb96ad4b6606 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A>=5.0.4 <7#optional!builtin": - version: 5.9.3 - resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A^6.0.2#optional!builtin": - version: 6.0.2 - resolution: "typescript@patch:typescript@npm%3A6.0.2#optional!builtin::version=6.0.2&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/49f0b84fc6ca55653e77752b8a61beabc09ee3dae5d965c31596225aa6ef213c5727b1d2e895b900416dc603854ba0872ac4a812c2a4ed6793a601f9c675de02 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A^6.0.3#optional!builtin": - version: 6.0.3 - resolution: "typescript@patch:typescript@npm%3A6.0.3#optional!builtin::version=6.0.3&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/2f25c74e65663c248fa1ade2b8459d9ce5372ff9dad07067310f132966ebec1d93f6c42f0baf77a6b6a7a91460463f708e6887013aaade22111037457c6b25df - languageName: node - linkType: hard - -"uint8array-extras@npm:^1.5.0": - version: 1.5.0 - resolution: "uint8array-extras@npm:1.5.0" - checksum: 10c0/0e74641ac7dadb02eadefc1ccdadba6010e007757bda824960de3c72bbe2b04e6d3af75648441f412148c4103261d54fcb60be45a2863beb76643a55fddba3bd - languageName: node - linkType: hard - -"unbox-primitive@npm:^1.1.0": - version: 1.1.0 - resolution: "unbox-primitive@npm:1.1.0" - dependencies: - call-bound: "npm:^1.0.3" - has-bigints: "npm:^1.0.2" - has-symbols: "npm:^1.1.0" - which-boxed-primitive: "npm:^1.1.1" - checksum: 10c0/7dbd35ab02b0e05fe07136c72cb9355091242455473ec15057c11430129bab38b7b3624019b8778d02a881c13de44d63cd02d122ee782fb519e1de7775b5b982 - languageName: node - linkType: hard - -"uncrypto@npm:^0.1.3": - version: 0.1.3 - resolution: "uncrypto@npm:0.1.3" - checksum: 10c0/74a29afefd76d5b77bedc983559ceb33f5bbc8dada84ff33755d1e3355da55a4e03a10e7ce717918c436b4dfafde1782e799ebaf2aadd775612b49f7b5b2998e - languageName: node - linkType: hard - -"undici-types@npm:>=7.24.0 <7.24.7": - version: 7.24.6 - resolution: "undici-types@npm:7.24.6" - checksum: 10c0/d9cd8befb643ac904615c280a095ba4240531f6bb4a5e75a22a7483630ca8d3f1016d2ab6ace6ceda1f63b3a2db2fe037fafe121d6917a0187573aa548ff78ca - languageName: node - linkType: hard - -"undici-types@npm:~6.19.2": - version: 6.19.8 - resolution: "undici-types@npm:6.19.8" - checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 - languageName: node - linkType: hard - -"undici-types@npm:~6.21.0": - version: 6.21.0 - resolution: "undici-types@npm:6.21.0" - checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 - languageName: node - linkType: hard - -"unrs-resolver@npm:^1.6.2": - version: 1.11.1 - resolution: "unrs-resolver@npm:1.11.1" - dependencies: - "@unrs/resolver-binding-android-arm-eabi": "npm:1.11.1" - "@unrs/resolver-binding-android-arm64": "npm:1.11.1" - "@unrs/resolver-binding-darwin-arm64": "npm:1.11.1" - "@unrs/resolver-binding-darwin-x64": "npm:1.11.1" - "@unrs/resolver-binding-freebsd-x64": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm64-musl": "npm:1.11.1" - "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.11.1" - "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-x64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-x64-musl": "npm:1.11.1" - "@unrs/resolver-binding-wasm32-wasi": "npm:1.11.1" - "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.11.1" - "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.11.1" - "@unrs/resolver-binding-win32-x64-msvc": "npm:1.11.1" - napi-postinstall: "npm:^0.3.0" - dependenciesMeta: - "@unrs/resolver-binding-android-arm-eabi": - optional: true - "@unrs/resolver-binding-android-arm64": - optional: true - "@unrs/resolver-binding-darwin-arm64": - optional: true - "@unrs/resolver-binding-darwin-x64": - optional: true - "@unrs/resolver-binding-freebsd-x64": - optional: true - "@unrs/resolver-binding-linux-arm-gnueabihf": - optional: true - "@unrs/resolver-binding-linux-arm-musleabihf": - optional: true - "@unrs/resolver-binding-linux-arm64-gnu": - optional: true - "@unrs/resolver-binding-linux-arm64-musl": - optional: true - "@unrs/resolver-binding-linux-ppc64-gnu": - optional: true - "@unrs/resolver-binding-linux-riscv64-gnu": - optional: true - "@unrs/resolver-binding-linux-riscv64-musl": - optional: true - "@unrs/resolver-binding-linux-s390x-gnu": - optional: true - "@unrs/resolver-binding-linux-x64-gnu": - optional: true - "@unrs/resolver-binding-linux-x64-musl": - optional: true - "@unrs/resolver-binding-wasm32-wasi": - optional: true - "@unrs/resolver-binding-win32-arm64-msvc": - optional: true - "@unrs/resolver-binding-win32-ia32-msvc": - optional: true - "@unrs/resolver-binding-win32-x64-msvc": - optional: true - checksum: 10c0/c91b112c71a33d6b24e5c708dab43ab80911f2df8ee65b87cd7a18fb5af446708e98c4b415ca262026ad8df326debcc7ca6a801b2935504d87fd6f0b9d70dce1 - languageName: node - linkType: hard - -"update-browserslist-db@npm:^1.1.3": - version: 1.1.3 - resolution: "update-browserslist-db@npm:1.1.3" - dependencies: - escalade: "npm:^3.2.0" - picocolors: "npm:^1.1.1" - peerDependencies: - browserslist: ">= 4.21.0" - bin: - update-browserslist-db: cli.js - checksum: 10c0/682e8ecbf9de474a626f6462aa85927936cdd256fe584c6df2508b0df9f7362c44c957e9970df55dfe44d3623807d26316ea2c7d26b80bb76a16c56c37233c32 - languageName: node - linkType: hard - -"update-browserslist-db@npm:^1.2.0": - version: 1.2.3 - resolution: "update-browserslist-db@npm:1.2.3" - dependencies: - escalade: "npm:^3.2.0" - picocolors: "npm:^1.1.1" - peerDependencies: - browserslist: ">= 4.21.0" - bin: - update-browserslist-db: cli.js - checksum: 10c0/13a00355ea822388f68af57410ce3255941d5fb9b7c49342c4709a07c9f230bbef7f7499ae0ca7e0de532e79a82cc0c4edbd125f1a323a1845bf914efddf8bec - languageName: node - linkType: hard - -"uri-js@npm:^4.2.2": - version: 4.4.1 - resolution: "uri-js@npm:4.4.1" - dependencies: - punycode: "npm:^2.1.0" - checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c - languageName: node - linkType: hard - -"use-callback-ref@npm:^1.3.3": - version: 1.3.3 - resolution: "use-callback-ref@npm:1.3.3" - dependencies: - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/f887488c6e6075cdad4962979da1714b217bcb1ee009a9e57ce9a844bcfc4c3a99e93983dfc2e5af9e0913824d24e730090ff255e902c516dcb58d2d3837e01c - languageName: node - linkType: hard - -"use-sidecar@npm:^1.1.3": - version: 1.1.3 - resolution: "use-sidecar@npm:1.1.3" - dependencies: - detect-node-es: "npm:^1.1.0" - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/161599bf921cfaa41c85d2b01c871975ee99260f3e874c2d41c05890d41170297bdcf314bc5185e7a700de2034ac5b888e3efc8e9f35724f4918f53538d717c9 - languageName: node - linkType: hard - -"util-deprecate@npm:^1.0.2": - version: 1.0.2 - resolution: "util-deprecate@npm:1.0.2" - checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 - languageName: node - linkType: hard - -"uuid@npm:^14.0.0": - version: 14.0.0 - resolution: "uuid@npm:14.0.0" - bin: - uuid: dist-node/bin/uuid - checksum: 10c0/a57ae7794c45005c1a9208989196c5baf79a7679c30f43c1bee9033a2c4d113a2cea216fa6fcc9663b08b0d55635df1a7c6eb7e7f3d21c3e50688c698fa39a50 - languageName: node - linkType: hard - -"web@workspace:.": - version: 0.0.0-use.local - resolution: "web@workspace:." - dependencies: - "@eslint/eslintrc": "npm:^3" - "@radix-ui/react-dialog": "npm:^1.1.15" - "@radix-ui/react-dropdown-menu": "npm:^2.1.16" - "@radix-ui/react-label": "npm:^2.1.7" - "@radix-ui/react-popover": "npm:^1.1.15" - "@radix-ui/react-scroll-area": "npm:^1.2.10" - "@radix-ui/react-slot": "npm:^1.2.3" - "@radix-ui/react-toast": "npm:^1.2.15" - "@types/canvas-confetti": "npm:^1.9.0" - "@types/minimatch": "npm:^6.0.0" - "@types/node": "npm:^20" - "@types/react": "npm:^19.2.7" - "@types/react-dom": "npm:^19.2.3" - "@upstash/ratelimit": "npm:2.0.6" - "@upstash/redis": "npm:1.35.3" - "@vercel/analytics": "npm:^1.5.0" - "@vercel/kv": "npm:3.0.0" - autoprefixer: "npm:^10.4.21" - canvas-confetti: "npm:^1.9.3" - class-variance-authority: "npm:^0.7.1" - clsx: "npm:^2.1.1" - cmdk: "npm:^1.1.1" - eslint: "npm:^9.39.2" - eslint-config-next: "npm:16.2.3" - ethers: "npm:6.13.4" - input-otp: "npm:^1.4.2" - lucide-react: "npm:^0.543.0" - next: "npm:16.2.3" - next-themes: "npm:^0.4.6" - portless: "npm:0.11.1" - postcss: "npm:^8.5.6" - react: "npm:19.2.3" - react-doctor: "npm:0.2.6" - react-dom: "npm:19.2.3" - react-phone-number-input: "npm:^3.4.12" - tailwind-merge: "npm:^3.3.1" - tailwindcss: "npm:^3.4.19" - typescript: "npm:^6.0.2" - zod: "npm:4.0.15" - languageName: unknown - linkType: soft - -"when-exit@npm:^2.1.4": - version: 2.1.5 - resolution: "when-exit@npm:2.1.5" - checksum: 10c0/7db41b28c98456b784c25780ca327653f233c6eb7b25d4ce251d04519828cbd609fb6d10548caf9031d4d6fab2999d6f6911c32e1731efab24c93a522573470d - languageName: node - linkType: hard - -"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": - version: 1.1.1 - resolution: "which-boxed-primitive@npm:1.1.1" - dependencies: - is-bigint: "npm:^1.1.0" - is-boolean-object: "npm:^1.2.1" - is-number-object: "npm:^1.1.1" - is-string: "npm:^1.1.1" - is-symbol: "npm:^1.1.1" - checksum: 10c0/aceea8ede3b08dede7dce168f3883323f7c62272b49801716e8332ff750e7ae59a511ae088840bc6874f16c1b7fd296c05c949b0e5b357bfe3c431b98c417abe - languageName: node - linkType: hard - -"which-builtin-type@npm:^1.2.1": - version: 1.2.1 - resolution: "which-builtin-type@npm:1.2.1" - dependencies: - call-bound: "npm:^1.0.2" - function.prototype.name: "npm:^1.1.6" - has-tostringtag: "npm:^1.0.2" - is-async-function: "npm:^2.0.0" - is-date-object: "npm:^1.1.0" - is-finalizationregistry: "npm:^1.1.0" - is-generator-function: "npm:^1.0.10" - is-regex: "npm:^1.2.1" - is-weakref: "npm:^1.0.2" - isarray: "npm:^2.0.5" - which-boxed-primitive: "npm:^1.1.0" - which-collection: "npm:^1.0.2" - which-typed-array: "npm:^1.1.16" - checksum: 10c0/8dcf323c45e5c27887800df42fbe0431d0b66b1163849bb7d46b5a730ad6a96ee8bfe827d078303f825537844ebf20c02459de41239a0a9805e2fcb3cae0d471 - languageName: node - linkType: hard - -"which-collection@npm:^1.0.2": - version: 1.0.2 - resolution: "which-collection@npm:1.0.2" - dependencies: - is-map: "npm:^2.0.3" - is-set: "npm:^2.0.3" - is-weakmap: "npm:^2.0.2" - is-weakset: "npm:^2.0.3" - checksum: 10c0/3345fde20964525a04cdf7c4a96821f85f0cc198f1b2ecb4576e08096746d129eb133571998fe121c77782ac8f21cbd67745a3d35ce100d26d4e684c142ea1f2 - languageName: node - linkType: hard - -"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": - version: 1.1.19 - resolution: "which-typed-array@npm:1.1.19" - dependencies: - available-typed-arrays: "npm:^1.0.7" - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.4" - for-each: "npm:^0.3.5" - get-proto: "npm:^1.0.1" - gopd: "npm:^1.2.0" - has-tostringtag: "npm:^1.0.2" - checksum: 10c0/702b5dc878addafe6c6300c3d0af5983b175c75fcb4f2a72dfc3dd38d93cf9e89581e4b29c854b16ea37e50a7d7fca5ae42ece5c273d8060dcd603b2404bbb3f - languageName: node - linkType: hard - -"which@npm:^2.0.1": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: "npm:^2.0.0" - bin: - node-which: ./bin/node-which - checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f - languageName: node - linkType: hard - -"which@npm:^6.0.0": - version: 6.0.1 - resolution: "which@npm:6.0.1" - dependencies: - isexe: "npm:^4.0.0" - bin: - node-which: bin/which.js - checksum: 10c0/7e710e54ea36d2d6183bee2f9caa27a3b47b9baf8dee55a199b736fcf85eab3b9df7556fca3d02b50af7f3dfba5ea3a45644189836df06267df457e354da66d5 - languageName: node - linkType: hard - -"word-wrap@npm:^1.2.5": - version: 1.2.5 - resolution: "word-wrap@npm:1.2.5" - checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20 - languageName: node - linkType: hard - -"ws@npm:8.17.1": - version: 8.17.1 - resolution: "ws@npm:8.17.1" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10c0/f4a49064afae4500be772abdc2211c8518f39e1c959640457dcee15d4488628620625c783902a52af2dd02f68558da2868fd06e6fd0e67ebcd09e6881b1b5bfe - languageName: node - linkType: hard - -"ws@npm:^8.20.0": - version: 8.21.0 - resolution: "ws@npm:8.21.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10c0/ef4a243476283fc49bc7550966c4af4aa0eef56273837211e700de3b664e08604a760cdddcb5ba43c049140e74ccfec5b0ee0bb439e08c2adf9138902fdde5f9 - languageName: node - linkType: hard - -"yallist@npm:^3.0.2": - version: 3.1.1 - resolution: "yallist@npm:3.1.1" - checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a - languageName: node - linkType: hard - -"yallist@npm:^5.0.0": - version: 5.0.0 - resolution: "yallist@npm:5.0.0" - checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 - languageName: node - linkType: hard - -"yaml@npm:^2.8.3": - version: 2.8.4 - resolution: "yaml@npm:2.8.4" - bin: - yaml: bin.mjs - checksum: 10c0/0a33a1fa28d4bc79f61a12ec7ef7a2bce0ce5f8e80c6eaecfb4a0c88c08767dd1ede372b6a3bcd70891213b8c9f3169b355c97e77026d3b3459e10d2cccaef1e - languageName: node - linkType: hard - -"yaml@npm:^2.9.0": - version: 2.9.0 - resolution: "yaml@npm:2.9.0" - bin: - yaml: bin.mjs - checksum: 10c0/f340718df45e97a9551b9bf9dac61c80050bc464513b710debfb5067c380c8472e3b67809cffacb4ab5ffb5e66ef9310816c88b05f371cec60abfedd8c88e0a2 - languageName: node - linkType: hard - -"yocto-queue@npm:^0.1.0": - version: 0.1.0 - resolution: "yocto-queue@npm:0.1.0" - checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f - languageName: node - linkType: hard - -"zod-validation-error@npm:^3.5.0 || ^4.0.0": - version: 4.0.2 - resolution: "zod-validation-error@npm:4.0.2" - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - checksum: 10c0/0ccfec48c46de1be440b719cd02044d4abb89ed0e14c13e637cd55bf29102f67ccdba373f25def0fc7130e5f15025be4d557a7edcc95d5a3811599aade689e1b - languageName: node - linkType: hard - -"zod@npm:4.0.15": - version: 4.0.15 - resolution: "zod@npm:4.0.15" - checksum: 10c0/c4d5b0c6668fe32fb6040d713d75dbf65e2e46cb68c77755b7b66426e8583d5dacf49a44f90beb6b12aa1aa2545cc0d3c186e368711021e11dd3c1f0addb9100 - languageName: node - linkType: hard - -"zod@npm:^3.25.0 || ^4.0.0": - version: 4.3.6 - resolution: "zod@npm:4.3.6" - checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307 - languageName: node - linkType: hard