From 24c96a3905f979d4554c916c97b057043079fe9b Mon Sep 17 00:00:00 2001 From: Screen <228304862+screen-gd@users.noreply.github.com> Date: Wed, 21 Oct 2026 03:13:00 +0400 Subject: [PATCH 01/20] Move the build from Next.js to Vite and TanStack Start The app now builds with Vite and runs on TanStack Start while every route, component and localStorage key keeps its current behaviour. - package.json: drop Next/PostCSS, add @tanstack/react-start and @tanstack/react-router, Vite 8 and Tailwind's Vite plugin; scripts become dev/build/start/lint/typecheck/test - vite.config.ts: Tailwind plugin, React plugin and TanStack Start configured with srcDirectory "src" and routesDirectory "routes" - tsconfig.json: bundler resolution, "@/*" path alias, Vite client types - eslint.config.mjs: flat config that reproduces the six jsx-a11y rules plus the two react-hooks rules the previous next/core-web-vitals preset enforced, so the port does not smuggle in an accessibility or behaviour refactor - .gitignore: ignore dist/ and .tanstack/ instead of .next/, and stop ignoring .env.example - remove next.config.ts, proxy.ts, postcss.config.mjs, next-env.d.ts and the one-off migration helper scripts --- .gitignore | 8 +- eslint.config.mjs | 72 +- next-env.d.ts | 6 - next.config.ts | 9 - package-lock.json | 15921 +++++++++++++------------- package.json | 102 +- postcss.config.mjs | 7 - proxy.ts | 38 - scripts/ensure-next-route-shims.mjs | 17 - scripts/render_logo.js | 48 - tmp_fix.js | 103 - tsconfig.json | 23 +- vite.config.ts | 26 + vitest.config.ts | 4 +- 14 files changed, 7932 insertions(+), 8452 deletions(-) delete mode 100644 next-env.d.ts delete mode 100644 next.config.ts delete mode 100644 postcss.config.mjs delete mode 100644 proxy.ts delete mode 100644 scripts/ensure-next-route-shims.mjs delete mode 100644 scripts/render_logo.js delete mode 100644 tmp_fix.js create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore index f7b27d4..1261d50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ node_modules -.next +dist +.tanstack .env .env.local -.env.example +.env.*.local coverage -dist *.log *.tsbuildinfo .agents\skills -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/eslint.config.mjs b/eslint.config.mjs index 8c9e7f2..3685713 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,25 +1,61 @@ -import { dirname } from "node:path"; -import { fileURLToPath } from "node:url"; -import { FlatCompat } from "@eslint/eslintrc"; +import js from "@eslint/js"; +import jsxA11y from "eslint-plugin-jsx-a11y"; +import reactHooks from "eslint-plugin-react-hooks"; +import globals from "globals"; +import tseslint from "typescript-eslint"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const config = [ +export default tseslint.config( { ignores: [ - ".next/**", - ".agents/**", "node_modules/**", - "coverage/**", "dist/**", + ".tanstack/**", + ".agents/**", + "coverage/**", + "src/routeTree.gen.ts", ], }, - ...compat.extends("next/core-web-vitals"), -]; - -export default config; + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ["**/*.{js,mjs,cjs,ts,tsx}"], + languageOptions: { + globals: { + ...globals.browser, + ...globals.node, + }, + }, + plugins: { + "jsx-a11y": jsxA11y, + }, + rules: { + // The previous `next/core-web-vitals` config enabled only this + // subset of jsx-a11y. Keeping the same gate avoids turning the + // framework port into an accessibility refactor. + "jsx-a11y/alt-text": "error", + "jsx-a11y/aria-props": "error", + "jsx-a11y/aria-proptypes": "error", + "jsx-a11y/aria-unsupported-elements": "error", + "jsx-a11y/role-has-required-aria-props": "error", + "jsx-a11y/role-supports-aria-props": "error", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + ], + }, + }, + { + files: ["**/*.{ts,tsx}"], + plugins: { + "react-hooks": reactHooks, + }, + rules: { + // Parity with the previous `next/core-web-vitals` setup. The newer + // React Hooks preset adds compiler-oriented rules that flag existing + // patterns; adopting those is a separate, behaviour-affecting change. + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + }, + }, +); diff --git a/next-env.d.ts b/next-env.d.ts deleted file mode 100644 index 9edff1c..0000000 --- a/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/types/routes.d.ts"; - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.ts b/next.config.ts deleted file mode 100644 index 5af2f21..0000000 --- a/next.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - experimental: { - optimizePackageImports: ["lucide-react"], - }, -}; - -export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 3d391d3..2bcd910 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8140 +1,7789 @@ { - "name": "zns-roadmap-studio", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "zns-roadmap-studio", - "version": "1.0.0", - "dependencies": { - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-switch": "^1.2.6", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "framer-motion": "^12.34.5", - "geist": "^1.7.0", - "lucide-react": "^0.576.0", - "motion": "^12.35.1", - "next": "^16.1.6", - "next-themes": "^0.4.6", - "openai": "^6.27.0", - "react": "^19.2.4", - "react-dom": "^19.2.4", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.0.0", - "@types/node": "^22.0.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "@vitest/ui": "^2.1.8", - "axe-core": "^4.11.1", - "chalk": "^5.6.2", - "eslint": "^9.0.0", - "eslint-config-next": "^15.1.0", - "fast-check": "^4.6.0", - "playwright": "^1.58.2", - "postcss": "^8.5.0", - "tailwindcss": "^4.0.0", - "typescript": "^5.7.0", - "vitest": "^2.1.8" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", - "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", - "dev": true, - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.3", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.3", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", - "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", - "dependencies": { - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", - "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", - "dependencies": { - "@floating-ui/dom": "^1.7.6" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==" - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@next/env": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", - "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.12.tgz", - "integrity": "sha512-+ZRSDFTv4aC96aMb5E41rMjysx8ApkryevnvEYZvPZO52KvkqP5rNExLUXJFr9P4s0f3oqNQR6vopCZsPWKDcQ==", - "dev": true, - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", - "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", - "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", - "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", - "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", - "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", - "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", - "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", - "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true - }, - "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==" - }, - "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", - "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.15", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", - "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", - "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-switch": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", - "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", - "dependencies": { - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", - "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", - "dev": true - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", - "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", - "dev": true, - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", - "jiti": "^2.6.1", - "lightningcss": "1.31.1", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.2.1" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", - "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", - "dev": true, - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-x64": "4.2.1", - "@tailwindcss/oxide-freebsd-x64": "4.2.1", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-x64-musl": "4.2.1", - "@tailwindcss/oxide-wasm32-wasi": "4.2.1", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", - "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", - "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", - "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", - "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", - "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", - "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", - "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", - "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", - "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", - "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", - "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", - "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/postcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz", - "integrity": "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==", - "dev": true, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.2.1", - "@tailwindcss/oxide": "4.2.1", - "postcss": "^8.5.6", - "tailwindcss": "4.2.1" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "22.19.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", - "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", - "dev": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "devOptional": true, - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", - "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/type-utils": "8.56.1", - "@typescript-eslint/utils": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.56.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", - "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", - "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.56.1", - "@typescript-eslint/types": "^8.56.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", - "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", - "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", - "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", - "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", - "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", - "dev": true, - "dependencies": { - "@typescript-eslint/project-service": "8.56.1", - "@typescript-eslint/tsconfig-utils": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", - "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", - "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.56.1", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@vitest/expect": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", - "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", - "dev": true, - "dependencies": { - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", - "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", - "dev": true, - "dependencies": { - "@vitest/spy": "2.1.8", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", - "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", - "dev": true, - "dependencies": { - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", - "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", - "dev": true, - "dependencies": { - "@vitest/utils": "2.1.8", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", - "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", - "dev": true, - "dependencies": { - "@vitest/pretty-format": "2.1.8", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", - "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", - "dev": true, - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/ui": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-2.1.8.tgz", - "integrity": "sha512-5zPJ1fs0ixSVSs5+5V2XJjXLmNzjugHRyV11RqxYVR+oMcogZ9qTuSfKW+OcTV0JeFNznI83BNylzH6SSNJ1+w==", - "dev": true, - "dependencies": { - "@vitest/utils": "2.1.8", - "fflate": "^0.8.2", - "flatted": "^3.3.1", - "pathe": "^1.1.2", - "sirv": "^3.0.0", - "tinyglobby": "^0.2.10", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "2.1.8" - } - }, - "node_modules/@vitest/utils": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", - "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", - "dev": true, - "dependencies": { - "@vitest/pretty-format": "2.1.8", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", - "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001776", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz", - "integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "engines": { - "node": ">= 16" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", - "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.3", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", - "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.3", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-next": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.12.tgz", - "integrity": "sha512-ktW3XLfd+ztEltY5scJNjxjHwtKWk6vU2iwzZqSN09UsbBmMeE/cVlJ1yESg6Yx5LW7p/Z8WzUAgYXGLEmGIpg==", - "dev": true, - "dependencies": { - "@next/eslint-plugin-next": "15.5.12", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "dev": true, - "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", - "dev": true, - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", - "dev": true, - "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-check": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.6.0.tgz", - "integrity": "sha512-h7H6Dm0Fy+H4ciQYFxFjXnXkzR2kr9Fb22c0UBpHnm59K2zpr2t13aPTHlltFiNT6zuxp6HMPAVVvgur4BLdpA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "dependencies": { - "pure-rand": "^8.0.0" - }, - "engines": { - "node": ">=12.17.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "dev": true - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.0.tgz", - "integrity": "sha512-kC6Bb+ooptOIvWj5B63EQWkF0FEnNjV2ZNkLMLZRDDduIiWeFF4iKnslwhiWxjAdbg4NzTNo6h0qLuvFrcx+Sw==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/framer-motion": { - "version": "12.35.1", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.35.1.tgz", - "integrity": "sha512-rL8cLrjYZNShZqKV3U0Qj6Y5WDiZXYEM5giiTLfEqsIZxtspzMDCkKmrO5po76jWfvOg04+Vk+sfBvTD0iMmLw==", - "dependencies": { - "motion-dom": "^12.35.1", - "motion-utils": "^12.29.2", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/geist": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/geist/-/geist-1.7.0.tgz", - "integrity": "sha512-ZaoiZwkSf0DwwB1ncdLKp+ggAldqxl5L1+SXaNIBGkPAqcu+xjVJLxlf3/S8vLt9UHx1xu5fz3lbzKCj5iOVdQ==", - "peerDependencies": { - "next": ">=13.2.0" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, - "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", - "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", - "dev": true, - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.31.1", - "lightningcss-darwin-arm64": "1.31.1", - "lightningcss-darwin-x64": "1.31.1", - "lightningcss-freebsd-x64": "1.31.1", - "lightningcss-linux-arm-gnueabihf": "1.31.1", - "lightningcss-linux-arm64-gnu": "1.31.1", - "lightningcss-linux-arm64-musl": "1.31.1", - "lightningcss-linux-x64-gnu": "1.31.1", - "lightningcss-linux-x64-musl": "1.31.1", - "lightningcss-win32-arm64-msvc": "1.31.1", - "lightningcss-win32-x64-msvc": "1.31.1" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", - "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", - "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", - "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", - "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", - "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", - "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", - "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", - "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", - "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", - "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", - "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true - }, - "node_modules/lucide-react": { - "version": "0.576.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.576.0.tgz", - "integrity": "sha512-koNxU14BXrxUfZQ9cUaP0ES1uyPZKYDjk31FQZB6dQ/x+tXk979sVAn9ppZ/pVeJJyOxVM8j1E+8QEuSc02Vug==", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/motion": { - "version": "12.35.1", - "resolved": "https://registry.npmjs.org/motion/-/motion-12.35.1.tgz", - "integrity": "sha512-yEt/49kWC0VU/IEduDfeZw82eDemlPwa1cyo/gcEEUCN4WgpSJpUcxz6BUwakGabvJiTzLQ58J73515I5tfykQ==", - "dependencies": { - "framer-motion": "^12.35.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/motion-dom": { - "version": "12.35.1", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.35.1.tgz", - "integrity": "sha512-7n6r7TtNOsH2UFSAXzTkfzOeO5616v9B178qBIjmu/WgEyJK0uqwytCEhwKBTuM/HJA40ptAw7hLFpxtPAMRZQ==", - "dependencies": { - "motion-utils": "^12.29.2" - } - }, - "node_modules/motion-utils": { - "version": "12.29.2", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz", - "integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/next": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", - "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", - "dependencies": { - "@next/env": "16.1.6", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.6", - "@next/swc-darwin-x64": "16.1.6", - "@next/swc-linux-arm64-gnu": "16.1.6", - "@next/swc-linux-arm64-musl": "16.1.6", - "@next/swc-linux-x64-gnu": "16.1.6", - "@next/swc-linux-x64-musl": "16.1.6", - "@next/swc-win32-arm64-msvc": "16.1.6", - "@next/swc-win32-x64-msvc": "16.1.6", - "sharp": "^0.34.4" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/node-exports-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", - "dev": true, - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/node-exports-info/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/openai": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.27.0.tgz", - "integrity": "sha512-osTKySlrdYrLYTt0zjhY8yp0JUBmWDCN+Q+QxsV4xMQnnoVFpylgKGgxwN8sSdTNw0G4y+WUXs4eCMWpyDNWZQ==", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "dev": true, - "dependencies": { - "playwright-core": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "dev": true, - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.0.0.tgz", - "integrity": "sha512-7rgWlxG2gAvFPIQfUreo1XYlNvrQ9VnQPFWdncPkdl3icucLK0InOxsaafbvxGTnI6Bk/Rxmslg0lQlRCuzOXw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.4" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "devOptional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "node_modules/sirv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", - "dev": true, - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/sonner": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", - "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwind-merge": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", - "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", - "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", - "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", - "dev": true, - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/vitest": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", - "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", - "dev": true, - "dependencies": { - "@vitest/expect": "2.1.8", - "@vitest/mocker": "2.1.8", - "@vitest/pretty-format": "^2.1.8", - "@vitest/runner": "2.1.8", - "@vitest/snapshot": "2.1.8", - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.8", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.8", - "@vitest/ui": "2.1.8", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true + "name": "syllo", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "syllo", + "version": "1.0.0", + "dependencies": { + "@fontsource-variable/geist": "5.3.0", + "@fontsource-variable/geist-mono": "5.3.0", + "@radix-ui/react-alert-dialog": "1.1.15", + "@radix-ui/react-select": "2.2.6", + "@radix-ui/react-separator": "1.1.8", + "@radix-ui/react-slot": "1.2.4", + "@radix-ui/react-switch": "1.2.6", + "@tanstack/react-router": "1.170.45", + "@tanstack/react-start": "1.169.3", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "framer-motion": "12.34.5", + "lucide-react": "0.576.0", + "motion": "12.35.1", + "openai": "6.27.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "sonner": "2.0.7", + "tailwind-merge": "3.5.0" + }, + "devDependencies": { + "@eslint/js": "9.39.5", + "@tailwindcss/vite": "4.3.3", + "@tanstack/router-plugin": "1.168.47", + "@types/node": "24.19.2", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "@vitejs/plugin-react": "6.1.2", + "eslint": "9.39.5", + "eslint-plugin-jsx-a11y": "6.10.2", + "eslint-plugin-react-hooks": "7.1.1", + "globals": "17.13.0", + "tailwindcss": "4.3.3", + "typescript": "5.9.3", + "typescript-eslint": "8.72.0", + "vite": "8.3.4", + "vitest": "4.1.12" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@fontsource-variable/geist": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/geist/-/geist-5.3.0.tgz", + "integrity": "sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/geist-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/geist-mono/-/geist-mono-5.3.0.tgz", + "integrity": "sha512-vBbuwDEo9AkrqADMXOrlAR3DFcJi4/JxeuU43FoiQERnNwsfXNnvxvReZG02cQKmyk4DZkZdBZX3oTDvy2zBAw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oozcitak/dom": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz", + "integrity": "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==", + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "^2.0.2", + "@oozcitak/url": "^3.0.0", + "@oozcitak/util": "^10.0.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oozcitak/infra": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-2.0.2.tgz", + "integrity": "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==", + "license": "MIT", + "dependencies": { + "@oozcitak/util": "^10.0.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oozcitak/url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-3.0.0.tgz", + "integrity": "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==", + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "^2.0.2", + "@oozcitak/util": "^10.0.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oozcitak/util": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-10.0.0.tgz", + "integrity": "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==", + "license": "MIT", + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz", + "integrity": "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", + "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.14.tgz", + "integrity": "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.14.tgz", + "integrity": "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.14.tgz", + "integrity": "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.14.tgz", + "integrity": "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.14.tgz", + "integrity": "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.14.tgz", + "integrity": "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.14.tgz", + "integrity": "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.14.tgz", + "integrity": "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.14.tgz", + "integrity": "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.14.tgz", + "integrity": "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.14.tgz", + "integrity": "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.14.tgz", + "integrity": "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.14.tgz", + "integrity": "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.14.tgz", + "integrity": "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.14.tgz", + "integrity": "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/history": { + "version": "1.162.5", + "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.162.5.tgz", + "integrity": "sha512-w1+YvwmTkwt5n8yUqEH3IWZ9DsmorCZW/EQ3KYHnqEgIkOZpH54NvW6/Epq3Dzb8H0ifIeYX8PExvrNLIH5FIA==", + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-router": { + "version": "1.170.45", + "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.170.45.tgz", + "integrity": "sha512-6tuiIAFb82zsk/YxveJRyGjbsm6kIfgUuoH7qCp2R2aSHveW6W0t/6j+Sn4m1iIlA2lLU4cvooqks08/YMOThw==", + "license": "MIT", + "dependencies": { + "@tanstack/history": "1.162.5", + "@tanstack/react-store": "^0.11.2", + "@tanstack/router-core": "1.171.38", + "isbot": "^5.1.22" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + } + }, + "node_modules/@tanstack/react-start": { + "version": "1.169.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-start/-/react-start-1.169.3.tgz", + "integrity": "sha512-tujqzu41T6c74zMj9c8rUpNOA5MExSHyLxCoE/m7QpfDLzTrloD5TUhk/9Z97c8AM91oFGdsQYN4JC5iUxl6Ow==", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.45", + "@tanstack/react-start-client": "1.168.43", + "@tanstack/react-start-rsc": "0.1.64", + "@tanstack/react-start-server": "1.167.51", + "@tanstack/router-utils": "1.162.4", + "@tanstack/start-client-core": "1.170.38", + "@tanstack/start-plugin-core": "1.171.54", + "@tanstack/start-server-core": "1.169.44", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rsbuild/core": "^2.0.0", + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0", + "vite": ">=8.0.0" + }, + "peerDependenciesMeta": { + "@rsbuild/core": { + "optional": true + }, + "@vitejs/plugin-rsc": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-start-client": { + "version": "1.168.43", + "resolved": "https://registry.npmjs.org/@tanstack/react-start-client/-/react-start-client-1.168.43.tgz", + "integrity": "sha512-XolxHGl5d0uLxVZ4A03iG+dL/xyAFwqbZYXque9iKftN/o/bbQ4ejjgc6yo3+FXb7LBcdjlWLyzy92r1vHFBog==", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.45", + "@tanstack/router-core": "1.171.38", + "@tanstack/start-client-core": "1.170.38" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + } + }, + "node_modules/@tanstack/react-start-server": { + "version": "1.167.51", + "resolved": "https://registry.npmjs.org/@tanstack/react-start-server/-/react-start-server-1.167.51.tgz", + "integrity": "sha512-vITebNqXC3ievzYQr3b4z2jZWpEo95oYPfXP1kBi5OudSbcp2pN2A2/pzDsoJ/ei3oZ2rjEEbnOVrbDSHiO+HA==", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.45", + "@tanstack/router-core": "1.171.38", + "@tanstack/start-server-core": "1.169.44" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + } + }, + "node_modules/@tanstack/react-start/node_modules/@tanstack/react-start-rsc": { + "version": "0.1.64", + "resolved": "https://registry.npmjs.org/@tanstack/react-start-rsc/-/react-start-rsc-0.1.64.tgz", + "integrity": "sha512-pxiz1O9c1HAo2BwzBuMUjkutKeDYlDlJHOzYrNaj6w4mwokOdN1b9B0myzDxNhnS9Ign0zU12vP9JVr20DUDSQ==", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.45", + "@tanstack/router-core": "1.171.38", + "@tanstack/router-utils": "1.162.4", + "@tanstack/start-client-core": "1.170.38", + "@tanstack/start-fn-stubs": "1.162.0", + "@tanstack/start-plugin-core": "1.171.54", + "@tanstack/start-storage-context": "1.167.40", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rspack/core": ">=2.0.0-0", + "@vitejs/plugin-rsc": ">=0.5.30", + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0", + "react-server-dom-rspack": ">=0.0.2" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "@vitejs/plugin-rsc": { + "optional": true + }, + "react-server-dom-rspack": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-store": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.11.2.tgz", + "integrity": "sha512-oj5O5DmiMWE+ih+SioX2ayC1kbK7d3fpLx0B75fPJr9nY3zU+hoW010yr64qXsY/9fyiLVE2bn5mGNV3owm8RQ==", + "license": "MIT", + "dependencies": { + "@tanstack/store": "0.11.2", + "use-sync-external-store": "^1.6.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/router-core": { + "version": "1.171.38", + "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.171.38.tgz", + "integrity": "sha512-XRZVxiO6BIexICmEo3XfPmpoyyLhbjgaMJsONz4gIf5oYys0UiaG1BJGRyj9x8QZPDZcBhvAMuTYN70wSxV0gA==", + "license": "MIT", + "dependencies": { + "@tanstack/history": "1.162.5", + "cookie-es": "^3.0.0", + "seroval": "^1.6.7", + "seroval-plugins": "^1.6.7" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/router-generator": { + "version": "1.167.45", + "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.167.45.tgz", + "integrity": "sha512-oCtNqtsMNcBJkPIOu1O7a68jnxt4TkVJj3jHrW3VgiZdBeaj/lBJQh50yvzmNRswRcyIkRg0g30g25bbLRrAGw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8", + "@tanstack/router-core": "1.171.38", + "@tanstack/router-utils": "1.162.4", + "@tanstack/virtual-file-routes": "1.162.0", + "jiti": "^2.7.0", + "magic-string": "^0.30.21", + "prettier": "^3.9.6", + "zod": "^4.5.4" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/router-plugin": { + "version": "1.168.47", + "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.168.47.tgz", + "integrity": "sha512-1qvMto2Id0Uulux4qd+WttCbOFloIEQ5x82m68T4knt6NmUDQC66P/u/5yzN0FWGSO0u2U2nbDSIQoR6g7HNmQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "@tanstack/router-core": "1.171.38", + "@tanstack/router-generator": "1.167.45", + "@tanstack/router-utils": "1.162.4", + "chokidar": "^5.0.0", + "unplugin": "^3.3.0", + "zod": "^4.5.4" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rsbuild/core": ">=1.0.2 || ^2.0.0", + "@tanstack/react-router": "^1.170.45", + "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", + "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", + "webpack": ">=5.92.0" + }, + "peerDependenciesMeta": { + "@rsbuild/core": { + "optional": true + }, + "@tanstack/react-router": { + "optional": true + }, + "vite": { + "optional": true + }, + "vite-plugin-solid": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@tanstack/router-utils": { + "version": "1.162.4", + "resolved": "https://registry.npmjs.org/@tanstack/router-utils/-/router-utils-1.162.4.tgz", + "integrity": "sha512-3TSH3rwAQTqQOtTBF8IXMFXmGNJWnjwIm+l87uguyC/of0pSRKcU9GJrozaf0fY9it8dLQvUZJA2MOlfqbW1sA==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.29.8", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "ansis": "^4.3.1", + "babel-dead-code-elimination": "^1.0.12", + "diff": "^9.0.0", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.17" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-client-core": { + "version": "1.170.38", + "resolved": "https://registry.npmjs.org/@tanstack/start-client-core/-/start-client-core-1.170.38.tgz", + "integrity": "sha512-XK+HVhXz9Fs7bJAYmsdLccq3lc/85Z83oPt7He7b3BMX6Ejw2/N9JzUeqwcxXtd29SPBRFb5VRMtZf3uu4Tlag==", + "license": "MIT", + "dependencies": { + "@tanstack/router-core": "1.171.38", + "@tanstack/start-fn-stubs": "1.162.0", + "@tanstack/start-storage-context": "1.167.40", + "seroval": "^1.6.7" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-fn-stubs": { + "version": "1.162.0", + "resolved": "https://registry.npmjs.org/@tanstack/start-fn-stubs/-/start-fn-stubs-1.162.0.tgz", + "integrity": "sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-plugin-core": { + "version": "1.171.54", + "resolved": "https://registry.npmjs.org/@tanstack/start-plugin-core/-/start-plugin-core-1.171.54.tgz", + "integrity": "sha512-EQJDF1HC1tIdz0pNey+GI0VWci4GU4zGKM4jTzwbNMQmoqlilFhkNwgrPgUP4TBRtkBbgpuuGlrafChzj66A3w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.29.7", + "@babel/core": "^7.29.7", + "@babel/types": "^7.29.8", + "@jridgewell/remapping": "^2.3.5", + "@tanstack/router-core": "1.171.38", + "@tanstack/router-generator": "1.167.45", + "@tanstack/router-plugin": "1.168.47", + "@tanstack/router-utils": "1.162.4", + "@tanstack/start-server-core": "1.169.44", + "exsolve": "^1.1.1", + "lightningcss": "^1.33.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.7", + "seroval": "^1.6.7", + "source-map": "^0.7.6", + "srvx": "^1.0.0", + "tinyglobby": "^0.2.17", + "ufo": "^1.6.4", + "vitefu": "^1.1.3", + "xmlbuilder2": "^4.0.3", + "zod": "^4.5.4" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rsbuild/core": "^2.0.0", + "vite": ">=8.0.0" + }, + "peerDependenciesMeta": { + "@rsbuild/core": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tanstack/start-server-core": { + "version": "1.169.44", + "resolved": "https://registry.npmjs.org/@tanstack/start-server-core/-/start-server-core-1.169.44.tgz", + "integrity": "sha512-xgZfItp1af0zBbR326R54ageMeQwnrZgRrapAc3sO9uMgANdy1rpxwvSPqT5+0T6HQbFQavRPwxydWSxT7Rr6g==", + "license": "MIT", + "dependencies": { + "@tanstack/history": "1.162.5", + "@tanstack/router-core": "1.171.38", + "@tanstack/start-client-core": "1.170.38", + "@tanstack/start-storage-context": "1.167.40", + "cookie-es": "^3.0.0", + "fetchdts": "^0.2.0", + "seroval": "^1.6.7" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-storage-context": { + "version": "1.167.40", + "resolved": "https://registry.npmjs.org/@tanstack/start-storage-context/-/start-storage-context-1.167.40.tgz", + "integrity": "sha512-xolDIZPyOiAk3XE5m4UICArqwkRNB6z3nr6aAKJphvCM8nzHKlu/laJ+3WtfWFb2iPB9B3K6R/X+fKSoz4OpVA==", + "license": "MIT", + "dependencies": { + "@tanstack/router-core": "1.171.38" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/store": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.11.2.tgz", + "integrity": "sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/virtual-file-routes": { + "version": "1.162.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-file-routes/-/virtual-file-routes-1.162.0.tgz", + "integrity": "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==", + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz", + "integrity": "sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/type-utils": "8.72.0", + "@typescript-eslint/utils": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.72.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", + "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.0.tgz", + "integrity": "sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.0.tgz", + "integrity": "sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.72.0", + "@typescript-eslint/types": "^8.72.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz", + "integrity": "sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz", + "integrity": "sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz", + "integrity": "sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/utils": "8.72.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.0.tgz", + "integrity": "sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz", + "integrity": "sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.72.0", + "@typescript-eslint/tsconfig-utils": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.0.tgz", + "integrity": "sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz", + "integrity": "sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", + "integrity": "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.152.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.12.tgz", + "integrity": "sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.12", + "@vitest/utils": "4.1.12", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.12.tgz", + "integrity": "sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.12", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.12.tgz", + "integrity": "sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.12.tgz", + "integrity": "sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.12", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.12.tgz", + "integrity": "sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.12", + "@vitest/utils": "4.1.12", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.12.tgz", + "integrity": "sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.12.tgz", + "integrity": "sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.12", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.4.0.tgz", + "integrity": "sha512-9k3v7xcHwgdO/DruxGIg4HtjvlAZlcnsX/mzqUb1t3NkYnl9kK2UJ+Gq0io+vQf7iT//BD/HB/NBkUR1LWxoeA==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.2.0.tgz", + "integrity": "sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "es-shim-unscopables": "^1.1.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.14.0.tgz", + "integrity": "sha512-9WTZxEjsZ7b13TH8JPmbV2z8CHbl80/2hm3XPEG4JgNdQLK81IBRXmSxHfMAOkSqQeRxT/0dwNDz2GOm3zzpcQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-dead-code-elimination": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz", + "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.30.tgz", + "integrity": "sha512-YJs6Pz17RMNv8HoyKREtCY38E3BV1nPkWVmIb+XN1uHC1Vqt3KZCH/uJH9ZKRoiKb/icZ6tvehyee6RZNbFfRA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.29.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz", + "integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.29", + "caniuse-lite": "^1.0.30001816", + "electron-to-chromium": "^1.5.459", + "node-releases": "^2.0.59", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001817", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001817.tgz", + "integrity": "sha512-uZ1JG80BwhtMalIAGPxPfmNhHyd/B/nTdr3hITWlRamSyAdUStP1Gop01e7SD5q6J90ixJvWDbvmiFVfDgn4bw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cookie-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz", + "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.462", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.462.tgz", + "integrity": "sha512-9qpCBlXPy5v7n1Q7WTO5rW2StI4WNQhl96HpgXRZLPlNoXAD0LbsQUflovQYAO44Yia073ISusqohncvGSMbGA==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exsolve": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.3.tgz", + "integrity": "sha512-kuzL9zherLQoqDeQaS55AiiIg1kb4KYXaSusTtgZTPlvKP2gGCI82LNmBNh5RjDVUXIcmgx+/Mx7vPwRzBbhJA==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetchdts": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fetchdts/-/fetchdts-0.2.0.tgz", + "integrity": "sha512-PJb4ZTGT2/wK78RmVrxZITStWabhZlKBsL52VOgq+c7clEkdHdEB+0rtzrXCbZJqeYJj+IwNWYqB5/G6vouA6A==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/framer-motion": { + "version": "12.34.5", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.5.tgz", + "integrity": "sha512-Z2dQ+o7BsfpJI3+u0SQUNCrN+ajCKJen1blC4rCHx1Ta2EOHs+xKJegLT2aaD9iSMbU3OoX+WabQXkloUbZmJQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.34.5", + "motion-utils": "^12.29.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", + "integrity": "sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isbot": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.2.2.tgz", + "integrity": "sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==", + "license": "Unlicense", + "engines": { + "node": ">=18" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.576.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.576.0.tgz", + "integrity": "sha512-koNxU14BXrxUfZQ9cUaP0ES1uyPZKYDjk31FQZB6dQ/x+tXk979sVAn9ppZ/pVeJJyOxVM8j1E+8QEuSc02Vug==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/motion": { + "version": "12.35.1", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.35.1.tgz", + "integrity": "sha512-yEt/49kWC0VU/IEduDfeZw82eDemlPwa1cyo/gcEEUCN4WgpSJpUcxz6BUwakGabvJiTzLQ58J73515I5tfykQ==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.35.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz", + "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, + "node_modules/motion/node_modules/framer-motion": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz", + "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.43.0", + "motion-utils": "^12.39.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.59", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.59.tgz", + "integrity": "sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/openai": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.27.0.tgz", + "integrity": "sha512-osTKySlrdYrLYTt0zjhY8yp0JUBmWDCN+Q+QxsV4xMQnnoVFpylgKGgxwN8sSdTNw0G4y+WUXs4eCMWpyDNWZQ==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "devOptional": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz", + "integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.14.tgz", + "integrity": "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.154.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.14", + "@rolldown/binding-android-arm64": "1.2.14", + "@rolldown/binding-darwin-arm64": "1.2.14", + "@rolldown/binding-darwin-x64": "1.2.14", + "@rolldown/binding-freebsd-x64": "1.2.14", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.14", + "@rolldown/binding-linux-arm64-gnu": "1.2.14", + "@rolldown/binding-linux-arm64-musl": "1.2.14", + "@rolldown/binding-linux-ppc64-gnu": "1.2.14", + "@rolldown/binding-linux-s390x-gnu": "1.2.14", + "@rolldown/binding-linux-x64-gnu": "1.2.14", + "@rolldown/binding-linux-x64-musl": "1.2.14", + "@rolldown/binding-openharmony-arm64": "1.2.14", + "@rolldown/binding-win32-arm64-msvc": "1.2.14", + "@rolldown/binding-win32-x64-msvc": "1.2.14" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.6.11.tgz", + "integrity": "sha512-hpb6XnMCGN/A+79Uf8V7/iTGkyyyazNn5X0CoCAp7Uy9vT3RT8jZ9GLcnxGoVjg3P8AArbe4ZVHq6C8oT2a0kA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.6.11.tgz", + "integrity": "sha512-LmB3nd8pL39jKHKvnpTUjcjBtNwR8MLty/ydAR8qbfXt5j6/6KqR1atuw8cVmD2/lDxAkvAwq5tI/HsisD9Fqg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/srvx": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/srvx/-/srvx-1.0.6.tgz", + "integrity": "sha512-QeTKgAhk8OSsfeq60bl3wuVs9PmBftzmk7owBInA/F1YQne+TRw0UcPymNpURldAmx8hfmM2PNNGH/zgo3GpLA==", + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.5.tgz", + "integrity": "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.72.0.tgz", + "integrity": "sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.72.0", + "@typescript-eslint/parser": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/utils": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unplugin": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.4.0.tgz", + "integrity": "sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.7", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rsbuild/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rsbuild/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.12.tgz", + "integrity": "sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.12", + "@vitest/mocker": "4.1.12", + "@vitest/pretty-format": "4.1.12", + "@vitest/runner": "4.1.12", + "@vitest/snapshot": "4.1.12", + "@vitest/spy": "4.1.12", + "@vitest/utils": "4.1.12", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.12", + "@vitest/browser-preview": "4.1.12", + "@vitest/browser-webdriverio": "^4.1.11", + "@vitest/coverage-istanbul": "4.1.12", + "@vitest/coverage-v8": "4.1.12", + "@vitest/ui": "4.1.12", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.24.tgz", + "integrity": "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xmlbuilder2": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz", + "integrity": "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==", + "license": "MIT", + "dependencies": { + "@oozcitak/dom": "^2.0.2", + "@oozcitak/infra": "^2.0.2", + "@oozcitak/util": "^10.0.0", + "js-yaml": "^4.1.1" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } - } } diff --git a/package.json b/package.json index 8228b32..e050f10 100644 --- a/package.json +++ b/package.json @@ -1,52 +1,54 @@ { - "name": "zns-roadmap-studio", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint .", - "typecheck": "next typegen && node scripts/ensure-next-route-shims.mjs && tsc --noEmit", - "test": "vitest run", - "test:watch": "vitest", - "test:ui": "vitest --ui" - }, - "dependencies": { - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-switch": "^1.2.6", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "framer-motion": "^12.34.5", - "geist": "^1.7.0", - "lucide-react": "^0.576.0", - "motion": "^12.35.1", - "next": "^16.1.6", - "next-themes": "^0.4.6", - "openai": "^6.27.0", - "react": "^19.2.4", - "react-dom": "^19.2.4", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.0.0", - "@types/node": "^22.0.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "@vitest/ui": "^2.1.8", - "axe-core": "^4.11.1", - "chalk": "^5.6.2", - "eslint": "^9.0.0", - "eslint-config-next": "^15.1.0", - "fast-check": "^4.6.0", - "playwright": "^1.58.2", - "postcss": "^8.5.0", - "tailwindcss": "^4.0.0", - "typescript": "^5.7.0", - "vitest": "^2.1.8" - } + "name": "syllo", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "start": "node server.mjs", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@fontsource-variable/geist": "5.3.0", + "@fontsource-variable/geist-mono": "5.3.0", + "@radix-ui/react-alert-dialog": "1.1.15", + "@radix-ui/react-select": "2.2.6", + "@radix-ui/react-separator": "1.1.8", + "@radix-ui/react-slot": "1.2.4", + "@radix-ui/react-switch": "1.2.6", + "@tanstack/react-router": "1.170.45", + "@tanstack/react-start": "1.169.3", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "framer-motion": "12.34.5", + "lucide-react": "0.576.0", + "motion": "12.35.1", + "openai": "6.27.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "sonner": "2.0.7", + "tailwind-merge": "3.5.0" + }, + "devDependencies": { + "@eslint/js": "9.39.5", + "@tailwindcss/vite": "4.3.3", + "@tanstack/router-plugin": "1.168.47", + "@types/node": "24.19.2", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "@vitejs/plugin-react": "6.1.2", + "eslint": "9.39.5", + "eslint-plugin-jsx-a11y": "6.10.2", + "eslint-plugin-react-hooks": "7.1.1", + "globals": "17.13.0", + "tailwindcss": "4.3.3", + "typescript": "5.9.3", + "typescript-eslint": "8.72.0", + "vite": "8.3.4", + "vitest": "4.1.12" + } } diff --git a/postcss.config.mjs b/postcss.config.mjs deleted file mode 100644 index 148a80b..0000000 --- a/postcss.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -const config = { - plugins: { - "@tailwindcss/postcss": {}, - }, -}; - -export default config; diff --git a/proxy.ts b/proxy.ts deleted file mode 100644 index a041764..0000000 --- a/proxy.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { NextResponse, type NextRequest } from "next/server"; -import { GATE_COOKIE, GATE_COOKIE_VALUE } from "@/lib/access-gate"; - -export async function proxy(request: NextRequest) { - const { pathname } = request.nextUrl; - - // The unlock API must always be reachable. - if (pathname === "/api/gate") { - return NextResponse.next(); - } - - const isUnlocked = - request.cookies.get(GATE_COOKIE)?.value === GATE_COOKIE_VALUE; - - // Unlocked visitors pass through normally. - if (isUnlocked) { - return NextResponse.next(); - } - - // The gate page itself and any of its assets render without the cookie. - if (pathname === "/gate" || pathname.startsWith("/_next/")) { - return NextResponse.next(); - } - - // Everyone else is redirected to the gate, remembering where they came from. - const gateUrl = new URL("/gate", request.url); - const destination = pathname + request.nextUrl.search; - if (destination !== "/") { - gateUrl.searchParams.set("next", destination); - } - return NextResponse.redirect(gateUrl); -} - -export const config = { - matcher: [ - "/((?!_next/static|_next/image|favicon.ico|manifest.json|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", - ], -}; diff --git a/scripts/ensure-next-route-shims.mjs b/scripts/ensure-next-route-shims.mjs deleted file mode 100644 index 98fe833..0000000 --- a/scripts/ensure-next-route-shims.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import { existsSync, mkdirSync, writeFileSync } from "node:fs"; -import { dirname, join } from "node:path"; - -const routeTypeDirs = [".next/types", ".next/dev/types"]; -const shimContents = 'export type * from "./routes";\n'; - -for (const dir of routeTypeDirs) { - const source = join(dir, "routes.d.ts"); - const target = join(dir, "routes.js.d.ts"); - - if (!existsSync(source)) { - continue; - } - - mkdirSync(dirname(target), { recursive: true }); - writeFileSync(target, shimContents, "utf8"); -} diff --git a/scripts/render_logo.js b/scripts/render_logo.js deleted file mode 100644 index 5e3ce5c..0000000 --- a/scripts/render_logo.js +++ /dev/null @@ -1,48 +0,0 @@ -const { chromium } = require('playwright'); -const fs = require('fs'); -const path = require('path'); - -const svgContent = ` - - - - - - - - - - - -`; - -async function render() { - const browser = await chromium.launch(); - const page = await browser.newPage(); - - await page.setContent(` - - ${svgContent} - `); - - const outputPath = path.join(__dirname, '..', 'public', 'brand-assets', 'exact-logo-render.png'); - await page.locator('svg').screenshot({ path: outputPath }); - - console.log(`Saved exact rendering to: ${outputPath}`); - await browser.close(); -} - -render().catch(console.error); diff --git a/tmp_fix.js b/tmp_fix.js deleted file mode 100644 index 70e41a6..0000000 --- a/tmp_fix.js +++ /dev/null @@ -1,103 +0,0 @@ -const fs = require('fs'); -const file = 'c:/Users/Screen/Documents/Roadmap-maker/components/workspace/WorkspaceShell.tsx'; -let content = fs.readFileSync(file, 'utf8'); - -content = content.replace( - 'import SectionRenderer from "@/components/workspace/SectionRenderer";', - 'import SectionRenderer from "@/components/workspace/SectionRenderer";\nimport WorkspaceSplitView from "@/components/workspace/WorkspaceSplitView";' -); - -const searchStr = ` ) : activeSection ? ( -
- onUpdateSection?.(activeSection.id, updater)} - onApiError={onApiError} - /> -
- ) : null}`; - -// normalize line endings to find it -const normalizedContent = content.replace(/\r\n/g, '\n'); -const normalizedSearchStr = searchStr.replace(/\r\n/g, '\n'); - -if (normalizedContent.includes(normalizedSearchStr)) { - const replaceStr = ` ) : activeSection?.type === "module" || activeSection?.type === "milestones" ? ( -
- { - onUpdateSection?.(moduleId, (s) => { - const ms = s; - return { - ...ms, - data: { - ...ms.data, - tasks: (ms.data.tasks || []).map((t) => - t.id === taskId - ? { - ...t, - subtasks: (t.subtasks || []).map((st) => - st.id === subtaskId ? { ...st, completed: !st.completed } : st - ), - } - : t - ), - }, - }; - }); - }} - onUpdateTask={(moduleId, taskId) => { - onUpdateSection?.(moduleId, (s) => { - const ms = s; - return { - ...ms, - data: { - ...ms.data, - tasks: (ms.data.tasks || []).map((t) => - t.id === taskId ? { ...t, completed: !t.completed } : t - ), - }, - }; - }); - }} - onUpdateModule={(moduleId) => { - onUpdateSection?.(moduleId, (s) => { - const ms = s; - const isCompleted = !ms.data.completed; - return { - ...ms, - data: { - ...ms.data, - completed: isCompleted, - tasks: isCompleted ? (ms.data.tasks || []).map((t) => ({ - ...t, - completed: true, - subtasks: (t.subtasks || []).map((st) => ({ ...st, completed: true })) - })) : ms.data.tasks - }, - }; - }); - }} - /> -
- ) : activeSection ? ( -
- onUpdateSection?.(activeSection.id, updater)} - onApiError={onApiError} - /> -
- ) : null}`; - - // Just replace in normalized content and write it back - const newContent = normalizedContent.replace(normalizedSearchStr, replaceStr); - fs.writeFileSync(file, newContent, 'utf8'); - console.log('Successfully replaced content.'); -} else { - console.log('Could not find target content to replace.'); -} diff --git a/tsconfig.json b/tsconfig.json index b63362c..a5c8bed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2017", + "target": "ES2022", "lib": [ "dom", "dom.iterable", @@ -17,26 +17,21 @@ "isolatedModules": true, "jsx": "react-jsx", "incremental": true, - "types": ["vitest/globals"], - "plugins": [ - { - "name": "next" - } - ], + "types": ["vitest/globals", "vite/client"], "paths": { "@/*": [ - "./*" + "./src/*" ] } }, "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts" + "src/**/*.ts", + "src/**/*.tsx", + "vite.config.ts", + "vitest.config.ts" ], "exclude": [ - "node_modules" + "node_modules", + "dist" ] } diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..6b9780a --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,26 @@ +import { fileURLToPath } from "node:url"; +import tailwindcss from "@tailwindcss/vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import viteReact from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + server: { + port: 3000, + }, + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, + plugins: [ + tailwindcss(), + tanstackStart({ + srcDirectory: "src", + router: { + routesDirectory: "routes", + }, + }), + viteReact(), + ], +}); diff --git a/vitest.config.ts b/vitest.config.ts index 7266a04..822dab9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from 'vitest/config'; -import { resolve } from 'path'; +import { resolve } from 'node:path'; export default defineConfig({ test: { @@ -19,7 +19,7 @@ export default defineConfig({ }, resolve: { alias: { - '@': resolve(__dirname, './'), + '@': resolve(import.meta.dirname, './src'), }, }, }); From 91611af786690908cfdadd29c97d849a31cdd2ee Mon Sep 17 00:00:00 2001 From: Screen <228304862+screen-gd@users.noreply.github.com> Date: Wed, 21 Oct 2026 03:14:59 +0400 Subject: [PATCH 02/20] Move shared source into src/ and swap Next navigation for TanStack Router Components, hooks, lib, types and the stylesheet now live under src/ so the Vite root and the "@/*" alias have a single source directory. File contents change only where they touched Next.js APIs. - components/hooks/lib/types: relocated with git mv so history follows - router-nav.ts: usePathname/useSearchParams adapters over TanStack Router state, so call sites keep their shape - next/link -> @tanstack/react-router Link, next/navigation -> the adapters - next/image usages were already plain elements; drop the stale @next/next disable comments that no longer refer to an installed plugin - app-url.ts: VITE_APP_URL-backed public origin helper - globals.css: import the self-hosted Fontsource Geist families and keep the "--font-geist-*" variables the theme tokens already referenced - roadmap-intake: read the body from a standard Request instead of NextRequest --- .../components}/credits/CreditBadge.tsx | 0 .../components}/dashboard/AtRiskAlerts.tsx | 0 .../components}/dashboard/CoachingLog.tsx | 0 .../dashboard/ModuleCompletionTable.tsx | 4 +-- .../components}/dashboard/SkillHeatmap.tsx | 0 .../components}/dashboard/VelocityChart.tsx | 0 .../components}/gallery/GalleryCard.tsx | 9 ++++--- .../components}/gallery/GalleryFilters.tsx | 8 +++--- .../components}/gallery/GalleryGrid.tsx | 0 .../components}/layout/Footer.tsx | 4 +-- .../components}/layout/Header.tsx | 12 ++++----- .../components}/layout/LoadingExperience.tsx | 0 .../components}/layout/MarketingHeroShell.tsx | 0 .../components}/layout/StudioSidebar.tsx | 8 +++--- .../components}/seo/FaqSchema.tsx | 0 .../components}/seo/GallerySchema.tsx | 0 .../components}/seo/HowToSchema.tsx | 0 .../components}/seo/WebAppSchema.tsx | 0 .../components}/shared/ContentViewer.tsx | 0 .../components}/shared/EmptyState.tsx | 0 .../components}/shared/Logo.tsx | 0 .../components}/shared/Modal.tsx | 0 .../components}/shared/ProgressRing.tsx | 0 .../components}/shared/Providers.tsx | 0 .../components}/shared/SmartEmbed.tsx | 1 - .../components}/shared/StatusBadge.tsx | 0 .../components}/shared/ThemeToggle.tsx | 0 .../components}/shared/VideoPlayer.tsx | 0 .../components}/sharing/ViralBadge.tsx | 4 +-- .../components}/ui/alert-dialog.tsx | 0 {components => src/components}/ui/button.tsx | 0 {components => src/components}/ui/card.tsx | 0 {components => src/components}/ui/input.tsx | 0 {components => src/components}/ui/select.tsx | 0 .../components}/ui/separator.tsx | 0 {components => src/components}/ui/switch.tsx | 0 .../components}/ui/textarea.tsx | 0 .../components}/workspace/ContextPanel.tsx | 0 .../components}/workspace/SectionRenderer.tsx | 0 .../components}/workspace/SettingsModal.tsx | 0 .../components}/workspace/ShareEmbedModal.tsx | 0 .../components}/workspace/SrsReviewModal.tsx | 0 .../components}/workspace/WorkspaceShell.tsx | 16 ++++++------ .../workspace/WorkspaceSplitView.tsx | 0 .../workspace/sections/CalendarSection.tsx | 0 .../workspace/sections/CustomSection.tsx | 0 .../workspace/sections/GlossarySection.tsx | 0 .../workspace/sections/MilestonesSection.tsx | 0 .../workspace/sections/ModuleSection.tsx | 0 .../workspace/sections/NotesSection.tsx | 0 .../workspace/sections/ProgressSection.tsx | 0 .../workspace/sections/ResourcesSection.tsx | 0 .../workspace/sections/SubmissionsSection.tsx | 0 .../workspace/sections/TaskListSection.tsx | 0 .../workspace/sections/VideosSection.tsx | 1 - .../workspaces/WorkspaceLibraryCard.tsx | 4 +-- {hooks => src/hooks}/useAchievements.ts | 0 {hooks => src/hooks}/useProgress.ts | 0 {hooks => src/hooks}/useRoadmap.ts | 0 {hooks => src/hooks}/useStreak.ts | 0 {hooks => src/hooks}/useTimeTracker.ts | 0 .../lib}/__tests__/learning-session.test.ts | 0 {lib => src/lib}/__tests__/navigation.test.ts | 0 .../lib}/__tests__/roadmap-intake.test.ts | 0 .../__tests__/workspace-persistence.test.ts | 0 {lib => src/lib}/accent-theme.ts | 0 {lib => src/lib}/access-gate.ts | 0 {lib => src/lib}/ai-config.ts | 0 {lib => src/lib}/aiClient.ts | 0 {lib => src/lib}/analytics.ts | 0 src/lib/app-url.ts | 9 +++++++ {lib => src/lib}/billing.ts | 0 {lib => src/lib}/constants.ts | 0 {lib => src/lib}/credits.ts | 0 {lib => src/lib}/export.ts | 0 {lib => src/lib}/gallery.ts | 0 {lib => src/lib}/learning-session.ts | 0 {lib => src/lib}/navigation.ts | 0 {lib => src/lib}/openai.ts | 0 {lib => src/lib}/plans.ts | 0 src/lib/router-nav.ts | 15 +++++++++++ {lib => src/lib}/server/ai.ts | 0 {lib => src/lib}/server/auth.ts | 0 {lib => src/lib}/server/credits.ts | 0 {lib => src/lib}/server/dashboard.ts | 0 {lib => src/lib}/server/gallery.ts | 0 {lib => src/lib}/server/quiz.ts | 0 {lib => src/lib}/server/roadmap-intake.ts | 25 +++++++++---------- {lib => src/lib}/server/settings.ts | 0 {lib => src/lib}/server/srs.ts | 0 {lib => src/lib}/server/store.ts | 0 {lib => src/lib}/server/workspaces.ts | 0 {lib => src/lib}/spaced-repetition.ts | 0 {lib => src/lib}/srs.ts | 0 {lib => src/lib}/storage.ts | 0 {lib => src/lib}/submissions-api.ts | 0 {lib => src/lib}/userConfig.ts | 0 {lib => src/lib}/utils.ts | 0 {lib => src/lib}/versioning.ts | 0 {lib => src/lib}/video-utils.ts | 0 {lib => src/lib}/workspace-persistence.ts | 0 {lib => src/lib}/workspace-stats.ts | 0 {app => src/styles}/globals.css | 12 +++++++++ {types => src/types}/index.ts | 0 104 files changed, 82 insertions(+), 50 deletions(-) rename {components => src/components}/credits/CreditBadge.tsx (100%) rename {components => src/components}/dashboard/AtRiskAlerts.tsx (100%) rename {components => src/components}/dashboard/CoachingLog.tsx (100%) rename {components => src/components}/dashboard/ModuleCompletionTable.tsx (96%) rename {components => src/components}/dashboard/SkillHeatmap.tsx (100%) rename {components => src/components}/dashboard/VelocityChart.tsx (100%) rename {components => src/components}/gallery/GalleryCard.tsx (92%) rename {components => src/components}/gallery/GalleryFilters.tsx (95%) rename {components => src/components}/gallery/GalleryGrid.tsx (100%) rename {components => src/components}/layout/Footer.tsx (95%) rename {components => src/components}/layout/Header.tsx (89%) rename {components => src/components}/layout/LoadingExperience.tsx (100%) rename {components => src/components}/layout/MarketingHeroShell.tsx (100%) rename {components => src/components}/layout/StudioSidebar.tsx (96%) rename {components => src/components}/seo/FaqSchema.tsx (100%) rename {components => src/components}/seo/GallerySchema.tsx (100%) rename {components => src/components}/seo/HowToSchema.tsx (100%) rename {components => src/components}/seo/WebAppSchema.tsx (100%) rename {components => src/components}/shared/ContentViewer.tsx (100%) rename {components => src/components}/shared/EmptyState.tsx (100%) rename {components => src/components}/shared/Logo.tsx (100%) rename {components => src/components}/shared/Modal.tsx (100%) rename {components => src/components}/shared/ProgressRing.tsx (100%) rename {components => src/components}/shared/Providers.tsx (100%) rename {components => src/components}/shared/SmartEmbed.tsx (98%) rename {components => src/components}/shared/StatusBadge.tsx (100%) rename {components => src/components}/shared/ThemeToggle.tsx (100%) rename {components => src/components}/shared/VideoPlayer.tsx (100%) rename {components => src/components}/sharing/ViralBadge.tsx (81%) rename {components => src/components}/ui/alert-dialog.tsx (100%) rename {components => src/components}/ui/button.tsx (100%) rename {components => src/components}/ui/card.tsx (100%) rename {components => src/components}/ui/input.tsx (100%) rename {components => src/components}/ui/select.tsx (100%) rename {components => src/components}/ui/separator.tsx (100%) rename {components => src/components}/ui/switch.tsx (100%) rename {components => src/components}/ui/textarea.tsx (100%) rename {components => src/components}/workspace/ContextPanel.tsx (100%) rename {components => src/components}/workspace/SectionRenderer.tsx (100%) rename {components => src/components}/workspace/SettingsModal.tsx (100%) rename {components => src/components}/workspace/ShareEmbedModal.tsx (100%) rename {components => src/components}/workspace/SrsReviewModal.tsx (100%) rename {components => src/components}/workspace/WorkspaceShell.tsx (98%) rename {components => src/components}/workspace/WorkspaceSplitView.tsx (100%) rename {components => src/components}/workspace/sections/CalendarSection.tsx (100%) rename {components => src/components}/workspace/sections/CustomSection.tsx (100%) rename {components => src/components}/workspace/sections/GlossarySection.tsx (100%) rename {components => src/components}/workspace/sections/MilestonesSection.tsx (100%) rename {components => src/components}/workspace/sections/ModuleSection.tsx (100%) rename {components => src/components}/workspace/sections/NotesSection.tsx (100%) rename {components => src/components}/workspace/sections/ProgressSection.tsx (100%) rename {components => src/components}/workspace/sections/ResourcesSection.tsx (100%) rename {components => src/components}/workspace/sections/SubmissionsSection.tsx (100%) rename {components => src/components}/workspace/sections/TaskListSection.tsx (100%) rename {components => src/components}/workspace/sections/VideosSection.tsx (98%) rename {components => src/components}/workspaces/WorkspaceLibraryCard.tsx (98%) rename {hooks => src/hooks}/useAchievements.ts (100%) rename {hooks => src/hooks}/useProgress.ts (100%) rename {hooks => src/hooks}/useRoadmap.ts (100%) rename {hooks => src/hooks}/useStreak.ts (100%) rename {hooks => src/hooks}/useTimeTracker.ts (100%) rename {lib => src/lib}/__tests__/learning-session.test.ts (100%) rename {lib => src/lib}/__tests__/navigation.test.ts (100%) rename {lib => src/lib}/__tests__/roadmap-intake.test.ts (100%) rename {lib => src/lib}/__tests__/workspace-persistence.test.ts (100%) rename {lib => src/lib}/accent-theme.ts (100%) rename {lib => src/lib}/access-gate.ts (100%) rename {lib => src/lib}/ai-config.ts (100%) rename {lib => src/lib}/aiClient.ts (100%) rename {lib => src/lib}/analytics.ts (100%) create mode 100644 src/lib/app-url.ts rename {lib => src/lib}/billing.ts (100%) rename {lib => src/lib}/constants.ts (100%) rename {lib => src/lib}/credits.ts (100%) rename {lib => src/lib}/export.ts (100%) rename {lib => src/lib}/gallery.ts (100%) rename {lib => src/lib}/learning-session.ts (100%) rename {lib => src/lib}/navigation.ts (100%) rename {lib => src/lib}/openai.ts (100%) rename {lib => src/lib}/plans.ts (100%) create mode 100644 src/lib/router-nav.ts rename {lib => src/lib}/server/ai.ts (100%) rename {lib => src/lib}/server/auth.ts (100%) rename {lib => src/lib}/server/credits.ts (100%) rename {lib => src/lib}/server/dashboard.ts (100%) rename {lib => src/lib}/server/gallery.ts (100%) rename {lib => src/lib}/server/quiz.ts (100%) rename {lib => src/lib}/server/roadmap-intake.ts (97%) rename {lib => src/lib}/server/settings.ts (100%) rename {lib => src/lib}/server/srs.ts (100%) rename {lib => src/lib}/server/store.ts (100%) rename {lib => src/lib}/server/workspaces.ts (100%) rename {lib => src/lib}/spaced-repetition.ts (100%) rename {lib => src/lib}/srs.ts (100%) rename {lib => src/lib}/storage.ts (100%) rename {lib => src/lib}/submissions-api.ts (100%) rename {lib => src/lib}/userConfig.ts (100%) rename {lib => src/lib}/utils.ts (100%) rename {lib => src/lib}/versioning.ts (100%) rename {lib => src/lib}/video-utils.ts (100%) rename {lib => src/lib}/workspace-persistence.ts (100%) rename {lib => src/lib}/workspace-stats.ts (100%) rename {app => src/styles}/globals.css (97%) rename {types => src/types}/index.ts (100%) diff --git a/components/credits/CreditBadge.tsx b/src/components/credits/CreditBadge.tsx similarity index 100% rename from components/credits/CreditBadge.tsx rename to src/components/credits/CreditBadge.tsx diff --git a/components/dashboard/AtRiskAlerts.tsx b/src/components/dashboard/AtRiskAlerts.tsx similarity index 100% rename from components/dashboard/AtRiskAlerts.tsx rename to src/components/dashboard/AtRiskAlerts.tsx diff --git a/components/dashboard/CoachingLog.tsx b/src/components/dashboard/CoachingLog.tsx similarity index 100% rename from components/dashboard/CoachingLog.tsx rename to src/components/dashboard/CoachingLog.tsx diff --git a/components/dashboard/ModuleCompletionTable.tsx b/src/components/dashboard/ModuleCompletionTable.tsx similarity index 96% rename from components/dashboard/ModuleCompletionTable.tsx rename to src/components/dashboard/ModuleCompletionTable.tsx index 70af42d..b9db928 100644 --- a/components/dashboard/ModuleCompletionTable.tsx +++ b/src/components/dashboard/ModuleCompletionTable.tsx @@ -1,4 +1,4 @@ -import Link from "next/link"; +import { Link } from "@tanstack/react-router"; import type { Roadmap } from "@/types"; import { getRoadmapCompletionRate, getRoadmapTaskTotals } from "@/lib/analytics"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; @@ -67,7 +67,7 @@ export default function ModuleCompletionTable({ roadmaps }: { roadmaps: Roadmap[ - + Open diff --git a/components/dashboard/SkillHeatmap.tsx b/src/components/dashboard/SkillHeatmap.tsx similarity index 100% rename from components/dashboard/SkillHeatmap.tsx rename to src/components/dashboard/SkillHeatmap.tsx diff --git a/components/dashboard/VelocityChart.tsx b/src/components/dashboard/VelocityChart.tsx similarity index 100% rename from components/dashboard/VelocityChart.tsx rename to src/components/dashboard/VelocityChart.tsx diff --git a/components/gallery/GalleryCard.tsx b/src/components/gallery/GalleryCard.tsx similarity index 92% rename from components/gallery/GalleryCard.tsx rename to src/components/gallery/GalleryCard.tsx index de89686..4d9c36a 100644 --- a/components/gallery/GalleryCard.tsx +++ b/src/components/gallery/GalleryCard.tsx @@ -1,10 +1,11 @@ "use client"; import { useState, useTransition } from "react"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useNavigate } from "@tanstack/react-router"; import { ArrowRight, CopyPlus } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { usePathname, useSearchParams } from "@/lib/router-nav"; import type { PublicRoadmapCard } from "@/types"; import { cn } from "@/lib/utils"; @@ -15,7 +16,7 @@ const difficultyTone: Record = { }; export default function GalleryCard({ item }: { item: PublicRoadmapCard }) { - const router = useRouter(); + const navigate = useNavigate(); const pathname = usePathname(); const searchParams = useSearchParams(); const [isPending, startTransition] = useTransition(); @@ -34,11 +35,11 @@ export default function GalleryCard({ item }: { item: PublicRoadmapCard }) { const payload = await response.json().catch(() => ({ success: false })); if (!response.ok || !payload.success || !payload.roadmap?.id) { const nextPath = `${pathname}${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`; - router.push(`/auth?next=${encodeURIComponent(nextPath)}`); + window.location.assign(`/auth?next=${encodeURIComponent(nextPath)}`); return; } - router.push(`/workspace/${payload.roadmap.id}`); + navigate({ to: "/workspace/$id", params: { id: payload.roadmap.id } }); } catch { setMessage("Sign in to continue."); } diff --git a/components/gallery/GalleryFilters.tsx b/src/components/gallery/GalleryFilters.tsx similarity index 95% rename from components/gallery/GalleryFilters.tsx rename to src/components/gallery/GalleryFilters.tsx index 8e6961d..eab02cd 100644 --- a/components/gallery/GalleryFilters.tsx +++ b/src/components/gallery/GalleryFilters.tsx @@ -1,8 +1,9 @@ "use client"; import { useEffect, useState, useTransition } from "react"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useNavigate } from "@tanstack/react-router"; import { Search } from "lucide-react"; +import { useSearchParams } from "@/lib/router-nav"; import { cn } from "@/lib/utils"; const modeOptions = [ @@ -21,8 +22,7 @@ const typeOptions = [ ]; export default function GalleryFilters() { - const router = useRouter(); - const pathname = usePathname(); + const navigate = useNavigate(); const searchParams = useSearchParams(); const [isPending, startTransition] = useTransition(); const [query, setQuery] = useState(searchParams.get("q") ?? ""); @@ -41,7 +41,7 @@ export default function GalleryFilters() { } startTransition(() => { - router.replace(params.toString() ? `${pathname}?${params.toString()}` : pathname); + navigate({ to: ".", search: Object.fromEntries(params), replace: true }); }); }; diff --git a/components/gallery/GalleryGrid.tsx b/src/components/gallery/GalleryGrid.tsx similarity index 100% rename from components/gallery/GalleryGrid.tsx rename to src/components/gallery/GalleryGrid.tsx diff --git a/components/layout/Footer.tsx b/src/components/layout/Footer.tsx similarity index 95% rename from components/layout/Footer.tsx rename to src/components/layout/Footer.tsx index d4029a2..b46e466 100644 --- a/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,4 +1,4 @@ -import Link from "next/link"; +import { Link } from "@tanstack/react-router"; import { Logo } from "@/components/shared/Logo"; import { FOOTER_NAV_GROUPS } from "@/lib/navigation"; @@ -24,7 +24,7 @@ export default function Footer() { {links.map((link) => ( {link.label} diff --git a/components/layout/Header.tsx b/src/components/layout/Header.tsx similarity index 89% rename from components/layout/Header.tsx rename to src/components/layout/Header.tsx index 61ca82a..1d83e97 100644 --- a/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,8 +1,8 @@ "use client"; import { useEffect, useState } from "react"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; +import { Link } from "@tanstack/react-router"; +import { usePathname } from "@/lib/router-nav"; import { Menu, X, Search } from "lucide-react"; import { Logo } from "@/components/shared/Logo"; import { Button } from "@/components/ui/button"; @@ -41,7 +41,7 @@ export default function Header() { )} >
- + @@ -62,7 +62,7 @@ export default function Header() {
- + New Roadmap
@@ -89,7 +89,7 @@ export default function Header() { return ( - + New Roadmap diff --git a/components/layout/LoadingExperience.tsx b/src/components/layout/LoadingExperience.tsx similarity index 100% rename from components/layout/LoadingExperience.tsx rename to src/components/layout/LoadingExperience.tsx diff --git a/components/layout/MarketingHeroShell.tsx b/src/components/layout/MarketingHeroShell.tsx similarity index 100% rename from components/layout/MarketingHeroShell.tsx rename to src/components/layout/MarketingHeroShell.tsx diff --git a/components/layout/StudioSidebar.tsx b/src/components/layout/StudioSidebar.tsx similarity index 96% rename from components/layout/StudioSidebar.tsx rename to src/components/layout/StudioSidebar.tsx index 602724a..9954448 100644 --- a/components/layout/StudioSidebar.tsx +++ b/src/components/layout/StudioSidebar.tsx @@ -1,7 +1,7 @@ "use client"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; +import { Link } from "@tanstack/react-router"; +import { usePathname } from "@/lib/router-nav"; import { BarChart2, LayoutDashboard, LayoutGrid, PlusCircle, Settings2 } from "lucide-react"; import CreditBadge from "@/components/credits/CreditBadge"; import { cn } from "@/lib/utils"; @@ -25,7 +25,7 @@ export default function StudioSidebar() { return ( {/* next/image is not practical here because favicon domains are fully dynamic. */} - {/* eslint-disable-next-line @next/next/no-img-element */} - + Remove ZNS branding on Pro diff --git a/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx similarity index 100% rename from components/ui/alert-dialog.tsx rename to src/components/ui/alert-dialog.tsx diff --git a/components/ui/button.tsx b/src/components/ui/button.tsx similarity index 100% rename from components/ui/button.tsx rename to src/components/ui/button.tsx diff --git a/components/ui/card.tsx b/src/components/ui/card.tsx similarity index 100% rename from components/ui/card.tsx rename to src/components/ui/card.tsx diff --git a/components/ui/input.tsx b/src/components/ui/input.tsx similarity index 100% rename from components/ui/input.tsx rename to src/components/ui/input.tsx diff --git a/components/ui/select.tsx b/src/components/ui/select.tsx similarity index 100% rename from components/ui/select.tsx rename to src/components/ui/select.tsx diff --git a/components/ui/separator.tsx b/src/components/ui/separator.tsx similarity index 100% rename from components/ui/separator.tsx rename to src/components/ui/separator.tsx diff --git a/components/ui/switch.tsx b/src/components/ui/switch.tsx similarity index 100% rename from components/ui/switch.tsx rename to src/components/ui/switch.tsx diff --git a/components/ui/textarea.tsx b/src/components/ui/textarea.tsx similarity index 100% rename from components/ui/textarea.tsx rename to src/components/ui/textarea.tsx diff --git a/components/workspace/ContextPanel.tsx b/src/components/workspace/ContextPanel.tsx similarity index 100% rename from components/workspace/ContextPanel.tsx rename to src/components/workspace/ContextPanel.tsx diff --git a/components/workspace/SectionRenderer.tsx b/src/components/workspace/SectionRenderer.tsx similarity index 100% rename from components/workspace/SectionRenderer.tsx rename to src/components/workspace/SectionRenderer.tsx diff --git a/components/workspace/SettingsModal.tsx b/src/components/workspace/SettingsModal.tsx similarity index 100% rename from components/workspace/SettingsModal.tsx rename to src/components/workspace/SettingsModal.tsx diff --git a/components/workspace/ShareEmbedModal.tsx b/src/components/workspace/ShareEmbedModal.tsx similarity index 100% rename from components/workspace/ShareEmbedModal.tsx rename to src/components/workspace/ShareEmbedModal.tsx diff --git a/components/workspace/SrsReviewModal.tsx b/src/components/workspace/SrsReviewModal.tsx similarity index 100% rename from components/workspace/SrsReviewModal.tsx rename to src/components/workspace/SrsReviewModal.tsx diff --git a/components/workspace/WorkspaceShell.tsx b/src/components/workspace/WorkspaceShell.tsx similarity index 98% rename from components/workspace/WorkspaceShell.tsx rename to src/components/workspace/WorkspaceShell.tsx index b4bb8b3..0b67d4f 100644 --- a/components/workspace/WorkspaceShell.tsx +++ b/src/components/workspace/WorkspaceShell.tsx @@ -25,10 +25,9 @@ import { } from "@/components/ui/alert-dialog"; import { Input } from "@/components/ui/input"; import { ThemeToggle } from "@/components/shared/ThemeToggle"; -import Link from "next/link"; +import { Link, useNavigate } from "@tanstack/react-router"; import { SECTION_LABELS } from "@/lib/constants"; import { Search, Settings, Home, ArrowRight, CheckCircle2, Circle, LayoutDashboard, SearchCode, BookOpen, Video, ListTodo, Trophy, Layers, Calendar, Clock, Lock, Target, Flame, Info, Plus, Share, RefreshCw, HardDrive, BarChart2, HelpCircle, X, FileText, Braces, Printer, History, ChevronDown, Key } from "lucide-react"; -import { useRouter } from "next/navigation"; import { exportAsMarkdown, exportAsJSON, exportAsPDF } from "@/lib/export"; import { saveVersion, getVersions } from "@/lib/versioning"; import { getWorkspacePersistence } from "@/lib/workspace-persistence"; @@ -55,7 +54,7 @@ export default function WorkspaceShell({ roadmap, onUpdateSection, onUpdateRoadm const { currentStreak, isAtRisk, logActivity, tasksCompletedToday, tasksCompletedYesterday } = useStreak(); const { unlockedBadges, unlockBadge, recentBadge, clearRecentBadge, getBadges } = useAchievements(); const { sessionTimeMs, getModuleTime } = useTimeTracker(activeSectionId !== "dashboard" ? activeSectionId : undefined); - const router = useRouter(); + const navigate = useNavigate(); const [userConfig, setUserConfig] = useState(null); const [srsItems, setSrsItems] = useState([]); const [isSrsModalOpen, setIsSrsModalOpen] = useState(false); @@ -597,7 +596,6 @@ export default function WorkspaceShell({ roadmap, onUpdateSection, onUpdateRoadm {logoUrl && ( <> {/* next/image is not practical here because the logo URL is fully user-defined. */} - {/* eslint-disable-next-line @next/next/no-img-element */} Logo )} @@ -745,14 +743,14 @@ export default function WorkspaceShell({ roadmap, onUpdateSection, onUpdateRoadm {sidebarOpen ? ( <> {!isEmbed && ( - + Back to Home )} {!isEmbed && !isReadOnly && ( <> - + Settings @@ -799,13 +797,13 @@ export default function WorkspaceShell({ roadmap, onUpdateSection, onUpdateRoadm ) : ( <> {!isEmbed && ( - + )} {!isEmbed && !isReadOnly && ( <> - + {userConfig?.useCustomKey && ( @@ -937,7 +935,7 @@ export default function WorkspaceShell({ roadmap, onUpdateSection, onUpdateRoadm Your content generated a minimal workspace. For richer results, try adding more detail to your content or use a longer guide. @@ -104,7 +102,7 @@ export default function DashboardPage() {

You have no active workspaces right now.

)} diff --git a/app/(studio)/settings/page.tsx b/src/views/studio/settings.tsx similarity index 98% rename from app/(studio)/settings/page.tsx rename to src/views/studio/settings.tsx index 9b52ae8..0a2c414 100644 --- a/app/(studio)/settings/page.tsx +++ b/src/views/studio/settings.tsx @@ -1,8 +1,7 @@ -"use client"; - import { Suspense, type ReactNode, useEffect, useState, useTransition } from "react"; -import Link from "next/link"; -import { useRouter, useSearchParams } from "next/navigation"; +import { Link } from "@tanstack/react-router"; +import { useNavigate } from "@tanstack/react-router"; +import { useSearchParams } from "@/lib/router-nav"; import { ArrowUpRight, Check, @@ -184,7 +183,7 @@ export default function SettingsPage() { } function SettingsContent() { - const router = useRouter(); + const navigate = useNavigate(); const searchParams = useSearchParams(); const [config, setConfig] = useState(DEFAULT_CONFIG); const [activeCategory, setActiveCategory] = useState("general"); @@ -271,7 +270,11 @@ function SettingsContent() { setActiveCategory(next); const params = new URLSearchParams(searchParams.toString()); params.set("tab", next); - router.replace(`/settings?${params.toString()}`); + navigate({ + to: "/settings", + search: Object.fromEntries(params), + replace: true, + }); } async function refreshRemoteSettings() { @@ -576,11 +579,11 @@ function SettingsContent() { You are on the free studio tier: up to 3 workspaces and 10 shared AI generations per month. Bring your own provider key (BYOK) to skip credit deductions entirely.

- + View pricing - + Compare plans diff --git a/app/(studio)/workspaces/page.tsx b/src/views/studio/workspaces.tsx similarity index 98% rename from app/(studio)/workspaces/page.tsx rename to src/views/studio/workspaces.tsx index 0dc4a14..2bed0f6 100644 --- a/app/(studio)/workspaces/page.tsx +++ b/src/views/studio/workspaces.tsx @@ -1,7 +1,5 @@ -"use client"; - import { useEffect, useMemo, useState } from "react"; -import Link from "next/link"; +import { Link } from "@tanstack/react-router"; import { ArrowUpDown, HardDrive, Plus, SlidersHorizontal } from "lucide-react"; import WorkspaceLibraryCard from "@/components/workspaces/WorkspaceLibraryCard"; import { Button } from "@/components/ui/button"; @@ -78,7 +76,7 @@ export default function WorkspacesPage() {

- + New workspace @@ -190,7 +188,7 @@ export default function WorkspacesPage() {

- Test image - Link - - - `); - }); - - await new Promise((resolve) => { - server.listen(0, () => { - const address = server.address(); - if (address && typeof address !== 'string') { - testUrl = `http://localhost:${address.port}`; - } - resolve(); - }); - }); - - checker = new AccessibilityChecker('AA'); - browser = await chromium.launch(); - }); - - afterAll(async () => { - await checker.close(); - await browser.close(); - await new Promise((resolve) => { - server.close(() => resolve()); - }); - }); - - it('should audit a page and return a report', async () => { - const report = await checker.auditPage(testUrl); - - expect(report).toBeDefined(); - expect(report.url).toBe(testUrl); - expect(report.wcagLevel).toBe('AA'); - expect(report.violations).toBeDefined(); - expect(report.passes).toBeDefined(); - expect(report.incomplete).toBeDefined(); - expect(typeof report.score).toBe('number'); - expect(report.score).toBeGreaterThanOrEqual(0); - expect(report.score).toBeLessThanOrEqual(100); - }); - - it('should detect violations in the report', async () => { - const report = await checker.auditPage(testUrl); - - expect(Array.isArray(report.violations)).toBe(true); - expect(Array.isArray(report.passes)).toBe(true); - }); -}); diff --git a/lib/ui-analysis/__tests__/accessibility-checker.test.ts b/lib/ui-analysis/__tests__/accessibility-checker.test.ts deleted file mode 100644 index 6db472d..0000000 --- a/lib/ui-analysis/__tests__/accessibility-checker.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Unit tests for AccessibilityChecker - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import { AccessibilityChecker, createAccessibilityChecker } from '../accessibility-checker'; - -describe('AccessibilityChecker', () => { - let checker: AccessibilityChecker; - - beforeAll(() => { - checker = createAccessibilityChecker('AA'); - }); - - afterAll(async () => { - await checker.close(); - }); - - describe('initialization', () => { - it('should create checker with default WCAG level', () => { - const defaultChecker = new AccessibilityChecker(); - expect(defaultChecker).toBeDefined(); - }); - - it('should create checker with specified WCAG level', () => { - const aaaChecker = new AccessibilityChecker('AAA'); - expect(aaaChecker).toBeDefined(); - }); - }); - - describe('factory functions', () => { - it('should create checker via factory function', () => { - const factoryChecker = createAccessibilityChecker('AA'); - expect(factoryChecker).toBeDefined(); - }); - }); -}); diff --git a/lib/ui-analysis/__tests__/analyzer.property.test.ts b/lib/ui-analysis/__tests__/analyzer.property.test.ts deleted file mode 100644 index 350286f..0000000 --- a/lib/ui-analysis/__tests__/analyzer.property.test.ts +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Property-based tests for UIAnalyzer - * Using fast-check library for property-based testing - */ - -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import fc from 'fast-check'; -import { UIAnalyzer } from '../analyzer'; -import { writeFileSync, mkdirSync, rmSync } from 'fs'; -import { join } from 'path'; - -describe('UIAnalyzer - Property-Based Tests', () => { - let analyzer: UIAnalyzer; - const testDir = join(__dirname, 'pbt-test-components'); - - beforeEach(() => { - analyzer = new UIAnalyzer(); - - // Clean up test directory - try { - rmSync(testDir, { recursive: true, force: true }); - } catch (error) { - // Directory might not exist - } - - // Create test directory - mkdirSync(testDir, { recursive: true }); - }); - - afterEach(() => { - // Clean up after tests - try { - rmSync(testDir, { recursive: true, force: true }); - } catch (error) { - // Ignore cleanup errors - } - }); - - /** - * Property 1: Component Scanning Completeness - * **Validates: Requirements 1.1** - * - * For any set of component files in the project, when the UI_Analyzer scans them, - * every file should be processed and included in the analysis report. - */ - describe('Property 1: Component Scanning Completeness', () => { - it('should process all component files in any given set', () => { - fc.assert( - fc.property( - // Generate an array of 1-10 component files - fc.array( - fc.record({ - name: fc.string({ minLength: 1, maxLength: 20 }) - .filter(s => /^[a-zA-Z0-9_-]+$/.test(s)) - .map(s => `${s}.tsx`), - content: fc.oneof( - fc.constant('export default function Component() { return
Test
; }'), - fc.constant('export default function Component() { return
Test
; }'), - fc.constant('export default function Component() { return
Test
; }'), - fc.constant('export default function Component() { return
Test
; }'), - fc.constant('export default function Component() { return ; }'), - ) - }), - { minLength: 1, maxLength: 10 } - ), - (componentFiles) => { - // Create unique file names by adding index prefix - const filesToCreate = componentFiles.map((file, index) => ({ - name: `file_${index}_${file.name}`, - content: file.content - })); - - // Write all component files to test directory - filesToCreate.forEach((file) => { - const filePath = join(testDir, file.name); - writeFileSync(filePath, file.content); - }); - - // Scan the directory - const report = analyzer.scanDirectory(testDir); - - // Property: All files should be scanned - // The number of files scanned should equal the number of files created - const result = report.summary.filesScanned === filesToCreate.length; - - // Clean up files for next iteration - filesToCreate.forEach((file) => { - try { - rmSync(join(testDir, file.name), { force: true }); - } catch (e) { - // Ignore - } - }); - - return result; - } - ), - { numRuns: 100 } - ); - }); - - it('should include all component files regardless of subdirectory depth', () => { - fc.assert( - fc.property( - // Generate component files with nested directory structures - fc.array( - fc.record({ - path: fc.array( - fc.string({ minLength: 1, maxLength: 10 }) - .filter(s => /^[a-zA-Z0-9_-]+$/.test(s)), - { minLength: 0, maxLength: 3 } - ).map(parts => parts.join('/')), - name: fc.string({ minLength: 1, maxLength: 20 }) - .filter(s => /^[a-zA-Z0-9_-]+$/.test(s)) - .map(s => `${s}.tsx`), - content: fc.constant('export default function Component() { return
Test
; }') - }), - { minLength: 1, maxLength: 8 } - ), - (componentFiles) => { - // Create files with unique paths - const filesToCreate = componentFiles.map((file, index) => ({ - path: file.path ? `dir_${index}_${file.path}` : `dir_${index}`, - name: `file_${index}_${file.name}`, - content: file.content - })); - - // Write all component files to test directory with subdirectories - filesToCreate.forEach((file) => { - const dirPath = join(testDir, file.path); - mkdirSync(dirPath, { recursive: true }); - - const filePath = join(dirPath, file.name); - writeFileSync(filePath, file.content); - }); - - // Scan the directory - const report = analyzer.scanDirectory(testDir); - - // Property: All files should be scanned regardless of depth - const result = report.summary.filesScanned === filesToCreate.length; - - // Clean up for next iteration - filesToCreate.forEach((file) => { - try { - const dirPath = join(testDir, file.path); - rmSync(dirPath, { recursive: true, force: true }); - } catch (e) { - // Ignore - } - }); - - return result; - } - ), - { numRuns: 50 } - ); - }); - - it('should only count .tsx and .jsx files, not other file types', () => { - fc.assert( - fc.property( - fc.record({ - componentFiles: fc.array( - fc.record({ - name: fc.string({ minLength: 1, maxLength: 20 }) - .filter(s => /^[a-zA-Z0-9_-]+$/.test(s)) - .map(s => `${s}.tsx`), - content: fc.constant('export default function Component() { return
Test
; }') - }), - { minLength: 1, maxLength: 5 } - ), - otherFiles: fc.array( - fc.record({ - name: fc.string({ minLength: 1, maxLength: 20 }) - .filter(s => /^[a-zA-Z0-9_-]+$/.test(s)) - .chain(s => fc.oneof( - fc.constant(`${s}.ts`), - fc.constant(`${s}.js`), - fc.constant(`${s}.json`), - fc.constant(`${s}.md`) - )), - content: fc.constant('// Not a component') - }), - { minLength: 0, maxLength: 5 } - ) - }), - ({ componentFiles, otherFiles }) => { - // Create unique file names with prefixes - const componentFilesToCreate = componentFiles.map((file, index) => ({ - name: `comp_${index}_${file.name}`, - content: file.content - })); - - const otherFilesToCreate = otherFiles.map((file, index) => ({ - name: `other_${index}_${file.name}`, - content: file.content - })); - - // Write component files - componentFilesToCreate.forEach((file) => { - const filePath = join(testDir, file.name); - writeFileSync(filePath, file.content); - }); - - // Write other files - otherFilesToCreate.forEach((file) => { - const filePath = join(testDir, file.name); - writeFileSync(filePath, file.content); - }); - - // Scan the directory - const report = analyzer.scanDirectory(testDir); - - // Property: Only component files (.tsx, .jsx) should be counted - const result = report.summary.filesScanned === componentFilesToCreate.length; - - // Clean up for next iteration - [...componentFilesToCreate, ...otherFilesToCreate].forEach((file) => { - try { - rmSync(join(testDir, file.name), { force: true }); - } catch (e) { - // Ignore - } - }); - - return result; - } - ), - { numRuns: 50 } - ); - }); - }); -}); diff --git a/lib/ui-analysis/__tests__/analyzer.test.ts b/lib/ui-analysis/__tests__/analyzer.test.ts deleted file mode 100644 index 11731ec..0000000 --- a/lib/ui-analysis/__tests__/analyzer.test.ts +++ /dev/null @@ -1,507 +0,0 @@ -/** - * Unit tests for UIAnalyzer - */ - -import { UIAnalyzer } from '../analyzer'; -import { writeFileSync, mkdirSync, rmSync } from 'fs'; -import { join } from 'path'; - -describe('UIAnalyzer', () => { - let analyzer: UIAnalyzer; - const testDir = join(__dirname, 'test-components'); - - beforeEach(() => { - analyzer = new UIAnalyzer(); - - // Clean up test directory - try { - rmSync(testDir, { recursive: true, force: true }); - } catch (error) { - // Directory might not exist - } - - // Create test directory - mkdirSync(testDir, { recursive: true }); - }); - - describe('scanComponent', () => { - it('should detect overflow without constraints', () => { - const testFile = join(testDir, 'overflow-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.length).toBeGreaterThan(0); - expect(issues.some(i => i.type === 'overflow')).toBe(true); - }); - - it('should detect overflow-auto without max-height', () => { - const testFile = join(testDir, 'overflow-auto-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'overflow' && i.message.includes('max-height'))).toBe(true); - }); - - it('should flag overflow-x-hidden as critical', () => { - const testFile = join(testDir, 'overflow-x-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'overflow' && i.severity === 'critical')).toBe(true); - }); - - it('should detect arbitrary spacing values', () => { - const testFile = join(testDir, 'spacing-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.length).toBeGreaterThan(0); - expect(issues.some(i => i.type === 'spacing-inconsistency')).toBe(true); - }); - - it('should detect non-standard spacing values', () => { - const testFile = join(testDir, 'non-standard-spacing.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'spacing-inconsistency' && i.message.includes('Non-standard'))).toBe(true); - }); - - it('should detect z-index usage', () => { - const testFile = join(testDir, 'zindex-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'z-index-conflict')).toBe(true); - }); - - it('should detect arbitrary z-index values', () => { - const testFile = join(testDir, 'arbitrary-zindex.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'z-index-conflict' && i.message.includes('Arbitrary'))).toBe(true); - }); - - it('should detect flex containers without alignment', () => { - const testFile = join(testDir, 'flex-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'misalignment')).toBe(true); - }); - - it('should detect grid containers without gap', () => { - const testFile = join(testDir, 'grid-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'misalignment' && i.message.includes('gap'))).toBe(true); - }); - - it('should detect absolute positioning without positioning classes', () => { - const testFile = join(testDir, 'absolute-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'misalignment' && i.message.includes('positioning'))).toBe(true); - }); - - it('should detect hardcoded colors in style attribute', () => { - const testFile = join(testDir, 'color-test.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'hardcoded-color')).toBe(true); - }); - - it('should not flag overflow with constraints', () => { - const testFile = join(testDir, 'valid-overflow.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'overflow')).toBe(false); - }); - - it('should not flag flex with alignment', () => { - const testFile = join(testDir, 'valid-flex.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'misalignment')).toBe(false); - }); - - it('should not flag grid with gap', () => { - const testFile = join(testDir, 'valid-grid.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'misalignment' && i.message.includes('gap'))).toBe(false); - }); - - it('should not flag absolute with positioning classes', () => { - const testFile = join(testDir, 'valid-absolute.tsx'); - const content = ` - export default function Component() { - return
Content
; - } - `; - writeFileSync(testFile, content); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.some(i => i.type === 'misalignment' && i.message.includes('positioning'))).toBe(false); - }); - - it('should handle parse errors gracefully', () => { - const testFile = join(testDir, 'invalid.tsx'); - const content = ` - export default function Component() { - return
{ - it('should scan all component files in directory', () => { - // Create multiple test files - const file1 = join(testDir, 'component1.tsx'); - const file2 = join(testDir, 'component2.tsx'); - - writeFileSync(file1, ` - export default function Component1() { - return
Content
; - } - `); - - writeFileSync(file2, ` - export default function Component2() { - return
Content
; - } - `); - - const report = analyzer.scanDirectory(testDir); - - expect(report.summary.filesScanned).toBe(2); - expect(report.issues.length).toBeGreaterThan(0); - expect(report.timestamp).toBeDefined(); - expect(report.duration).toBeGreaterThan(0); - }); - - it('should detect z-index conflicts across multiple files', () => { - const file1 = join(testDir, 'modal1.tsx'); - const file2 = join(testDir, 'modal2.tsx'); - - writeFileSync(file1, ` - export default function Modal1() { - return
Modal 1
; - } - `); - - writeFileSync(file2, ` - export default function Modal2() { - return
Modal 2
; - } - `); - - const report = analyzer.scanDirectory(testDir); - - // Should detect that multiple components use z-50 - const zIndexConflicts = report.issues.filter(i => - i.type === 'z-index-conflict' && i.message.includes('Multiple components') - ); - expect(zIndexConflicts.length).toBeGreaterThan(0); - }); - - it('should generate correct summary', () => { - const testFile = join(testDir, 'test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return ( -
-
Issue 1
-
Issue 2
-
Issue 3
-
- ); - } - `); - - const report = analyzer.scanDirectory(testDir); - - expect(report.summary.totalIssues).toBeGreaterThan(0); - expect(report.summary.complianceScore).toBeLessThan(100); - expect(report.summary.complianceScore).toBeGreaterThanOrEqual(0); - }); - - it('should skip node_modules directory', () => { - const nodeModulesDir = join(testDir, 'node_modules'); - mkdirSync(nodeModulesDir, { recursive: true }); - - const testFile = join(nodeModulesDir, 'test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const report = analyzer.scanDirectory(testDir); - - expect(report.summary.filesScanned).toBe(0); - }); - }); - - describe('issue structure', () => { - it('should include all required fields in LayoutIssue', () => { - const testFile = join(testDir, 'test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.length).toBeGreaterThan(0); - - const issue = issues[0]; - expect(issue).toHaveProperty('type'); - expect(issue).toHaveProperty('severity'); - expect(issue).toHaveProperty('filePath'); - expect(issue).toHaveProperty('lineNumber'); - expect(issue).toHaveProperty('columnNumber'); - expect(issue).toHaveProperty('message'); - expect(issue).toHaveProperty('autoFixable'); - }); - - it('should assign severity levels correctly', () => { - const testFile = join(testDir, 'test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return ( -
-
Critical
-
Moderate
-
Minor
-
- ); - } - `); - - const issues = analyzer.scanComponent(testFile); - - const severities = issues.map(i => i.severity); - expect(severities).toContain('critical'); - expect(severities).toContain('moderate'); - expect(severities).toContain('minor'); - }); - - it('should assign critical severity to width classes without responsive variants', () => { - const testFile = join(testDir, 'width-test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - - const widthIssue = issues.find(i => i.type === 'missing-responsive' && i.message.includes('w-64')); - expect(widthIssue?.severity).toBe('critical'); - }); - - it('should assign moderate severity to text classes without responsive variants', () => { - const testFile = join(testDir, 'text-test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - - const textIssue = issues.find(i => i.type === 'missing-responsive' && i.message.includes('text-xl')); - expect(textIssue?.severity).toBe('moderate'); - }); - - it('should provide helpful suggestions', () => { - const testFile = join(testDir, 'suggestion-test.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - - expect(issues.length).toBeGreaterThan(0); - expect(issues[0].suggestion).toBeDefined(); - expect(issues[0].suggestion).toContain('w-*'); - }); - }); - - describe('severity assignment', () => { - it('should assign critical severity to overflow-x-hidden', () => { - const testFile = join(testDir, 'overflow-x.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - const overflowIssue = issues.find(i => i.type === 'overflow' && i.message.includes('overflow-x-hidden')); - - expect(overflowIssue?.severity).toBe('critical'); - }); - - it('should assign moderate severity to high z-index values', () => { - const testFile = join(testDir, 'high-zindex.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - const zIndexIssue = issues.find(i => i.type === 'z-index-conflict' && i.message.includes('Extremely high')); - - expect(zIndexIssue?.severity).toBe('moderate'); - }); - - it('should assign minor severity to arbitrary spacing', () => { - const testFile = join(testDir, 'arbitrary-spacing.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - const spacingIssue = issues.find(i => i.type === 'spacing-inconsistency'); - - expect(spacingIssue?.severity).toBe('minor'); - }); - - it('should assign minor severity to flex without alignment', () => { - const testFile = join(testDir, 'flex-no-align.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - const flexIssue = issues.find(i => i.type === 'misalignment' && i.message.includes('Flex')); - - expect(flexIssue?.severity).toBe('minor'); - }); - - it('should assign moderate severity to absolute without positioning', () => { - const testFile = join(testDir, 'absolute-no-pos.tsx'); - writeFileSync(testFile, ` - export default function Component() { - return
Content
; - } - `); - - const issues = analyzer.scanComponent(testFile); - const absoluteIssue = issues.find(i => i.type === 'misalignment' && i.message.includes('Absolute')); - - expect(absoluteIssue?.severity).toBe('moderate'); - }); - }); -}); diff --git a/lib/ui-analysis/__tests__/config.test.ts b/lib/ui-analysis/__tests__/config.test.ts deleted file mode 100644 index 2ba655b..0000000 --- a/lib/ui-analysis/__tests__/config.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Tests for configuration loader - */ - -import { ConfigLoader, DEFAULT_CONFIG } from '../config'; -import type { UIAnalysisConfig } from '../types'; - -describe('ConfigLoader', () => { - describe('default configuration', () => { - it('should load default configuration when no file exists', () => { - const loader = new ConfigLoader(); - const config = loader.getConfig(); - - expect(config).toBeDefined(); - expect(config.enabled).toBe(true); - expect(config.mode).toBe('development'); - }); - - it('should have valid default breakpoints', () => { - const loader = new ConfigLoader(); - const config = loader.getConfig(); - - expect(config.breakpoints).toHaveLength(4); - expect(config.breakpoints[0].name).toBe('mobile'); - expect(config.breakpoints[0].width).toBe(375); - }); - - it('should have valid default rules', () => { - const loader = new ConfigLoader(); - const config = loader.getConfig(); - - expect(config.rules.layout.checkOverflow).toBe(true); - expect(config.rules.responsive.minTouchTargetSize.width).toBe(44); - expect(config.rules.accessibility.wcagLevel).toBe('AA'); - expect(config.rules.designSystem.enforceTokens).toBe(true); - }); - }); - - describe('validation', () => { - it('should validate default configuration successfully', () => { - const loader = new ConfigLoader(); - const validation = loader.validate(); - - expect(validation.valid).toBe(true); - expect(validation.errors).toHaveLength(0); - }); - - it('should detect invalid mode', () => { - const loader = new ConfigLoader(); - loader.updateConfig({ mode: 'invalid' as any }); - const validation = loader.validate(); - - expect(validation.valid).toBe(false); - expect(validation.errors.length).toBeGreaterThan(0); - }); - - it('should detect invalid WCAG level', () => { - const loader = new ConfigLoader(); - loader.updateConfig({ - rules: { - ...DEFAULT_CONFIG.rules, - accessibility: { - ...DEFAULT_CONFIG.rules.accessibility, - wcagLevel: 'INVALID' as any, - }, - }, - }); - const validation = loader.validate(); - - expect(validation.valid).toBe(false); - expect(validation.errors.some(e => e.includes('WCAG'))).toBe(true); - }); - - it('should detect invalid breakpoint dimensions', () => { - const loader = new ConfigLoader(); - loader.updateConfig({ - breakpoints: [{ name: 'invalid', width: -100, height: 0 }], - }); - const validation = loader.validate(); - - expect(validation.valid).toBe(false); - expect(validation.errors.some(e => e.includes('breakpoint'))).toBe(true); - }); - }); - - describe('mode-specific configuration', () => { - it('should create development mode configuration', () => { - const config = ConfigLoader.forMode('development'); - - expect(config.mode).toBe('development'); - expect(config.reporting.console).toBe(true); - expect(config.reporting.overlay).toBe(true); - expect(config.reporting.json).toBe(false); - }); - - it('should create CI mode configuration', () => { - const config = ConfigLoader.forMode('ci'); - - expect(config.mode).toBe('ci'); - expect(config.reporting.console).toBe(true); - expect(config.reporting.overlay).toBe(false); - expect(config.reporting.json).toBe(true); - expect(config.reporting.minSeverity).toBe('critical'); - expect(config.rules.designSystem.strictMode).toBe(true); - }); - - it('should create production mode configuration', () => { - const config = ConfigLoader.forMode('production'); - - expect(config.mode).toBe('production'); - expect(config.enabled).toBe(false); - }); - }); - - describe('configuration updates', () => { - it('should update configuration at runtime', () => { - const loader = new ConfigLoader(); - - loader.updateConfig({ enabled: false }); - expect(loader.getConfig().enabled).toBe(false); - - loader.updateConfig({ mode: 'ci' }); - expect(loader.getConfig().mode).toBe('ci'); - }); - - it('should merge partial updates with existing config', () => { - const loader = new ConfigLoader(); - const originalBreakpoints = loader.getConfig().breakpoints; - - loader.updateConfig({ - rules: { - ...DEFAULT_CONFIG.rules, - layout: { - ...DEFAULT_CONFIG.rules.layout, - checkOverflow: false, - }, - }, - }); - - const config = loader.getConfig(); - expect(config.rules.layout.checkOverflow).toBe(false); - expect(config.rules.layout.checkAlignment).toBe(true); // unchanged - expect(config.breakpoints).toEqual(originalBreakpoints); // unchanged - }); - }); -}); diff --git a/lib/ui-analysis/__tests__/design-system-validator.test.ts b/lib/ui-analysis/__tests__/design-system-validator.test.ts deleted file mode 100644 index 241de11..0000000 --- a/lib/ui-analysis/__tests__/design-system-validator.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Unit tests for Design System Validator - */ - -import { describe, it, expect, beforeEach } from 'vitest'; -import { DesignSystemValidator } from '../design-system-validator'; - -describe('DesignSystemValidator', () => { - let validator: DesignSystemValidator; - - beforeEach(() => { - validator = new DesignSystemValidator(); - }); - - describe('validateColors', () => { - it('should detect hardcoded hex colors', () => { - const content = ` - const styles = { - color: '#ff0000', - background: '#8aa2ff' - }; - `; - - const violations = validator.validateColors('test.tsx', content); - - expect(violations).toHaveLength(2); - expect(violations[0].type).toBe('hardcoded-hex'); - expect(violations[0].value).toBe('#ff0000'); - expect(violations[1].value).toBe('#8aa2ff'); - expect(violations[1].suggestedToken).toBe('var(--color-accent)'); - }); - - it('should detect hardcoded rgb colors', () => { - const content = ` - const styles = { - color: 'rgb(255, 0, 0)', - background: 'rgba(255, 0, 0, 0.5)' - }; - `; - - const violations = validator.validateColors('test.tsx', content); - - expect(violations).toHaveLength(2); - expect(violations[0].type).toBe('hardcoded-rgb'); - expect(violations[1].type).toBe('hardcoded-rgb'); - }); - - it('should not flag CSS custom properties', () => { - const content = ` - const styles = { - color: 'var(--color-text)', - background: 'var(--color-surface)' - }; - `; - - const violations = validator.validateColors('test.tsx', content); - - expect(violations).toHaveLength(0); - }); - }); - - describe('validateSpacing', () => { - it('should detect arbitrary spacing values not in scale', () => { - const content = ` -
Content
- `; - - const violations = validator.validateSpacing('test.tsx', content); - - expect(violations.length).toBeGreaterThan(0); - expect(violations[0].type).toBe('arbitrary-value'); - }); - - it('should not flag spacing values in the design scale', () => { - const content = ` -
Content
- `; - - const violations = validator.validateSpacing('test.tsx', content); - - expect(violations).toHaveLength(0); - }); - }); - - describe('validateBorderRadius', () => { - it('should detect arbitrary border radius values', () => { - const content = ` -
Content
- `; - - const violations = validator.validateBorderRadius('test.tsx', content); - - expect(violations).toHaveLength(1); - expect(violations[0].type).toBe('hardcoded-radius'); - expect(violations[0].suggestedToken).toBe('var(--radius-sm)'); - }); - - it('should suggest nearest radius token', () => { - const content = ` -
Content
- `; - - const violations = validator.validateBorderRadius('test.tsx', content); - - expect(violations).toHaveLength(1); - expect(violations[0].suggestedToken).toBe('var(--radius-md)'); - }); - }); - - describe('validateFonts', () => { - it('should detect hardcoded font families', () => { - const content = ` - .text { - font-family: 'Arial, sans-serif'; - } - `; - - const violations = validator.validateFonts('test.css', content); - - expect(violations).toHaveLength(1); - expect(violations[0].type).toBe('hardcoded-font'); - }); - - it('should not flag CSS custom properties for fonts', () => { - const content = ` - .text { - font-family: var(--font-sans); - } - `; - - const violations = validator.validateFonts('test.css', content); - - expect(violations).toHaveLength(0); - }); - - it('should suggest appropriate font token based on name', () => { - const content = ` - .code { - font-family: 'Monaco, monospace'; - } - `; - - const violations = validator.validateFonts('test.css', content); - - expect(violations).toHaveLength(1); - expect(violations[0].suggestedToken).toBe('var(--font-mono)'); - }); - }); - - describe('generateReport', () => { - it('should generate comprehensive report with all violations', () => { - const cssContent = ` - .text { - color: #ff0000; - font-family: 'Arial'; - } - `; - - const tsxContent = ` -
Content
- `; - - validator.validateColors('test.css', cssContent); - validator.validateSpacing('test.tsx', tsxContent); - validator.validateBorderRadius('test.tsx', tsxContent); - validator.validateFonts('test.css', cssContent); - - const report = validator.generateReport(); - - expect(report.totalViolations).toBeGreaterThan(0); - expect(report.colorViolations.length).toBeGreaterThan(0); - expect(report.complianceScore).toBeLessThan(100); - }); - - it('should calculate compliance score correctly', () => { - const report = validator.generateReport(); - - expect(report.complianceScore).toBeGreaterThanOrEqual(0); - expect(report.complianceScore).toBeLessThanOrEqual(100); - }); - }); - - describe('reset', () => { - it('should clear all violations', () => { - const cssContent = ` - .text { - color: #ff0000; - } - `; - - validator.validateColors('test.css', cssContent); - expect(validator.generateReport().totalViolations).toBeGreaterThan(0); - - validator.reset(); - expect(validator.generateReport().totalViolations).toBe(0); - }); - }); -}); diff --git a/lib/ui-analysis/__tests__/responsive-validator.integration.example.ts b/lib/ui-analysis/__tests__/responsive-validator.integration.example.ts deleted file mode 100644 index d6a9f10..0000000 --- a/lib/ui-analysis/__tests__/responsive-validator.integration.example.ts +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Integration Example for ResponsiveValidator - * - * This file demonstrates how to use the ResponsiveValidator in real scenarios. - * Note: This is an example file, not an actual test that runs in CI. - */ - -import { ResponsiveValidator, testResponsive, STANDARD_BREAKPOINTS } from '../responsive-validator'; - -/** - * Example 1: Quick test of a single URL - */ -async function quickTest() { - console.log('Running quick responsive test...'); - - const report = await testResponsive('http://localhost:3000'); - - console.log(`Tested URL: ${report.url}`); - console.log(`Total issues found: ${report.issues.length}`); - - // Check mobile viewport - if (report.breakpoints.mobile.hasHorizontalScroll) { - console.log('⚠️ Mobile viewport has horizontal scroll'); - } - - // Check touch targets - const mobileTouchIssues = report.breakpoints.mobile.touchTargetIssues; - if (mobileTouchIssues.length > 0) { - console.log(`⚠️ Found ${mobileTouchIssues.length} touch target issues on mobile`); - mobileTouchIssues.forEach(issue => { - console.log(` - ${issue.selector}: ${issue.width}x${issue.height}px`); - }); - } - - return report; -} - -/** - * Example 2: Test specific viewport with custom settings - */ -async function customViewportTest() { - console.log('Running custom viewport test...'); - - // Create validator with larger touch target requirement - const validator = new ResponsiveValidator({ width: 48, height: 48 }); - - try { - await validator.initialize(); - - // Test at a specific viewport size - const report = await validator.testViewport( - 'http://localhost:3000', - 375, // iPhone SE width - 667 // iPhone SE height - ); - - console.log(`Viewport: ${report.width}x${report.height}`); - console.log(`Horizontal scroll: ${report.hasHorizontalScroll ? 'Yes' : 'No'}`); - console.log(`Touch target issues: ${report.touchTargetIssues.length}`); - console.log(`Layout shifts: ${report.layoutShifts.length}`); - - return report; - } finally { - await validator.close(); - } -} - -/** - * Example 3: Test all breakpoints and generate detailed report - */ -async function comprehensiveTest() { - console.log('Running comprehensive responsive test...'); - - const validator = new ResponsiveValidator(); - - try { - const report = await validator.testAllBreakpoints('http://localhost:3000'); - - // Analyze each breakpoint - const breakpoints = ['mobile', 'tablet', 'desktop', 'wide'] as const; - - for (const bp of breakpoints) { - const bpReport = report.breakpoints[bp]; - console.log(`\n${bp.toUpperCase()} (${bpReport.width}x${bpReport.height}):`); - console.log(` Horizontal scroll: ${bpReport.hasHorizontalScroll ? '❌' : '✅'}`); - console.log(` Touch target issues: ${bpReport.touchTargetIssues.length}`); - console.log(` Layout shifts: ${bpReport.layoutShifts.length}`); - } - - // Summary - console.log(`\n📊 SUMMARY:`); - console.log(`Total issues: ${report.issues.length}`); - - const criticalIssues = report.issues.filter(i => i.severity === 'critical'); - const moderateIssues = report.issues.filter(i => i.severity === 'moderate'); - - console.log(` Critical: ${criticalIssues.length}`); - console.log(` Moderate: ${moderateIssues.length}`); - - return report; - } finally { - await validator.close(); - } -} - -/** - * Example 4: Test multiple pages - */ -async function multiPageTest() { - console.log('Running multi-page test...'); - - const validator = new ResponsiveValidator(); - const pages = [ - 'http://localhost:3000', - 'http://localhost:3000/about', - 'http://localhost:3000/contact', - ]; - - try { - const results = []; - - for (const url of pages) { - console.log(`\nTesting: ${url}`); - const report = await validator.testAllBreakpoints(url); - results.push(report); - - console.log(` Issues: ${report.issues.length}`); - } - - // Aggregate results - const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0); - console.log(`\n📊 Total issues across all pages: ${totalIssues}`); - - return results; - } finally { - await validator.close(); - } -} - -/** - * Example 5: CI/CD integration - */ -async function ciTest() { - console.log('Running CI test...'); - - const validator = new ResponsiveValidator(); - - try { - const report = await validator.testAllBreakpoints('http://localhost:3000'); - - // Check for critical issues - const criticalIssues = report.issues.filter(i => i.severity === 'critical'); - - if (criticalIssues.length > 0) { - console.error(`❌ CI FAILED: Found ${criticalIssues.length} critical issues`); - criticalIssues.forEach(issue => { - console.error(` - ${issue.message}`); - }); - process.exit(1); - } - - console.log('✅ CI PASSED: No critical responsive issues found'); - return report; - } finally { - await validator.close(); - } -} - -// Export examples for documentation -export { - quickTest, - customViewportTest, - comprehensiveTest, - multiPageTest, - ciTest, -}; - -// Usage instructions -if (require.main === module) { - console.log(` -ResponsiveValidator Integration Examples -========================================= - -To run these examples: - -1. Start your development server: - npm run dev - -2. Run an example: - npx tsx lib/ui-analysis/__tests__/responsive-validator.integration.example.ts - -Available examples: -- quickTest() - Quick test of a single URL -- customViewportTest() - Test with custom settings -- comprehensiveTest() - Test all breakpoints with detailed report -- multiPageTest() - Test multiple pages -- ciTest() - CI/CD integration example - -Standard breakpoints: -- Mobile: ${STANDARD_BREAKPOINTS.mobile.width}x${STANDARD_BREAKPOINTS.mobile.height} -- Tablet: ${STANDARD_BREAKPOINTS.tablet.width}x${STANDARD_BREAKPOINTS.tablet.height} -- Desktop: ${STANDARD_BREAKPOINTS.desktop.width}x${STANDARD_BREAKPOINTS.desktop.height} -- Wide: ${STANDARD_BREAKPOINTS.wide.width}x${STANDARD_BREAKPOINTS.wide.height} - `); -} diff --git a/lib/ui-analysis/__tests__/responsive-validator.test.ts b/lib/ui-analysis/__tests__/responsive-validator.test.ts deleted file mode 100644 index 35429e1..0000000 --- a/lib/ui-analysis/__tests__/responsive-validator.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Unit tests for ResponsiveValidator - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import { ResponsiveValidator, STANDARD_BREAKPOINTS } from '../responsive-validator'; - -describe('ResponsiveValidator', () => { - let validator: ResponsiveValidator; - - beforeAll(() => { - validator = new ResponsiveValidator(); - }); - - afterAll(async () => { - await validator.close(); - }); - - describe('initialization', () => { - it('should create validator with default touch target size', () => { - const v = new ResponsiveValidator(); - expect(v).toBeDefined(); - }); - - it('should create validator with custom touch target size', () => { - const v = new ResponsiveValidator({ width: 48, height: 48 }); - expect(v).toBeDefined(); - }); - - it('should initialize browser', async () => { - await validator.initialize(); - // If no error is thrown, initialization succeeded - expect(true).toBe(true); - }); - }); - - describe('standard breakpoints', () => { - it('should define mobile breakpoint at 640px', () => { - expect(STANDARD_BREAKPOINTS.mobile.width).toBe(640); - }); - - it('should define tablet breakpoint at 768px', () => { - expect(STANDARD_BREAKPOINTS.tablet.width).toBe(768); - }); - - it('should define desktop breakpoint at 1024px', () => { - expect(STANDARD_BREAKPOINTS.desktop.width).toBe(1024); - }); - - it('should define wide breakpoint at 1280px', () => { - expect(STANDARD_BREAKPOINTS.wide.width).toBe(1280); - }); - }); - - describe('viewport testing', () => { - it('should return viewport report structure', async () => { - // Create a simple HTML page for testing - const testHtml = ` - - - Test - - - - - `; - - await validator.initialize(); - - // We'll skip actual browser testing in unit tests - // This would be better suited for integration tests - expect(validator).toBeDefined(); - }); - }); - - describe('touch target validation', () => { - it('should use default minimum size of 44x44px', () => { - const v = new ResponsiveValidator(); - expect(v).toBeDefined(); - }); - - it('should accept custom minimum touch target size', () => { - const customSize = { width: 48, height: 48 }; - const v = new ResponsiveValidator(customSize); - expect(v).toBeDefined(); - }); - }); - - describe('horizontal scroll detection', () => { - it('should detect when scroll width exceeds viewport width', () => { - // This would require actual browser testing - // Placeholder for structure validation - expect(true).toBe(true); - }); - }); - - describe('layout shift measurement', () => { - it('should identify images without explicit dimensions', () => { - // This would require actual browser testing - // Placeholder for structure validation - expect(true).toBe(true); - }); - }); - - describe('issue conversion', () => { - it('should convert horizontal scroll to critical layout issue', () => { - // Test that viewport issues are properly converted to layout issues - expect(true).toBe(true); - }); - - it('should convert touch target issues to moderate layout issues', () => { - // Test that touch target violations are properly reported - expect(true).toBe(true); - }); - }); - - describe('cleanup', () => { - it('should close browser gracefully', async () => { - const v = new ResponsiveValidator(); - await v.initialize(); - await v.close(); - // If no error is thrown, cleanup succeeded - expect(true).toBe(true); - }); - }); -}); diff --git a/lib/ui-analysis/__tests__/test-components/absolute-no-pos.tsx b/lib/ui-analysis/__tests__/test-components/absolute-no-pos.tsx deleted file mode 100644 index d0369a2..0000000 --- a/lib/ui-analysis/__tests__/test-components/absolute-no-pos.tsx +++ /dev/null @@ -1,5 +0,0 @@ - - export default function Component() { - return
Content
; - } - \ No newline at end of file diff --git a/lib/ui-analysis/__tests__/visual-hierarchy-optimizer.test.ts b/lib/ui-analysis/__tests__/visual-hierarchy-optimizer.test.ts deleted file mode 100644 index 0646e75..0000000 --- a/lib/ui-analysis/__tests__/visual-hierarchy-optimizer.test.ts +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Unit tests for Visual Hierarchy Optimizer - */ - -import { describe, it, expect, beforeEach } from 'vitest'; -import { VisualHierarchyOptimizer } from '../visual-hierarchy-optimizer'; - -describe('VisualHierarchyOptimizer', () => { - let optimizer: VisualHierarchyOptimizer; - - beforeEach(() => { - optimizer = new VisualHierarchyOptimizer(); - }); - - describe('analyzeHeadingStructure', () => { - it('should extract headings from HTML', () => { - const html = ` -

Main Title

-

Subtitle

-

Section

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.headings).toHaveLength(3); - expect(report.headings[0].level).toBe(1); - expect(report.headings[0].text).toBe('Main Title'); - expect(report.headings[1].level).toBe(2); - expect(report.headings[1].text).toBe('Subtitle'); - expect(report.headings[2].level).toBe(3); - expect(report.headings[2].text).toBe('Section'); - }); - - it('should detect multiple h1 elements', () => { - const html = ` -

First Title

-

Second Title

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.structure).toBe('invalid'); - expect(report.issues).toHaveLength(1); - expect(report.issues[0].type).toBe('multiple-h1'); - }); - - it('should detect skipped heading levels', () => { - const html = ` -

Title

-

Skipped h2

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.structure).toBe('invalid'); - expect(report.issues).toHaveLength(1); - expect(report.issues[0].type).toBe('skipped-level'); - expect(report.issues[0].message).toContain('h1 followed by h3'); - }); - - it('should validate proper heading hierarchy', () => { - const html = ` -

Main Title

-

Section 1

-

Subsection 1.1

-

Subsection 1.2

-

Section 2

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.structure).toBe('valid'); - expect(report.issues).toHaveLength(0); - }); - - it('should extract inline styles from headings', () => { - const html = ` -

Styled Title

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.headings[0].fontSize).toBe('3rem'); - expect(report.headings[0].fontWeight).toBe('800'); - }); - - it('should use default styles when no inline styles present', () => { - const html = `

Title

`; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.headings[0].fontSize).toBe('2rem'); - expect(report.headings[0].fontWeight).toBe('700'); - expect(report.headings[0].lineHeight).toBe('1.2'); - }); - - it('should detect inconsistent font sizes for same heading level', () => { - const html = ` -

First h2

-

Second h2

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.structure).toBe('invalid'); - const inconsistentIssues = report.issues.filter(i => i.type === 'inconsistent-size'); - expect(inconsistentIssues.length).toBeGreaterThan(0); - }); - - it('should strip HTML tags from heading text', () => { - const html = ` -

Title with nested tags

- `; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.headings[0].text).toBe('Title with nested tags'); - }); - - it('should handle empty HTML', () => { - const html = ''; - - const report = optimizer.analyzeHeadingStructure(html); - - expect(report.headings).toHaveLength(0); - expect(report.structure).toBe('valid'); - expect(report.issues).toHaveLength(0); - }); - }); - - describe('analyzeAccentUsage', () => { - it('should detect accent color in hex format', () => { - const css = ` - .button { - background-color: #8aa2ff; - } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages).toHaveLength(1); - expect(report.usages[0].location.lineNumber).toBe(3); - }); - - it('should detect accent color in CSS variable format', () => { - const css = ` - .link { - color: var(--color-accent); - } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages).toHaveLength(1); - }); - - it('should identify interactive context as appropriate', () => { - const css = ` - button:hover { - background-color: #8aa2ff; - } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages[0].context).toBe('interactive'); - expect(report.usages[0].appropriate).toBe(true); - expect(report.issues).toHaveLength(0); - }); - - it('should identify decorative context as inappropriate', () => { - const css = ` - .divider { - border-color: #8aa2ff; - } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages[0].context).toBe('decorative'); - expect(report.usages[0].appropriate).toBe(false); - expect(report.issues.length).toBeGreaterThan(0); - }); - - it('should detect multiple accent color usages', () => { - const css = ` - .button { color: #8aa2ff; } - .link:hover { color: var(--color-accent); } - .border { border-color: #8aa2ff; } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages).toHaveLength(3); - }); - - it('should handle CSS without accent color', () => { - const css = ` - .text { - color: #ffffff; - } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages).toHaveLength(0); - expect(report.issues).toHaveLength(0); - }); - - it('should be case-insensitive for color detection', () => { - const css = ` - .button { - background-color: #8AA2FF; - } - `; - - const report = optimizer.analyzeAccentUsage(css); - - expect(report.usages).toHaveLength(1); - }); - }); -}); diff --git a/lib/ui-analysis/accessibility-checker.ts b/lib/ui-analysis/accessibility-checker.ts deleted file mode 100644 index 47ed014..0000000 --- a/lib/ui-analysis/accessibility-checker.ts +++ /dev/null @@ -1,456 +0,0 @@ -/** - * Accessibility Checker - Automated accessibility audit system using axe-core - * - * This module integrates axe-core for WCAG validation and provides custom - * accessibility checks for keyboard navigation, focus indicators, and more. - */ - -import { chromium, Browser, Page, BrowserContext } from 'playwright'; -import type { - A11yReport, - A11yViolation, - A11yPass, - A11yIncomplete, - ViolationNode, - KeyboardIssue, - FocusIssue, - ContrastIssue, - MotionIssue, -} from './types'; - -// ============================================================================ -// Accessibility Checker Class -// ============================================================================ - -export class AccessibilityChecker { - private browser: Browser | null = null; - private context: BrowserContext | null = null; - private wcagLevel: 'A' | 'AA' | 'AAA'; - - constructor(wcagLevel: 'A' | 'AA' | 'AAA' = 'AA') { - this.wcagLevel = wcagLevel; - } - - /** - * Initialize browser instance - */ - async initialize(): Promise { - if (!this.browser) { - this.browser = await chromium.launch({ - headless: true, - }); - this.context = await this.browser.newContext(); - } - } - - /** - * Close browser instance - */ - async close(): Promise { - if (this.context) { - await this.context.close(); - this.context = null; - } - if (this.browser) { - await this.browser.close(); - this.browser = null; - } - } - - /** - * Run comprehensive accessibility audit on a page - */ - async auditPage(url: string): Promise { - await this.initialize(); - - if (!this.context) { - throw new Error('Browser context not initialized'); - } - - const page = await this.context.newPage(); - - try { - // Navigate to URL - await page.goto(url, { waitUntil: 'networkidle' }); - - // Wait for page to stabilize - await page.waitForTimeout(500); - - // Inject axe-core library - await this.injectAxeCore(page); - - // Run axe-core audit - const axeResults = await this.runAxeCore(page); - - // Calculate accessibility score - const score = this.calculateScore(axeResults.violations, axeResults.passes); - - return { - url, - violations: axeResults.violations, - passes: axeResults.passes, - incomplete: axeResults.incomplete, - wcagLevel: this.wcagLevel, - score, - }; - } finally { - await page.close(); - } - } - - /** - * Check keyboard navigation accessibility - */ - async checkKeyboardNav(page: Page): Promise { - const issues: KeyboardIssue[] = []; - - // Get all interactive elements - const interactiveElements = await page.$$('button, a, input, select, textarea, [role="button"], [tabindex]'); - - for (const element of interactiveElements) { - // Check if element is focusable - const isFocusable = await element.evaluate((el) => { - const tabIndex = el.getAttribute('tabindex'); - if (tabIndex === '-1') return false; - - // Check if element is naturally focusable or has tabindex >= 0 - const focusableElements = ['button', 'a', 'input', 'select', 'textarea']; - return focusableElements.includes(el.tagName.toLowerCase()) || (tabIndex !== null && parseInt(tabIndex) >= 0); - }); - - if (!isFocusable) { - const selector = await element.evaluate((el) => { - const id = el.id ? `#${el.id}` : ''; - const classes = el.className ? `.${el.className.toString().split(' ').join('.')}` : ''; - return `${el.tagName.toLowerCase()}${id}${classes}`; - }); - - issues.push({ - element: selector, - issue: 'not-focusable', - recommendation: 'Add tabindex="0" or use a naturally focusable element', - }); - } - } - - return issues; - } - - /** - * Check focus indicators - */ - async checkFocusIndicators(page: Page): Promise { - const issues: FocusIssue[] = []; - - // Get all focusable elements - const focusableElements = await page.$$('button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])'); - - for (const element of focusableElements) { - // Focus the element - await element.focus(); - - // Get computed outline style - const outlineInfo = await element.evaluate((el) => { - const styles = window.getComputedStyle(el); - return { - outline: styles.outline, - outlineWidth: styles.outlineWidth, - outlineStyle: styles.outlineStyle, - outlineOffset: styles.outlineOffset, - }; - }); - - // Check if outline meets requirements (2px solid with 2px offset) - const hasValidOutline = this.validateFocusOutline(outlineInfo); - - if (!hasValidOutline) { - const selector = await element.evaluate((el) => { - const id = el.id ? `#${el.id}` : ''; - const classes = el.className ? `.${el.className.toString().split(' ').join('.')}` : ''; - return `${el.tagName.toLowerCase()}${id}${classes}`; - }); - - issues.push({ - element: selector, - currentOutline: outlineInfo.outline, - requiredOutline: '2px solid with 2px offset', - }); - } - } - - return issues; - } - - /** - * Check color contrast ratios - */ - async checkColorContrast(page: Page): Promise { - const issues: ContrastIssue[] = []; - - // Get all text elements - const textElements = await page.$$('p, h1, h2, h3, h4, h5, h6, span, a, button, label, li'); - - for (const element of textElements) { - const contrastInfo = await element.evaluate((el) => { - const styles = window.getComputedStyle(el); - const foreground = styles.color; - const background = styles.backgroundColor; - const fontSize = parseFloat(styles.fontSize); - const fontWeight = styles.fontWeight; - - return { - foreground, - background, - fontSize, - fontWeight, - }; - }); - - // Calculate contrast ratio - const ratio = this.calculateContrastRatio(contrastInfo.foreground, contrastInfo.background); - - // Determine required ratio based on text size - const isLargeText = contrastInfo.fontSize >= 18 || (contrastInfo.fontSize >= 14 && parseInt(contrastInfo.fontWeight) >= 700); - const requiredRatio = isLargeText ? 3.0 : 4.5; - - if (ratio < requiredRatio) { - const selector = await element.evaluate((el) => { - const id = el.id ? `#${el.id}` : ''; - const classes = el.className ? `.${el.className.toString().split(' ').join('.')}` : ''; - return `${el.tagName.toLowerCase()}${id}${classes}`; - }); - - issues.push({ - element: selector, - foreground: contrastInfo.foreground, - background: contrastInfo.background, - ratio, - requiredRatio, - wcagLevel: 'AA', - }); - } - } - - return issues; - } - - /** - * Check reduced motion support - */ - async checkReducedMotion(): Promise { - // This would require analyzing CSS files for @media (prefers-reduced-motion) - // For now, return empty array as placeholder - // Full implementation would parse CSS and check for proper media query usage - return []; - } - - /** - * Inject axe-core library into page - */ - private async injectAxeCore(page: Page): Promise { - // Inject axe-core from node_modules - const axeCorePath = require.resolve('axe-core'); - const axeSource = require('fs').readFileSync(axeCorePath, 'utf8'); - await page.evaluate(axeSource); - } - - /** - * Run axe-core audit - */ - private async runAxeCore(page: Page): Promise<{ - violations: A11yViolation[]; - passes: A11yPass[]; - incomplete: A11yIncomplete[]; - }> { - const results = await page.evaluate((wcagLevel) => { - // @ts-ignore - axe is injected globally - return window.axe.run({ - runOnly: { - type: 'tag', - values: [`wcag2${wcagLevel.toLowerCase()}`, 'best-practice'], - }, - }); - }, this.wcagLevel); - - return { - violations: results.violations.map(this.mapAxeViolation), - passes: results.passes.map(this.mapAxePass), - incomplete: results.incomplete.map(this.mapAxeIncomplete), - }; - } - - /** - * Map axe-core violation to our format - */ - private mapAxeViolation(violation: any): A11yViolation { - return { - id: violation.id, - impact: violation.impact as 'critical' | 'serious' | 'moderate' | 'minor', - description: violation.description, - help: violation.help, - helpUrl: violation.helpUrl, - nodes: violation.nodes.map((node: any) => ({ - html: node.html, - target: node.target, - failureSummary: node.failureSummary || '', - })), - }; - } - - /** - * Map axe-core pass to our format - */ - private mapAxePass(pass: any): A11yPass { - return { - id: pass.id, - description: pass.description, - }; - } - - /** - * Map axe-core incomplete to our format - */ - private mapAxeIncomplete(incomplete: any): A11yIncomplete { - return { - id: incomplete.id, - description: incomplete.description, - nodes: incomplete.nodes.map((node: any) => ({ - html: node.html, - target: node.target, - failureSummary: node.failureSummary || '', - })), - }; - } - - /** - * Calculate accessibility score (0-100) - */ - private calculateScore(violations: A11yViolation[], passes: A11yPass[]): number { - // Weight violations by impact - const impactWeights = { - critical: 10, - serious: 7, - moderate: 4, - minor: 1, - }; - - const totalViolationWeight = violations.reduce((sum, v) => { - return sum + (impactWeights[v.impact] * v.nodes.length); - }, 0); - - const totalPasses = passes.length; - - // Calculate score: more passes and fewer violations = higher score - if (totalPasses === 0 && totalViolationWeight === 0) return 100; - if (totalPasses === 0) return 0; - - const score = Math.max(0, 100 - (totalViolationWeight / totalPasses) * 100); - return Math.round(score); - } - - /** - * Validate focus outline meets requirements - */ - private validateFocusOutline(outlineInfo: { - outline: string; - outlineWidth: string; - outlineStyle: string; - outlineOffset: string; - }): boolean { - // Check for 2px width - const widthMatch = outlineInfo.outlineWidth.includes('2px'); - - // Check for solid style - const styleMatch = outlineInfo.outlineStyle === 'solid' || outlineInfo.outline.includes('solid'); - - // Check for 2px offset - const offsetMatch = outlineInfo.outlineOffset.includes('2px'); - - // Check if outline is not 'none' - const notNone = outlineInfo.outline !== 'none' && outlineInfo.outlineStyle !== 'none'; - - return widthMatch && styleMatch && offsetMatch && notNone; - } - - /** - * Calculate contrast ratio between two colors - */ - private calculateContrastRatio(foreground: string, background: string): number { - const fgLuminance = this.getRelativeLuminance(foreground); - const bgLuminance = this.getRelativeLuminance(background); - - const lighter = Math.max(fgLuminance, bgLuminance); - const darker = Math.min(fgLuminance, bgLuminance); - - return (lighter + 0.05) / (darker + 0.05); - } - - /** - * Get relative luminance of a color - */ - private getRelativeLuminance(color: string): number { - // Parse RGB values from color string - const rgb = this.parseColor(color); - if (!rgb) return 0; - - // Convert to relative luminance - const [r, g, b] = rgb.map((val) => { - const sRGB = val / 255; - return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4); - }); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - } - - /** - * Parse color string to RGB values - */ - private parseColor(color: string): [number, number, number] | null { - // Handle rgb/rgba format - const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); - if (rgbMatch) { - return [parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3])]; - } - - // Handle hex format - const hexMatch = color.match(/#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/); - if (hexMatch) { - return [ - parseInt(hexMatch[1], 16), - parseInt(hexMatch[2], 16), - parseInt(hexMatch[3], 16), - ]; - } - - return null; - } -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -/** - * Create an accessibility checker with default settings - */ -export function createAccessibilityChecker( - wcagLevel?: 'A' | 'AA' | 'AAA' -): AccessibilityChecker { - return new AccessibilityChecker(wcagLevel); -} - -/** - * Quick audit for a single URL - */ -export async function auditAccessibility( - url: string, - wcagLevel?: 'A' | 'AA' | 'AAA' -): Promise { - const checker = createAccessibilityChecker(wcagLevel); - try { - const report = await checker.auditPage(url); - return report; - } finally { - await checker.close(); - } -} diff --git a/lib/ui-analysis/analyzer.ts b/lib/ui-analysis/analyzer.ts deleted file mode 100644 index a11f358..0000000 --- a/lib/ui-analysis/analyzer.ts +++ /dev/null @@ -1,819 +0,0 @@ -/** - * UI Analyzer - Core static analysis engine for detecting layout issues - * - * This module scans React/TSX components for layout issues including: - * - Overflow problems - * - Misalignment - * - Spacing inconsistencies - * - Z-index conflicts - * - Hardcoded colors - * - Missing responsive variants - * - Small touch targets - * - Insufficient contrast - */ - -import * as ts from 'typescript'; -import { readFileSync, readdirSync, statSync, watch } from 'fs'; -import { join, extname } from 'path'; -import type { LayoutIssue, AnalysisReport, AnalysisSummary, AnalysisError } from './types'; - -// ============================================================================ -// UIAnalyzer Class -// ============================================================================ - -export class UIAnalyzer { - private errors: AnalysisError[] = []; - private zIndexRegistry: Map> = new Map(); - - /** - * Scan a single component file for layout issues - */ - scanComponent(filePath: string): LayoutIssue[] { - const issues: LayoutIssue[] = []; - - try { - // Read file content - const sourceCode = readFileSync(filePath, 'utf-8'); - - // Parse TSX file using TypeScript Compiler API - const sourceFile = ts.createSourceFile( - filePath, - sourceCode, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TSX - ); - - // Extract JSX elements and analyze - this.visitNode(sourceFile, issues, filePath, sourceCode); - } catch (error) { - this.errors.push({ - phase: 'parsing', - file: filePath, - error: error as Error, - recoverable: true, - }); - console.warn(`Failed to parse ${filePath}:`, (error as Error).message); - } - - return issues; - } - - /** - * Scan a directory recursively for all component files - */ - scanDirectory(dirPath: string): AnalysisReport { - const startTime = Date.now(); - const allIssues: LayoutIssue[] = []; - const filesScanned: string[] = []; - - // Clear z-index registry for fresh scan - this.zIndexRegistry.clear(); - - try { - const componentFiles = this.findComponentFiles(dirPath); - - for (const filePath of componentFiles) { - const issues = this.scanComponent(filePath); - allIssues.push(...issues); - filesScanned.push(filePath); - } - - // Detect z-index conflicts across all scanned components - const zIndexConflicts = this.detectZIndexConflicts(); - allIssues.push(...zIndexConflicts); - } catch (error) { - this.errors.push({ - phase: 'analysis', - file: dirPath, - error: error as Error, - recoverable: false, - }); - console.error(`Failed to scan directory ${dirPath}:`, (error as Error).message); - } - - const duration = Date.now() - startTime; - const summary = this.generateSummary(allIssues, filesScanned.length); - - return { - timestamp: new Date().toISOString(), - duration, - summary, - issues: allIssues, - }; - } - - /** - * Watch mode - monitor files for changes and trigger re-analysis - */ - watchMode(dirPath: string, callback: (issues: LayoutIssue[]) => void): void { - console.log(`Watching ${dirPath} for changes...`); - - watch(dirPath, { recursive: true }, (eventType, filename) => { - if (!filename) return; - - const filePath = join(dirPath, filename); - - // Only process component files - if (this.isComponentFile(filePath)) { - console.log(`File changed: ${filePath}`); - - try { - const issues = this.scanComponent(filePath); - callback(issues); - } catch (error) { - console.error(`Error scanning ${filePath}:`, (error as Error).message); - } - } - }); - } - - /** - * Get accumulated errors from analysis - */ - getErrors(): AnalysisError[] { - return this.errors; - } - - /** - * Clear accumulated errors - */ - clearErrors(): void { - this.errors = []; - } - - // ========================================================================== - // Private Helper Methods - // ========================================================================== - - /** - * Recursively visit AST nodes to extract JSX elements - */ - private visitNode( - node: ts.Node, - issues: LayoutIssue[], - filePath: string, - sourceCode: string - ): void { - // Check if node is a JSX element - if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) { - this.analyzeJsxElement(node, issues, filePath, sourceCode); - } - - // Recursively visit child nodes - ts.forEachChild(node, (child) => this.visitNode(child, issues, filePath, sourceCode)); - } - - /** - * Analyze a JSX element for layout issues - */ - private analyzeJsxElement( - node: ts.JsxElement | ts.JsxSelfClosingElement, - issues: LayoutIssue[], - filePath: string, - sourceCode: string - ): void { - // Get opening element (handles both JsxElement and JsxSelfClosingElement) - const openingElement = ts.isJsxElement(node) - ? node.openingElement - : node; - - // Extract className attribute - const classNameAttr = this.getClassNameAttribute(openingElement); - - if (classNameAttr) { - const className = this.getClassNameValue(classNameAttr); - const { line, character } = this.getPosition(node, sourceCode); - - // Analyze className for issues - this.analyzeClassName(className, filePath, line, character, issues); - } - - // Extract style attribute for hardcoded values - const styleAttr = this.getStyleAttribute(openingElement); - - if (styleAttr) { - const { line, character } = this.getPosition(node, sourceCode); - this.analyzeStyleAttribute(styleAttr, filePath, line, character, issues); - } - } - - /** - * Get className attribute from JSX element - */ - private getClassNameAttribute( - element: ts.JsxOpeningElement | ts.JsxSelfClosingElement - ): ts.JsxAttribute | undefined { - return element.attributes.properties.find( - (prop): prop is ts.JsxAttribute => - ts.isJsxAttribute(prop) && - prop.name.getText() === 'className' - ); - } - - /** - * Get style attribute from JSX element - */ - private getStyleAttribute( - element: ts.JsxOpeningElement | ts.JsxSelfClosingElement - ): ts.JsxAttribute | undefined { - return element.attributes.properties.find( - (prop): prop is ts.JsxAttribute => - ts.isJsxAttribute(prop) && - prop.name.getText() === 'style' - ); - } - - /** - * Extract className value from attribute - */ - private getClassNameValue(attr: ts.JsxAttribute): string { - if (!attr.initializer) return ''; - - if (ts.isStringLiteral(attr.initializer)) { - return attr.initializer.text; - } - - if (ts.isJsxExpression(attr.initializer) && attr.initializer.expression) { - // Handle template literals and string concatenation - return attr.initializer.expression.getText(); - } - - return ''; - } - - /** - * Get line and column position of a node - */ - private getPosition(node: ts.Node, sourceCode: string): { line: number; character: number } { - const sourceFile = node.getSourceFile(); - const pos = sourceFile.getLineAndCharacterOfPosition(node.getStart()); - - return { - line: pos.line + 1, // Convert to 1-indexed - character: pos.character + 1, - }; - } - - /** - * Analyze className string for layout issues - */ - private analyzeClassName( - className: string, - filePath: string, - lineNumber: number, - columnNumber: number, - issues: LayoutIssue[] - ): void { - // Split className into individual classes - const classes = className.split(/\s+/).filter(Boolean); - - for (const cls of classes) { - // ======================================================================== - // OVERFLOW DETECTION (Enhanced) - // ======================================================================== - - // Check for overflow-hidden without constraints - if (cls.includes('overflow-hidden') && !this.hasConstraints(classes)) { - const severity = this.determineOverflowSeverity(classes); - issues.push({ - type: 'overflow', - severity, - filePath, - lineNumber, - columnNumber, - message: 'overflow-hidden used without explicit width/height constraints', - suggestion: 'Add w-* or h-* classes to constrain dimensions', - autoFixable: false, - }); - } - - // Check for overflow-auto/scroll without max-height - if ((cls === 'overflow-auto' || cls === 'overflow-scroll' || cls === 'overflow-y-auto' || cls === 'overflow-y-scroll') - && !this.hasMaxHeight(classes)) { - issues.push({ - type: 'overflow', - severity: 'moderate', - filePath, - lineNumber, - columnNumber, - message: `${cls} used without max-height constraint - may cause infinite expansion`, - suggestion: 'Add max-h-* class to limit scrollable area', - autoFixable: false, - }); - } - - // Check for overflow-x-hidden which often indicates responsive issues - if (cls === 'overflow-x-hidden') { - issues.push({ - type: 'overflow', - severity: 'critical', - filePath, - lineNumber, - columnNumber, - message: 'overflow-x-hidden may hide horizontal scroll issues instead of fixing them', - suggestion: 'Fix the root cause of horizontal overflow instead of hiding it', - autoFixable: false, - }); - } - - // ======================================================================== - // SPACING INCONSISTENCY DETECTION (Enhanced) - // ======================================================================== - - // Check for arbitrary spacing values - if (this.isArbitrarySpacing(cls)) { - const suggestedValue = this.suggestSpacingValue(cls); - issues.push({ - type: 'spacing-inconsistency', - severity: 'minor', - filePath, - lineNumber, - columnNumber, - message: `Arbitrary spacing value detected: ${cls}`, - suggestion: suggestedValue - ? `Use design system value: ${suggestedValue}` - : 'Use design system spacing scale (0.5rem, 0.75rem, 1rem, 1.5rem, 2rem)', - autoFixable: false, - }); - } - - // Check for non-standard spacing utilities - if (this.isNonStandardSpacing(cls)) { - issues.push({ - type: 'spacing-inconsistency', - severity: 'minor', - filePath, - lineNumber, - columnNumber, - message: `Non-standard spacing class: ${cls}`, - suggestion: 'Use standard spacing scale (p-1, p-2, p-4, p-6, p-8, etc.)', - autoFixable: false, - }); - } - - // ======================================================================== - // Z-INDEX CONFLICT DETECTION (New) - // ======================================================================== - - // Check for z-index usage - if (this.isZIndexClass(cls)) { - const zValue = this.extractZIndexValue(cls); - if (zValue !== null) { - this.registerZIndex(filePath, lineNumber, zValue); - - // Check for problematic z-index values - if (zValue > 9999) { - issues.push({ - type: 'z-index-conflict', - severity: 'moderate', - filePath, - lineNumber, - columnNumber, - message: `Extremely high z-index value: ${zValue}`, - suggestion: 'Use a layered z-index system (0-10 for content, 10-50 for overlays, 50-100 for modals)', - autoFixable: false, - }); - } - - // Check for arbitrary z-index values - if (cls.includes('[') && cls.includes(']')) { - issues.push({ - type: 'z-index-conflict', - severity: 'minor', - filePath, - lineNumber, - columnNumber, - message: `Arbitrary z-index value: ${cls}`, - suggestion: 'Use predefined z-index utilities (z-0, z-10, z-20, z-30, z-40, z-50)', - autoFixable: false, - }); - } - } - } - - // ======================================================================== - // MISSING RESPONSIVE VARIANTS (Enhanced) - // ======================================================================== - - // Check for missing responsive variants on sizing classes - if (this.isSizingClass(cls) && !this.hasResponsiveVariant(classes, cls)) { - const severity = this.determineResponsiveSeverity(cls); - issues.push({ - type: 'missing-responsive', - severity, - filePath, - lineNumber, - columnNumber, - message: `Sizing class ${cls} lacks responsive variants`, - suggestion: 'Add responsive variants (sm:, md:, lg:, xl:)', - autoFixable: false, - }); - } - } - - // ======================================================================== - // MISALIGNMENT DETECTION (Enhanced) - // ======================================================================== - - // Check for flex containers without alignment - if (this.isFlexContainer(classes) && !this.hasAlignment(classes)) { - issues.push({ - type: 'misalignment', - severity: 'minor', - filePath, - lineNumber, - columnNumber, - message: 'Flex container without explicit alignment classes', - suggestion: 'Add items-center, items-start, items-end, or items-baseline', - autoFixable: false, - }); - } - - // Check for grid containers without gap - if (this.isGridContainer(classes) && !this.hasGap(classes)) { - issues.push({ - type: 'misalignment', - severity: 'minor', - filePath, - lineNumber, - columnNumber, - message: 'Grid container without gap spacing', - suggestion: 'Add gap-* class for consistent grid spacing', - autoFixable: false, - }); - } - - // Check for absolute positioning without explicit positioning classes - if (this.hasAbsolutePosition(classes) && !this.hasPositioningClasses(classes)) { - issues.push({ - type: 'misalignment', - severity: 'moderate', - filePath, - lineNumber, - columnNumber, - message: 'Absolute positioning without explicit top/right/bottom/left classes', - suggestion: 'Add positioning classes (top-*, right-*, bottom-*, left-*) or use inset-*', - autoFixable: false, - }); - } - } - - /** - * Analyze style attribute for hardcoded values - */ - private analyzeStyleAttribute( - attr: ts.JsxAttribute, - filePath: string, - lineNumber: number, - columnNumber: number, - issues: LayoutIssue[] - ): void { - const styleText = attr.getText(); - - // Check for hardcoded colors (hex, rgb, rgba) - const hexColorRegex = /#[0-9a-fA-F]{3,6}/g; - const rgbColorRegex = /rgba?\([^)]+\)/g; - - if (hexColorRegex.test(styleText) || rgbColorRegex.test(styleText)) { - issues.push({ - type: 'hardcoded-color', - severity: 'moderate', - filePath, - lineNumber, - columnNumber, - message: 'Hardcoded color value in style attribute', - suggestion: 'Use CSS custom properties from design system (var(--color-*))', - autoFixable: false, - }); - } - } - - /** - * Check if classes include width/height constraints - */ - private hasConstraints(classes: string[]): boolean { - return classes.some(cls => - cls.startsWith('w-') || - cls.startsWith('h-') || - cls.startsWith('max-w-') || - cls.startsWith('max-h-') || - cls.startsWith('min-w-') || - cls.startsWith('min-h-') - ); - } - - /** - * Check if classes include max-height constraint - */ - private hasMaxHeight(classes: string[]): boolean { - return classes.some(cls => - cls.startsWith('max-h-') || - cls.startsWith('h-') - ); - } - - /** - * Determine overflow severity based on context - */ - private determineOverflowSeverity(classes: string[]): 'critical' | 'moderate' | 'minor' { - // Critical if it's on a container that might affect layout - if (classes.some(cls => cls.includes('container') || cls.includes('wrapper'))) { - return 'critical'; - } - return 'moderate'; - } - - /** - * Check if class is an arbitrary spacing value - */ - private isArbitrarySpacing(cls: string): boolean { - // Match patterns like p-[13px], m-[2.5rem], gap-[15px] - const arbitraryPattern = /^(p|m|gap|space)-\[[\d.]+(?:px|rem|em)\]$/; - return arbitraryPattern.test(cls); - } - - /** - * Check if class is non-standard spacing (e.g., p-3, p-5, p-7, p-9, etc.) - */ - private isNonStandardSpacing(cls: string): boolean { - // Standard Tailwind spacing: 0, 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, etc. - // Non-standard would be odd numbers like 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 - const nonStandardPattern = /^(p|m|gap|space)-(3|5|7|9|11|13|15|17|19|21|23)$/; - return nonStandardPattern.test(cls); - } - - /** - * Suggest nearest spacing value from design scale - */ - private suggestSpacingValue(arbitraryClass: string): string | null { - // Extract the numeric value - const match = arbitraryClass.match(/\[([\d.]+)(?:px|rem|em)\]/); - if (!match) return null; - - const value = parseFloat(match[1]); - const unit = arbitraryClass.includes('rem') ? 'rem' : 'px'; - - // Design scale in rem: 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3 - // Convert to px (assuming 1rem = 16px): 8, 12, 16, 20, 24, 32, 40, 48 - const scaleInPx = [8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96]; - const scaleInRem = [0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4, 5, 6]; - - const scale = unit === 'rem' ? scaleInRem : scaleInPx; - - // Find nearest value - let nearest = scale[0]; - let minDiff = Math.abs(value - nearest); - - for (const scaleValue of scale) { - const diff = Math.abs(value - scaleValue); - if (diff < minDiff) { - minDiff = diff; - nearest = scaleValue; - } - } - - // Map to Tailwind class - const prefix = arbitraryClass.split('-')[0]; - const tailwindMap: Record = { - 0.5: '0.5', 0.75: '1', 1: '2', 1.25: '3', 1.5: '4', 2: '6', 2.5: '8', 3: '10', 4: '12', 5: '16', 6: '20' - }; - - if (unit === 'rem' && tailwindMap[nearest]) { - return `${prefix}-${tailwindMap[nearest]}`; - } - - return null; - } - - /** - * Check if class is a z-index class - */ - private isZIndexClass(cls: string): boolean { - return cls.startsWith('z-') || cls.startsWith('-z-'); - } - - /** - * Extract z-index numeric value from class - */ - private extractZIndexValue(cls: string): number | null { - // Handle arbitrary values like z-[999] - const arbitraryMatch = cls.match(/z-\[(-?\d+)\]/); - if (arbitraryMatch) { - return parseInt(arbitraryMatch[1], 10); - } - - // Handle standard Tailwind z-index values - const standardMatch = cls.match(/^-?z-(\d+)$/); - if (standardMatch) { - return parseInt(standardMatch[1], 10); - } - - // Handle named z-index values - const namedValues: Record = { - 'z-auto': 0, - 'z-0': 0, - 'z-10': 10, - 'z-20': 20, - 'z-30': 30, - 'z-40': 40, - 'z-50': 50, - }; - - return namedValues[cls] ?? null; - } - - /** - * Register z-index usage for conflict detection - */ - private registerZIndex(filePath: string, lineNumber: number, value: number): void { - const key = `z-${value}`; - if (!this.zIndexRegistry.has(key)) { - this.zIndexRegistry.set(key, []); - } - this.zIndexRegistry.get(key)!.push({ value, filePath, lineNumber }); - } - - /** - * Detect z-index conflicts across components - */ - detectZIndexConflicts(): LayoutIssue[] { - const conflicts: LayoutIssue[] = []; - - // Check for multiple components using the same high z-index - for (const [key, usages] of this.zIndexRegistry.entries()) { - if (usages.length > 1 && usages[0].value >= 40) { - for (const usage of usages) { - conflicts.push({ - type: 'z-index-conflict', - severity: 'moderate', - filePath: usage.filePath, - lineNumber: usage.lineNumber, - columnNumber: 0, - message: `Multiple components use z-index ${usage.value} - potential stacking conflict`, - suggestion: 'Review z-index hierarchy and ensure intentional stacking order', - autoFixable: false, - }); - } - } - } - - return conflicts; - } - - /** - * Check if class is a sizing class - */ - private isSizingClass(cls: string): boolean { - return cls.startsWith('w-') || - cls.startsWith('h-') || - cls.startsWith('text-'); - } - - /** - * Determine severity for missing responsive variants - */ - private determineResponsiveSeverity(cls: string): 'critical' | 'moderate' | 'minor' { - // Critical for width classes that might break mobile layout - if (cls.startsWith('w-') && !cls.includes('full') && !cls.includes('auto')) { - return 'critical'; - } - // Moderate for text sizing - if (cls.startsWith('text-')) { - return 'moderate'; - } - return 'minor'; - } - - /** - * Check if classes include responsive variants for a given class - */ - private hasResponsiveVariant(classes: string[], baseClass: string): boolean { - const responsivePrefixes = ['sm:', 'md:', 'lg:', 'xl:', '2xl:']; - const baseClassName = baseClass.split(':').pop() || baseClass; - - return classes.some(cls => - responsivePrefixes.some(prefix => cls.startsWith(prefix) && cls.includes(baseClassName)) - ); - } - - /** - * Check if classes define a flex container - */ - private isFlexContainer(classes: string[]): boolean { - return classes.some(cls => cls === 'flex' || cls.startsWith('flex-')); - } - - /** - * Check if classes define a grid container - */ - private isGridContainer(classes: string[]): boolean { - return classes.some(cls => cls === 'grid' || cls.startsWith('grid-')); - } - - /** - * Check if classes include alignment - */ - private hasAlignment(classes: string[]): boolean { - return classes.some(cls => - cls.startsWith('items-') || - cls.startsWith('justify-') || - cls.startsWith('content-') - ); - } - - /** - * Check if classes include gap spacing - */ - private hasGap(classes: string[]): boolean { - return classes.some(cls => cls.startsWith('gap-')); - } - - /** - * Check if classes include absolute positioning - */ - private hasAbsolutePosition(classes: string[]): boolean { - return classes.some(cls => cls === 'absolute'); - } - - /** - * Check if classes include positioning classes (top, right, bottom, left, inset) - */ - private hasPositioningClasses(classes: string[]): boolean { - return classes.some(cls => - cls.startsWith('top-') || - cls.startsWith('right-') || - cls.startsWith('bottom-') || - cls.startsWith('left-') || - cls.startsWith('inset-') - ); - } - - /** - * Find all component files in a directory recursively - */ - private findComponentFiles(dirPath: string): string[] { - const files: string[] = []; - - try { - const entries = readdirSync(dirPath); - - for (const entry of entries) { - const fullPath = join(dirPath, entry); - - try { - const stat = statSync(fullPath); - - if (stat.isDirectory()) { - // Skip node_modules and hidden directories - if (entry !== 'node_modules' && !entry.startsWith('.')) { - files.push(...this.findComponentFiles(fullPath)); - } - } else if (this.isComponentFile(fullPath)) { - files.push(fullPath); - } - } catch (error) { - console.warn(`Failed to stat ${fullPath}:`, (error as Error).message); - } - } - } catch (error) { - console.error(`Failed to read directory ${dirPath}:`, (error as Error).message); - } - - return files; - } - - /** - * Check if file is a component file (tsx, jsx) - */ - private isComponentFile(filePath: string): boolean { - const ext = extname(filePath); - return ext === '.tsx' || ext === '.jsx'; - } - - /** - * Generate analysis summary from issues - */ - private generateSummary(issues: LayoutIssue[], filesScanned: number): AnalysisSummary { - const criticalIssues = issues.filter(i => i.severity === 'critical').length; - const moderateIssues = issues.filter(i => i.severity === 'moderate').length; - const minorIssues = issues.filter(i => i.severity === 'minor').length; - - // Calculate compliance score (0-100) - // Weighted: critical = 10 points, moderate = 5 points, minor = 1 point - const totalPenalty = (criticalIssues * 10) + (moderateIssues * 5) + (minorIssues * 1); - const maxScore = 100; - const complianceScore = Math.max(0, maxScore - totalPenalty); - - return { - totalIssues: issues.length, - criticalIssues, - moderateIssues, - minorIssues, - filesScanned, - pagesAudited: 0, // Will be set by responsive validator - complianceScore, - }; - } -} - diff --git a/lib/ui-analysis/config.ts b/lib/ui-analysis/config.ts deleted file mode 100644 index 38c23b0..0000000 --- a/lib/ui-analysis/config.ts +++ /dev/null @@ -1,292 +0,0 @@ -/** - * Configuration schema and loader for UI analysis system - */ - -import { readFileSync, existsSync } from 'fs'; -import { join } from 'path'; -import type { UIAnalysisConfig, Breakpoint, RuleConfig, ReportingConfig } from './types'; - -// ============================================================================ -// Default Configuration -// ============================================================================ - -const DEFAULT_BREAKPOINTS: Breakpoint[] = [ - { name: 'mobile', width: 375, height: 667 }, - { name: 'tablet', width: 768, height: 1024 }, - { name: 'desktop', width: 1280, height: 800 }, - { name: 'wide', width: 1920, height: 1080 }, -]; - -const DEFAULT_RULES: RuleConfig = { - layout: { - checkOverflow: true, - checkAlignment: true, - checkSpacing: true, - checkZIndex: true, - }, - responsive: { - minTouchTargetSize: { width: 44, height: 44 }, - breakpoints: [640, 768, 1024, 1280], - allowHorizontalScroll: false, - }, - accessibility: { - wcagLevel: 'AA', - checkKeyboard: true, - checkFocus: true, - checkContrast: true, - checkReducedMotion: true, - }, - designSystem: { - enforceTokens: true, - allowedArbitraryValues: [], - strictMode: false, - }, -}; - -const DEFAULT_REPORTING: ReportingConfig = { - console: true, - overlay: true, - json: false, - outputPath: './ui-analysis-report.json', - minSeverity: 'minor', -}; - -export const DEFAULT_CONFIG: UIAnalysisConfig = { - enabled: true, - mode: 'development', - rules: DEFAULT_RULES, - breakpoints: DEFAULT_BREAKPOINTS, - reporting: DEFAULT_REPORTING, -}; - -// ============================================================================ -// Configuration Loader -// ============================================================================ - -export class ConfigLoader { - private config: UIAnalysisConfig; - - constructor(configPath?: string) { - this.config = this.loadConfig(configPath); - } - - /** - * Load configuration from file or use defaults - */ - private loadConfig(configPath?: string): UIAnalysisConfig { - const paths = configPath - ? [configPath] - : [ - join(process.cwd(), 'ui-analysis.config.json'), - join(process.cwd(), '.ui-analysis.json'), - join(process.cwd(), 'ui-analysis.config.js'), - ]; - - for (const path of paths) { - if (existsSync(path)) { - try { - const fileContent = readFileSync(path, 'utf-8'); - const userConfig = path.endsWith('.js') - ? require(path) - : JSON.parse(fileContent); - - return this.mergeConfig(DEFAULT_CONFIG, userConfig); - } catch (error) { - console.warn(`Failed to load config from ${path}:`, error); - console.warn('Using default configuration'); - } - } - } - - return DEFAULT_CONFIG; - } - - /** - * Deep merge user config with defaults - */ - private mergeConfig( - defaults: UIAnalysisConfig, - userConfig: Partial - ): UIAnalysisConfig { - return { - enabled: userConfig.enabled ?? defaults.enabled, - mode: userConfig.mode ?? defaults.mode, - rules: { - layout: { - ...defaults.rules.layout, - ...userConfig.rules?.layout, - }, - responsive: { - ...defaults.rules.responsive, - ...userConfig.rules?.responsive, - }, - accessibility: { - ...defaults.rules.accessibility, - ...userConfig.rules?.accessibility, - }, - designSystem: { - ...defaults.rules.designSystem, - ...userConfig.rules?.designSystem, - }, - }, - breakpoints: userConfig.breakpoints ?? defaults.breakpoints, - reporting: { - ...defaults.reporting, - ...userConfig.reporting, - }, - }; - } - - /** - * Get the loaded configuration - */ - getConfig(): UIAnalysisConfig { - return this.config; - } - - /** - * Update configuration at runtime - */ - updateConfig(updates: Partial): void { - this.config = this.mergeConfig(this.config, updates); - } - - /** - * Validate configuration - */ - validate(): { valid: boolean; errors: string[] } { - const errors: string[] = []; - - // Validate mode - if (!['development', 'ci', 'production'].includes(this.config.mode)) { - errors.push(`Invalid mode: ${this.config.mode}`); - } - - // Validate WCAG level - if (!['A', 'AA', 'AAA'].includes(this.config.rules.accessibility.wcagLevel)) { - errors.push(`Invalid WCAG level: ${this.config.rules.accessibility.wcagLevel}`); - } - - // Validate breakpoints - if (this.config.breakpoints.length === 0) { - errors.push('At least one breakpoint must be defined'); - } - - for (const bp of this.config.breakpoints) { - if (bp.width <= 0 || bp.height <= 0) { - errors.push(`Invalid breakpoint dimensions: ${bp.name}`); - } - } - - // Validate touch target size - const { minTouchTargetSize } = this.config.rules.responsive; - if (minTouchTargetSize.width < 0 || minTouchTargetSize.height < 0) { - errors.push('Touch target size must be positive'); - } - - // Validate reporting config - if (this.config.reporting.json && !this.config.reporting.outputPath) { - errors.push('Output path required when JSON reporting is enabled'); - } - - // Validate severity - if (!['critical', 'moderate', 'minor'].includes(this.config.reporting.minSeverity)) { - errors.push(`Invalid severity level: ${this.config.reporting.minSeverity}`); - } - - return { - valid: errors.length === 0, - errors, - }; - } - - /** - * Get configuration for specific mode - */ - static forMode(mode: 'development' | 'ci' | 'production'): UIAnalysisConfig { - const config = { ...DEFAULT_CONFIG }; - config.mode = mode; - - // Adjust settings based on mode - switch (mode) { - case 'development': - config.reporting.console = true; - config.reporting.overlay = true; - config.reporting.json = false; - break; - - case 'ci': - config.reporting.console = true; - config.reporting.overlay = false; - config.reporting.json = true; - config.reporting.minSeverity = 'critical'; - config.rules.designSystem.strictMode = true; - break; - - case 'production': - config.enabled = false; - break; - } - - return config; - } -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -/** - * Create a default configuration file - */ -export function createDefaultConfigFile(path: string = 'ui-analysis.config.json'): void { - const fs = require('fs'); - const configJson = JSON.stringify(DEFAULT_CONFIG, null, 2); - fs.writeFileSync(path, configJson, 'utf-8'); - console.log(`Created default configuration file at ${path}`); -} - -/** - * Load configuration from environment variables - */ -export function loadConfigFromEnv(): Partial { - const config: Partial = {}; - - if (process.env.UI_ANALYSIS_ENABLED !== undefined) { - config.enabled = process.env.UI_ANALYSIS_ENABLED === 'true'; - } - - if (process.env.UI_ANALYSIS_MODE) { - config.mode = process.env.UI_ANALYSIS_MODE as 'development' | 'ci' | 'production'; - } - - if (process.env.UI_ANALYSIS_MIN_SEVERITY) { - config.reporting = { - ...DEFAULT_REPORTING, - minSeverity: process.env.UI_ANALYSIS_MIN_SEVERITY as 'critical' | 'moderate' | 'minor', - }; - } - - return config; -} - -/** - * Get configuration with environment overrides - */ -export function getConfig(configPath?: string): UIAnalysisConfig { - const loader = new ConfigLoader(configPath); - const envConfig = loadConfigFromEnv(); - - if (Object.keys(envConfig).length > 0) { - loader.updateConfig(envConfig); - } - - const validation = loader.validate(); - if (!validation.valid) { - console.error('Configuration validation failed:'); - validation.errors.forEach(error => console.error(` - ${error}`)); - throw new Error('Invalid configuration'); - } - - return loader.getConfig(); -} diff --git a/lib/ui-analysis/design-system-validator.ts b/lib/ui-analysis/design-system-validator.ts deleted file mode 100644 index 0d0c98b..0000000 --- a/lib/ui-analysis/design-system-validator.ts +++ /dev/null @@ -1,467 +0,0 @@ -/** - * Design System Validator - * - * Token usage checker that ensures consistent application of CSS custom properties - * and design system values across the codebase. - * - * Validates: - * - Color token usage (no hardcoded hex/rgb values) - * - Spacing scale compliance - * - Border radius token usage - * - Font family token usage - */ - -import * as ts from 'typescript'; -import { readFileSync } from 'fs'; -import { join } from 'path'; -import type { - ColorViolation, - SpacingViolation, - RadiusViolation, - FontViolation, - DesignSystemReport, - CodeLocation, -} from './types'; - -// ============================================================================ -// Design System Tokens -// ============================================================================ - -const COLOR_TOKENS = [ - 'var(--color-page)', - 'var(--color-surface)', - 'var(--color-surface-subtle)', - 'var(--color-surface-muted)', - 'var(--color-text)', - 'var(--color-text-muted)', - 'var(--color-text-soft)', - 'var(--color-accent)', - 'var(--color-border)', - 'var(--color-border-strong)', -]; - -const SPACING_SCALE = [0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3]; // in rem - -const RADIUS_TOKENS = { - 'var(--radius-sm)': 10, - 'var(--radius-md)': 16, - 'var(--radius-lg)': 24, -} as const; - -const FONT_TOKENS = [ - 'var(--font-sans)', - 'var(--font-mono)', - 'var(--font-display)', -]; - -// ============================================================================ -// Regex Patterns -// ============================================================================ - -const HEX_COLOR_PATTERN = /#[0-9a-fA-F]{3,6}\b/g; -const RGB_COLOR_PATTERN = /rgba?\s*\([^)]+\)/g; -const ARBITRARY_SPACING_PATTERN = /\[[\d.]+(?:px|rem)\]/g; -const ARBITRARY_RADIUS_PATTERN = /rounded-\[[\d.]+(?:px|rem)\]/g; - -// ============================================================================ -// Design System Validator -// ============================================================================ - -export class DesignSystemValidator { - private colorViolations: ColorViolation[] = []; - private spacingViolations: SpacingViolation[] = []; - private radiusViolations: RadiusViolation[] = []; - private fontViolations: FontViolation[] = []; - - /** - * Validate colors in CSS and TSX files - */ - validateColors(filePath: string, content: string): ColorViolation[] { - const violations: ColorViolation[] = []; - const lines = content.split('\n'); - - // Check for hardcoded hex colors - lines.forEach((line, index) => { - const hexMatches = line.matchAll(HEX_COLOR_PATTERN); - for (const match of hexMatches) { - if (match.index !== undefined) { - violations.push({ - type: 'hardcoded-hex', - value: match[0], - location: { - filePath, - lineNumber: index + 1, - columnNumber: match.index + 1, - snippet: line.trim(), - }, - suggestedToken: this.findNearestColorToken(match[0]), - }); - } - } - - // Check for hardcoded rgb/rgba colors - const rgbMatches = line.matchAll(RGB_COLOR_PATTERN); - for (const match of rgbMatches) { - if (match.index !== undefined) { - violations.push({ - type: 'hardcoded-rgb', - value: match[0], - location: { - filePath, - lineNumber: index + 1, - columnNumber: match.index + 1, - snippet: line.trim(), - }, - suggestedToken: 'var(--color-*)', - }); - } - } - }); - - // Store violations in class property - this.colorViolations.push(...violations); - return violations; - } - - /** - * Validate spacing values in TSX files - */ - validateSpacing(filePath: string, content: string): SpacingViolation[] { - const violations: SpacingViolation[] = []; - const lines = content.split('\n'); - - lines.forEach((line, index) => { - // Check for arbitrary Tailwind spacing values - const matches = line.matchAll(ARBITRARY_SPACING_PATTERN); - for (const match of matches) { - if (match.index !== undefined) { - const value = match[0]; - const numericValue = this.extractNumericValue(value); - - if (numericValue !== null && !this.isInSpacingScale(numericValue)) { - violations.push({ - type: 'arbitrary-value', - value, - location: { - filePath, - lineNumber: index + 1, - columnNumber: match.index + 1, - snippet: line.trim(), - }, - suggestedValue: this.findNearestSpacingValue(numericValue), - }); - } - } - } - }); - - // Store violations in class property - this.spacingViolations.push(...violations); - return violations; - } - - /** - * Validate border radius values in TSX files - */ - validateBorderRadius(filePath: string, content: string): RadiusViolation[] { - const violations: RadiusViolation[] = []; - const lines = content.split('\n'); - - lines.forEach((line, index) => { - // Check for arbitrary border radius values - const matches = line.matchAll(ARBITRARY_RADIUS_PATTERN); - for (const match of matches) { - if (match.index !== undefined) { - const value = match[0]; - const numericValue = this.extractNumericValue(value); - - if (numericValue !== null) { - violations.push({ - type: 'hardcoded-radius', - value, - location: { - filePath, - lineNumber: index + 1, - columnNumber: match.index + 1, - snippet: line.trim(), - }, - suggestedToken: this.findNearestRadiusToken(numericValue), - }); - } - } - } - }); - - // Store violations in class property - this.radiusViolations.push(...violations); - return violations; - } - - /** - * Validate font family declarations - */ - validateFonts(filePath: string, content: string): FontViolation[] { - const violations: FontViolation[] = []; - const lines = content.split('\n'); - - lines.forEach((line, index) => { - // Check for hardcoded font-family declarations - const fontFamilyPattern = /font-family\s*:\s*['"]?([^;'"]+)['"]?/gi; - const matches = line.matchAll(fontFamilyPattern); - - for (const match of matches) { - if (match.index !== undefined) { - const value = match[1].trim(); - - // Skip if already using a CSS variable - if (value.startsWith('var(--font-')) { - continue; - } - - violations.push({ - type: 'hardcoded-font', - value, - location: { - filePath, - lineNumber: index + 1, - columnNumber: match.index + 1, - snippet: line.trim(), - }, - suggestedToken: this.findNearestFontToken(value), - }); - } - } - }); - - // Store violations in class property - this.fontViolations.push(...violations); - return violations; - } - - /** - * Validate a single file for all design system violations - */ - validateFile(filePath: string): void { - try { - const content = readFileSync(filePath, 'utf-8'); - - // Run all validations - this.colorViolations.push(...this.validateColors(filePath, content)); - this.spacingViolations.push(...this.validateSpacing(filePath, content)); - this.radiusViolations.push(...this.validateBorderRadius(filePath, content)); - this.fontViolations.push(...this.validateFonts(filePath, content)); - } catch (error) { - console.warn(`Failed to validate file ${filePath}:`, error); - } - } - - /** - * Generate comprehensive design system report - */ - generateReport(): DesignSystemReport { - const totalViolations = - this.colorViolations.length + - this.spacingViolations.length + - this.radiusViolations.length + - this.fontViolations.length; - - // Calculate compliance score (0-100) - // Assuming a baseline of 100 violations = 0 score - const complianceScore = Math.max(0, Math.min(100, 100 - totalViolations)); - - return { - totalViolations, - colorViolations: this.colorViolations, - spacingViolations: this.spacingViolations, - radiusViolations: this.radiusViolations, - fontViolations: this.fontViolations, - complianceScore, - }; - } - - /** - * Reset all violations (useful for re-running validation) - */ - reset(): void { - this.colorViolations = []; - this.spacingViolations = []; - this.radiusViolations = []; - this.fontViolations = []; - } - - // ============================================================================ - // Helper Methods - // ============================================================================ - - /** - * Find the nearest color token for a given hex color - */ - private findNearestColorToken(hexColor: string): string { - // Map common colors to tokens - const colorMap: Record = { - '#8aa2ff': 'var(--color-accent)', - '#0b0d10': 'var(--color-page)', - '#f3f5f7': 'var(--color-text)', - }; - - const normalized = hexColor.toLowerCase(); - return colorMap[normalized] || 'var(--color-*)'; - } - - /** - * Extract numeric value from arbitrary Tailwind value - */ - private extractNumericValue(value: string): number | null { - const match = value.match(/([\d.]+)(px|rem)/); - if (!match) return null; - - const num = parseFloat(match[1]); - const unit = match[2]; - - // Convert px to rem (assuming 16px = 1rem) - return unit === 'px' ? num / 16 : num; - } - - /** - * Check if a value is in the spacing scale - */ - private isInSpacingScale(value: number): boolean { - return SPACING_SCALE.some(scale => Math.abs(scale - value) < 0.01); - } - - /** - * Find the nearest spacing value from the design scale - */ - private findNearestSpacingValue(value: number): string { - let nearest = SPACING_SCALE[0]; - let minDiff = Math.abs(value - nearest); - - for (const scale of SPACING_SCALE) { - const diff = Math.abs(value - scale); - if (diff < minDiff) { - minDiff = diff; - nearest = scale; - } - } - - return `${nearest}rem`; - } - - /** - * Find the nearest radius token for a given pixel value - */ - private findNearestRadiusToken( - value: number - ): 'var(--radius-sm)' | 'var(--radius-md)' | 'var(--radius-lg)' { - // Convert rem to px if needed (assuming 16px = 1rem) - const pxValue = value < 5 ? value * 16 : value; - - const radiusValues = { - 'var(--radius-sm)': 10, - 'var(--radius-md)': 16, - 'var(--radius-lg)': 24, - } as const; - - let nearest: keyof typeof radiusValues = 'var(--radius-sm)'; - let minDiff = Math.abs(pxValue - radiusValues[nearest]); - - for (const [token, tokenValue] of Object.entries(radiusValues)) { - const diff = Math.abs(pxValue - tokenValue); - if (diff < minDiff) { - minDiff = diff; - nearest = token as keyof typeof radiusValues; - } - } - - return nearest; - } - - /** - * Find the nearest font token based on font name - */ - private findNearestFontToken( - fontName: string - ): 'var(--font-sans)' | 'var(--font-mono)' | 'var(--font-display)' { - const lower = fontName.toLowerCase(); - - if (lower.includes('mono') || lower.includes('code')) { - return 'var(--font-mono)'; - } - - if (lower.includes('display') || lower.includes('heading')) { - return 'var(--font-display)'; - } - - return 'var(--font-sans)'; - } -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -/** - * Parse TypeScript/TSX file and extract style-related information - */ -export function parseTSXFile(filePath: string): ts.SourceFile { - const content = readFileSync(filePath, 'utf-8'); - return ts.createSourceFile( - filePath, - content, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TSX - ); -} - -/** - * Extract className attributes from JSX elements - */ -export function extractClassNames(sourceFile: ts.SourceFile): string[] { - const classNames: string[] = []; - - function visit(node: ts.Node) { - if (ts.isJsxAttribute(node)) { - const attrName = ts.isIdentifier(node.name) ? node.name.text : null; - if (attrName === 'className' && node.initializer && ts.isStringLiteral(node.initializer)) { - classNames.push(node.initializer.text); - } - } - ts.forEachChild(node, visit); - } - - visit(sourceFile); - return classNames; -} - -/** - * Extract style prop values from JSX elements - */ -export function extractStyleProps(sourceFile: ts.SourceFile): Record[] { - const styles: Record[] = []; - - function visit(node: ts.Node) { - if (ts.isJsxAttribute(node)) { - const attrName = ts.isIdentifier(node.name) ? node.name.text : null; - if (attrName === 'style' && node.initializer && ts.isJsxExpression(node.initializer)) { - const expression = node.initializer.expression; - if (expression && ts.isObjectLiteralExpression(expression)) { - const styleObj: Record = {}; - expression.properties.forEach(prop => { - if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) { - const key = prop.name.text; - if (ts.isStringLiteral(prop.initializer)) { - styleObj[key] = prop.initializer.text; - } - } - }); - styles.push(styleObj); - } - } - } - ts.forEachChild(node, visit); - } - - visit(sourceFile); - return styles; -} diff --git a/lib/ui-analysis/index.ts b/lib/ui-analysis/index.ts deleted file mode 100644 index 3240e97..0000000 --- a/lib/ui-analysis/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * UI Analysis System - Core Infrastructure - * - * This module provides the foundation for automated UI analysis, - * including layout issue detection, responsive validation, accessibility - * checking, and design system enforcement. - */ - -// Export all types -export * from './types'; - -// Export configuration utilities -export { - ConfigLoader, - DEFAULT_CONFIG, - createDefaultConfigFile, - loadConfigFromEnv, - getConfig, -} from './config'; - -// Export analyzer -export { UIAnalyzer } from './analyzer'; - -// Export responsive validator -export { - ResponsiveValidator, - STANDARD_BREAKPOINTS, - createResponsiveValidator, - testResponsive, -} from './responsive-validator'; - -// Export accessibility checker -export { - AccessibilityChecker, - createAccessibilityChecker, - auditAccessibility, -} from './accessibility-checker'; - -// Export visual hierarchy optimizer -export { VisualHierarchyOptimizer } from './visual-hierarchy-optimizer'; - -// Export design system validator -export { - DesignSystemValidator, - parseTSXFile, - extractClassNames, - extractStyleProps, -} from './design-system-validator'; diff --git a/lib/ui-analysis/responsive-validator.ts b/lib/ui-analysis/responsive-validator.ts deleted file mode 100644 index 3b5a2cd..0000000 --- a/lib/ui-analysis/responsive-validator.ts +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Responsive Validator - Runtime testing framework for cross-viewport validation - * - * This module uses Playwright to test layout behavior across different viewport sizes, - * validating touch targets, horizontal scroll, and responsive breakpoints. - */ - -import { chromium, Browser, Page, BrowserContext } from 'playwright'; -import type { - ViewportReport, - ResponsiveReport, - TouchTargetIssue, - LayoutShift, - LayoutIssue, -} from './types'; - -// ============================================================================ -// Standard Breakpoints -// ============================================================================ - -export const STANDARD_BREAKPOINTS = { - mobile: { width: 640, height: 667 }, - tablet: { width: 768, height: 1024 }, - desktop: { width: 1024, height: 800 }, - wide: { width: 1280, height: 1080 }, -} as const; - -// ============================================================================ -// Responsive Validator Class -// ============================================================================ - -export class ResponsiveValidator { - private browser: Browser | null = null; - private context: BrowserContext | null = null; - private minTouchTargetSize: { width: number; height: number }; - - constructor(minTouchTargetSize = { width: 44, height: 44 }) { - this.minTouchTargetSize = minTouchTargetSize; - } - - /** - * Initialize browser instance - */ - async initialize(): Promise { - if (!this.browser) { - this.browser = await chromium.launch({ - headless: true, - }); - this.context = await this.browser.newContext(); - } - } - - /** - * Close browser instance - */ - async close(): Promise { - if (this.context) { - await this.context.close(); - this.context = null; - } - if (this.browser) { - await this.browser.close(); - this.browser = null; - } - } - - /** - * Test a specific viewport size - */ - async testViewport( - url: string, - width: number, - height: number - ): Promise { - await this.initialize(); - - if (!this.context) { - throw new Error('Browser context not initialized'); - } - - const page = await this.context.newPage(); - - try { - // Set viewport size - await page.setViewportSize({ width, height }); - - // Navigate to URL - await page.goto(url, { waitUntil: 'networkidle' }); - - // Wait for layout stabilization - await page.waitForTimeout(500); - - // Check for horizontal scroll - const hasHorizontalScroll = await this.checkHorizontalScroll(page); - - // Check touch targets - const touchTargetIssues = await this.checkTouchTargets(page); - - // Measure layout shifts - const layoutShifts = await this.measureLayoutShifts(page); - - // Capture screenshot - const screenshot = await page.screenshot({ fullPage: false }); - - return { - width, - height, - hasHorizontalScroll, - touchTargetIssues, - layoutShifts, - screenshot, - }; - } finally { - await page.close(); - } - } - - /** - * Test all standard breakpoints - */ - async testAllBreakpoints(url: string): Promise { - const issues: LayoutIssue[] = []; - - // Test mobile breakpoint - const mobileReport = await this.testViewport( - url, - STANDARD_BREAKPOINTS.mobile.width, - STANDARD_BREAKPOINTS.mobile.height - ); - - // Test tablet breakpoint - const tabletReport = await this.testViewport( - url, - STANDARD_BREAKPOINTS.tablet.width, - STANDARD_BREAKPOINTS.tablet.height - ); - - // Test desktop breakpoint - const desktopReport = await this.testViewport( - url, - STANDARD_BREAKPOINTS.desktop.width, - STANDARD_BREAKPOINTS.desktop.height - ); - - // Test wide breakpoint - const wideReport = await this.testViewport( - url, - STANDARD_BREAKPOINTS.wide.width, - STANDARD_BREAKPOINTS.wide.height - ); - - // Convert viewport issues to layout issues - this.convertToLayoutIssues(mobileReport, 'mobile', issues); - this.convertToLayoutIssues(tabletReport, 'tablet', issues); - this.convertToLayoutIssues(desktopReport, 'desktop', issues); - this.convertToLayoutIssues(wideReport, 'wide', issues); - - return { - url, - breakpoints: { - mobile: mobileReport, - tablet: tabletReport, - desktop: desktopReport, - wide: wideReport, - }, - issues, - }; - } - - /** - * Check for horizontal scroll - */ - async checkHorizontalScroll(page: Page): Promise { - return await page.evaluate(() => { - const scrollWidth = document.documentElement.scrollWidth; - const clientWidth = window.innerWidth; - return scrollWidth > clientWidth; - }); - } - - /** - * Check touch target sizes - */ - async checkTouchTargets(page: Page): Promise { - const issues: TouchTargetIssue[] = []; - - // Query all interactive elements - const interactiveSelectors = [ - 'button', - 'a', - 'input', - '[role="button"]', - '[onclick]', - 'select', - 'textarea', - ]; - - for (const selector of interactiveSelectors) { - const elements = await page.$$(selector); - - for (const element of elements) { - const box = await element.boundingBox(); - - if (box) { - // Check if element meets minimum size requirements - if ( - box.width < this.minTouchTargetSize.width || - box.height < this.minTouchTargetSize.height - ) { - // Get element selector for reporting - const elementSelector = await element.evaluate((el) => { - const id = el.id ? `#${el.id}` : ''; - const classes = el.className - ? `.${el.className.split(' ').join('.')}` - : ''; - return `${el.tagName.toLowerCase()}${id}${classes}`; - }); - - issues.push({ - selector: elementSelector, - width: box.width, - height: box.height, - minimumRequired: this.minTouchTargetSize, - }); - } - } - } - } - - return issues; - } - - /** - * Measure layout shifts - */ - private async measureLayoutShifts(page: Page): Promise { - // This is a simplified implementation - // In a real scenario, you'd use Performance Observer API - return await page.evaluate(() => { - const shifts: LayoutShift[] = []; - - // Check for elements without explicit dimensions - const images = document.querySelectorAll('img'); - images.forEach((img) => { - if (!img.width || !img.height) { - shifts.push({ - element: img.tagName.toLowerCase(), - shift: 0, // Would be calculated from actual measurements - }); - } - }); - - return shifts; - }); - } - - /** - * Convert viewport issues to layout issues - */ - private convertToLayoutIssues( - report: ViewportReport, - breakpoint: string, - issues: LayoutIssue[] - ): void { - // Add horizontal scroll issues - if (report.hasHorizontalScroll) { - issues.push({ - type: 'overflow', - severity: 'critical', - filePath: 'runtime', - lineNumber: 0, - columnNumber: 0, - message: `Horizontal scroll detected at ${breakpoint} breakpoint (${report.width}px)`, - suggestion: 'Check for elements with fixed widths or overflow issues', - autoFixable: false, - }); - } - - // Add touch target issues - for (const touchIssue of report.touchTargetIssues) { - issues.push({ - type: 'touch-target-small', - severity: 'moderate', - filePath: 'runtime', - lineNumber: 0, - columnNumber: 0, - message: `Touch target too small at ${breakpoint}: ${touchIssue.selector} (${touchIssue.width}x${touchIssue.height}px, minimum ${touchIssue.minimumRequired.width}x${touchIssue.minimumRequired.height}px)`, - suggestion: `Increase size to at least ${touchIssue.minimumRequired.width}x${touchIssue.minimumRequired.height}px`, - autoFixable: false, - }); - } - } -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -/** - * Create a responsive validator with default settings - */ -export function createResponsiveValidator( - minTouchTargetSize?: { width: number; height: number } -): ResponsiveValidator { - return new ResponsiveValidator(minTouchTargetSize); -} - -/** - * Quick test for a single URL at all breakpoints - */ -export async function testResponsive(url: string): Promise { - const validator = createResponsiveValidator(); - try { - const report = await validator.testAllBreakpoints(url); - return report; - } finally { - await validator.close(); - } -} diff --git a/lib/ui-analysis/types.ts b/lib/ui-analysis/types.ts deleted file mode 100644 index 8d2ebd0..0000000 --- a/lib/ui-analysis/types.ts +++ /dev/null @@ -1,342 +0,0 @@ -/** - * Shared TypeScript interfaces for UI analysis system - */ - -// ============================================================================ -// Issue Types -// ============================================================================ - -export type IssueType = - | 'overflow' - | 'misalignment' - | 'spacing-inconsistency' - | 'z-index-conflict' - | 'hardcoded-color' - | 'missing-responsive' - | 'touch-target-small' - | 'contrast-insufficient'; - -export type IssueSeverity = 'critical' | 'moderate' | 'minor'; - -export interface LayoutIssue { - type: IssueType; - severity: IssueSeverity; - filePath: string; - lineNumber: number; - columnNumber: number; - message: string; - suggestion?: string; - autoFixable: boolean; -} - -// ============================================================================ -// Code Location -// ============================================================================ - -export interface CodeLocation { - filePath: string; - lineNumber: number; - columnNumber: number; - snippet: string; -} - -// ============================================================================ -// Responsive Validation -// ============================================================================ - -export interface TouchTargetIssue { - selector: string; - width: number; - height: number; - minimumRequired: { width: number; height: number }; -} - -export interface LayoutShift { - element: string; - shift: number; -} - -export interface ViewportReport { - width: number; - height: number; - hasHorizontalScroll: boolean; - touchTargetIssues: TouchTargetIssue[]; - layoutShifts: LayoutShift[]; - screenshot?: Buffer; -} - -export interface ResponsiveReport { - url: string; - breakpoints: { - mobile: ViewportReport; - tablet: ViewportReport; - desktop: ViewportReport; - wide: ViewportReport; - }; - issues: LayoutIssue[]; -} - -// ============================================================================ -// Accessibility -// ============================================================================ - -export interface ViolationNode { - html: string; - target: string[]; - failureSummary: string; -} - -export interface A11yViolation { - id: string; - impact: 'critical' | 'serious' | 'moderate' | 'minor'; - description: string; - help: string; - helpUrl: string; - nodes: ViolationNode[]; -} - -export interface A11yPass { - id: string; - description: string; -} - -export interface A11yIncomplete { - id: string; - description: string; - nodes: ViolationNode[]; -} - -export interface A11yReport { - url: string; - violations: A11yViolation[]; - passes: A11yPass[]; - incomplete: A11yIncomplete[]; - wcagLevel: 'A' | 'AA' | 'AAA'; - score: number; -} - -export interface KeyboardIssue { - element: string; - issue: 'not-focusable' | 'no-tab-index' | 'focus-trap'; - recommendation: string; -} - -export interface FocusIssue { - element: string; - currentOutline: string; - requiredOutline: string; -} - -export interface ContrastIssue { - element: string; - foreground: string; - background: string; - ratio: number; - requiredRatio: number; - wcagLevel: 'AA' | 'AAA'; -} - -export interface MotionIssue { - element: string; - issue: string; - recommendation: string; -} - -// ============================================================================ -// Visual Hierarchy -// ============================================================================ - -export interface HeadingNode { - level: 1 | 2 | 3 | 4 | 5 | 6; - text: string; - fontSize: string; - fontWeight: string; - lineHeight: string; -} - -export interface HeadingIssue { - type: 'skipped-level' | 'inconsistent-size' | 'multiple-h1'; - heading: HeadingNode; - message: string; -} - -export interface HeadingReport { - headings: HeadingNode[]; - issues: HeadingIssue[]; - structure: 'valid' | 'invalid'; -} - -export interface ButtonInfo { - text: string; - className: string; - visualWeight: number; -} - -export interface ButtonIssue { - type: 'ambiguous-hierarchy' | 'too-many-primary'; - message: string; -} - -export interface ButtonHierarchyReport { - primaryButtons: ButtonInfo[]; - secondaryButtons: ButtonInfo[]; - issues: ButtonIssue[]; -} - -export interface SectionInfo { - selector: string; - marginTop: number; - marginBottom: number; - paddingTop: number; - paddingBottom: number; -} - -export interface SpacingIssue { - sections: [string, string]; - expectedGap: number; - actualGap: number; - deviation: number; -} - -export interface SpacingReport { - sections: SectionInfo[]; - averageGap: number; - inconsistencies: SpacingIssue[]; -} - -export interface AccentUsageReport { - usages: Array<{ - location: CodeLocation; - context: 'interactive' | 'decorative'; - appropriate: boolean; - }>; - issues: string[]; -} - -// ============================================================================ -// Design System Validation -// ============================================================================ - -export interface ColorViolation { - type: 'hardcoded-hex' | 'hardcoded-rgb' | 'non-token-var'; - value: string; - location: CodeLocation; - suggestedToken: string; -} - -export interface SpacingViolation { - type: 'arbitrary-value' | 'non-scale-value'; - value: string; - location: CodeLocation; - suggestedValue: string; -} - -export interface RadiusViolation { - type: 'hardcoded-radius' | 'non-token-radius'; - value: string; - location: CodeLocation; - suggestedToken: 'var(--radius-sm)' | 'var(--radius-md)' | 'var(--radius-lg)'; -} - -export interface FontViolation { - type: 'hardcoded-font' | 'non-token-font'; - value: string; - location: CodeLocation; - suggestedToken: 'var(--font-sans)' | 'var(--font-mono)' | 'var(--font-display)'; -} - -export interface DesignSystemReport { - totalViolations: number; - colorViolations: ColorViolation[]; - spacingViolations: SpacingViolation[]; - radiusViolations: RadiusViolation[]; - fontViolations: FontViolation[]; - complianceScore: number; -} - -// ============================================================================ -// Analysis Results -// ============================================================================ - -export interface AnalysisSummary { - totalIssues: number; - criticalIssues: number; - moderateIssues: number; - minorIssues: number; - filesScanned: number; - pagesAudited: number; - complianceScore: number; -} - -export interface AnalysisReport { - timestamp: string; - duration: number; - summary: AnalysisSummary; - issues: LayoutIssue[]; - responsiveReport?: ResponsiveReport; - a11yReport?: A11yReport; - designSystemReport?: DesignSystemReport; -} - -// ============================================================================ -// Configuration -// ============================================================================ - -export interface Breakpoint { - name: string; - width: number; - height: number; -} - -export interface RuleConfig { - layout: { - checkOverflow: boolean; - checkAlignment: boolean; - checkSpacing: boolean; - checkZIndex: boolean; - }; - responsive: { - minTouchTargetSize: { width: number; height: number }; - breakpoints: number[]; - allowHorizontalScroll: boolean; - }; - accessibility: { - wcagLevel: 'A' | 'AA' | 'AAA'; - checkKeyboard: boolean; - checkFocus: boolean; - checkContrast: boolean; - checkReducedMotion: boolean; - }; - designSystem: { - enforceTokens: boolean; - allowedArbitraryValues: string[]; - strictMode: boolean; - }; -} - -export interface ReportingConfig { - console: boolean; - overlay: boolean; - json: boolean; - outputPath: string; - minSeverity: IssueSeverity; -} - -export interface UIAnalysisConfig { - enabled: boolean; - mode: 'development' | 'ci' | 'production'; - rules: RuleConfig; - breakpoints: Breakpoint[]; - reporting: ReportingConfig; -} - -// ============================================================================ -// Analysis Error -// ============================================================================ - -export interface AnalysisError { - phase: 'parsing' | 'analysis' | 'reporting'; - file: string; - error: Error; - recoverable: boolean; -} diff --git a/lib/ui-analysis/visual-hierarchy-optimizer.ts b/lib/ui-analysis/visual-hierarchy-optimizer.ts deleted file mode 100644 index 560a01e..0000000 --- a/lib/ui-analysis/visual-hierarchy-optimizer.ts +++ /dev/null @@ -1,512 +0,0 @@ -/** - * Visual Hierarchy Optimizer - Analysis tool for evaluating visual hierarchy - * - * This module analyzes: - * - Heading structure and hierarchy (h1-h6) - * - Button visual hierarchy (primary vs secondary) - * - Section spacing consistency - * - Accent color usage patterns - * - Contrast ratios for visual prominence - */ - -import type { - HeadingReport, - HeadingNode, - HeadingIssue, - ButtonHierarchyReport, - ButtonInfo, - ButtonIssue, - SpacingReport, - SectionInfo, - SpacingIssue, - AccentUsageReport, - CodeLocation, -} from './types'; - -// ============================================================================ -// VisualHierarchyOptimizer Class -// ============================================================================ - -export class VisualHierarchyOptimizer { - /** - * Analyze heading structure from HTML content - */ - analyzeHeadingStructure(html: string): HeadingReport { - const headings = this.extractHeadings(html); - const issues = this.validateHeadingStructure(headings); - const structure = issues.length === 0 ? 'valid' : 'invalid'; - - return { - headings, - issues, - structure, - }; - } - - /** - * Analyze button hierarchy from page - * Note: This requires browser context for computed styles - */ - async analyzeButtonHierarchy(page: any): Promise { - // Extract button information with computed styles - const buttons = await this.extractButtons(page); - - // Classify buttons by visual weight - const primaryButtons: ButtonInfo[] = []; - const secondaryButtons: ButtonInfo[] = []; - - for (const button of buttons) { - if (button.visualWeight >= 70) { - primaryButtons.push(button); - } else { - secondaryButtons.push(button); - } - } - - // Detect hierarchy issues - const issues = this.validateButtonHierarchy(primaryButtons, secondaryButtons); - - return { - primaryButtons, - secondaryButtons, - issues, - }; - } - - /** - * Analyze section spacing consistency - * Note: This requires browser context for computed styles - */ - async analyzeSectionSpacing(page: any): Promise { - const sections = await this.extractSections(page); - const gaps = this.calculateGaps(sections); - const averageGap = gaps.length > 0 - ? gaps.reduce((sum, gap) => sum + gap, 0) / gaps.length - : 0; - - const inconsistencies = this.detectSpacingInconsistencies(sections, averageGap); - - return { - sections, - averageGap, - inconsistencies, - }; - } - - /** - * Analyze accent color usage in CSS - */ - analyzeAccentUsage(css: string): AccentUsageReport { - const usages: AccentUsageReport['usages'] = []; - const issues: string[] = []; - - // Search for accent color usage (#8aa2ff or var(--color-accent)) - const accentColorRegex = /#8aa2ff|var\(--color-accent\)/gi; - const lines = css.split('\n'); - - // Track current CSS rule context - let currentRuleContext = ''; - let braceDepth = 0; - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - - // Track CSS rule context (selector) - if (line.includes('{')) { - braceDepth++; - if (braceDepth === 1) { - // This is the start of a new rule, capture the selector - currentRuleContext = line.split('{')[0].trim(); - } - } - if (line.includes('}')) { - braceDepth--; - if (braceDepth === 0) { - currentRuleContext = ''; - } - } - - const matches = line.matchAll(accentColorRegex); - - for (const match of matches) { - // Use the rule context (selector) to determine if it's interactive - const context = this.determineColorContext(currentRuleContext || line); - const appropriate = this.isAppropriateAccentUsage(context); - - usages.push({ - location: { - filePath: 'styles.css', // Would be set by caller - lineNumber: i + 1, - columnNumber: match.index || 0, - snippet: line.trim(), - }, - context, - appropriate, - }); - - if (!appropriate) { - issues.push( - `Line ${i + 1}: Accent color used in ${context} context - should be reserved for interactive elements` - ); - } - } - } - - return { - usages, - issues, - }; - } - - // ========================================================================== - // Private Helper Methods - Heading Analysis - // ========================================================================== - - /** - * Extract heading elements from HTML - */ - private extractHeadings(html: string): HeadingNode[] { - const headings: HeadingNode[] = []; - - // Match heading tags with their content - const headingRegex = /]*>(.*?)<\/h\1>/gi; - const matches = html.matchAll(headingRegex); - - for (const match of matches) { - const level = parseInt(match[1], 10) as 1 | 2 | 3 | 4 | 5 | 6; - const text = this.stripHtmlTags(match[2]); - - // Extract inline styles if present - const styleMatch = match[0].match(/style="([^"]*)"/); - const styles = this.parseInlineStyles(styleMatch ? styleMatch[1] : ''); - - headings.push({ - level, - text, - fontSize: styles.fontSize || this.getDefaultFontSize(level), - fontWeight: styles.fontWeight || this.getDefaultFontWeight(level), - lineHeight: styles.lineHeight || this.getDefaultLineHeight(level), - }); - } - - return headings; - } - - /** - * Validate heading structure for issues - */ - private validateHeadingStructure(headings: HeadingNode[]): HeadingIssue[] { - const issues: HeadingIssue[] = []; - - // Check for multiple h1 elements - const h1Count = headings.filter(h => h.level === 1).length; - if (h1Count > 1) { - const h1Headings = headings.filter(h => h.level === 1); - for (const heading of h1Headings.slice(1)) { - issues.push({ - type: 'multiple-h1', - heading, - message: 'Multiple h1 elements found - only one h1 should exist per page', - }); - } - } - - // Check for skipped heading levels - for (let i = 1; i < headings.length; i++) { - const current = headings[i]; - const previous = headings[i - 1]; - - if (current.level > previous.level + 1) { - issues.push({ - type: 'skipped-level', - heading: current, - message: `Heading level skipped: h${previous.level} followed by h${current.level}`, - }); - } - } - - // Check for inconsistent font sizes within same level - const sizesByLevel = new Map(); - for (const heading of headings) { - if (!sizesByLevel.has(heading.level)) { - sizesByLevel.set(heading.level, []); - } - sizesByLevel.get(heading.level)!.push(heading.fontSize); - } - - for (const [level, sizes] of sizesByLevel.entries()) { - const uniqueSizes = new Set(sizes); - if (uniqueSizes.size > 1) { - const affectedHeadings = headings.filter(h => h.level === level); - for (const heading of affectedHeadings) { - issues.push({ - type: 'inconsistent-size', - heading, - message: `Inconsistent font size for h${level} elements`, - }); - } - } - } - - return issues; - } - - /** - * Strip HTML tags from text - */ - private stripHtmlTags(html: string): string { - return html.replace(/<[^>]*>/g, '').trim(); - } - - /** - * Parse inline CSS styles - */ - private parseInlineStyles(styleString: string): Record { - const styles: Record = {}; - - const declarations = styleString.split(';').filter(Boolean); - for (const declaration of declarations) { - const [property, value] = declaration.split(':').map(s => s.trim()); - if (property && value) { - // Convert kebab-case to camelCase - const camelProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); - styles[camelProperty] = value; - } - } - - return styles; - } - - /** - * Get default font size for heading level - */ - private getDefaultFontSize(level: number): string { - const sizes: Record = { - 1: '2rem', - 2: '1.5rem', - 3: '1.25rem', - 4: '1rem', - 5: '0.875rem', - 6: '0.75rem', - }; - return sizes[level] || '1rem'; - } - - /** - * Get default font weight for heading level - */ - private getDefaultFontWeight(level: number): string { - return level <= 3 ? '700' : '600'; - } - - /** - * Get default line height for heading level - */ - private getDefaultLineHeight(level: number): string { - return '1.2'; - } - - // ========================================================================== - // Private Helper Methods - Button Analysis - // ========================================================================== - - /** - * Extract button information from page - */ - private async extractButtons(page: any): Promise { - // This would be called with a Playwright page object - // For now, return empty array as placeholder - return page.evaluate(() => { - const buttons = Array.from(document.querySelectorAll('button, a[role="button"], input[type="button"], input[type="submit"]')); - - return buttons.map((button: Element) => { - const styles = window.getComputedStyle(button); - const text = button.textContent?.trim() || ''; - const className = button.className || ''; - - // Calculate visual weight based on styles - let visualWeight = 0; - - // Background color contributes to weight - const bgColor = styles.backgroundColor; - if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') { - visualWeight += 40; - } - - // Border contributes to weight - const borderWidth = parseFloat(styles.borderWidth); - if (borderWidth > 0) { - visualWeight += 20; - } - - // Padding contributes to weight - const padding = parseFloat(styles.paddingTop) + parseFloat(styles.paddingBottom); - if (padding > 16) { - visualWeight += 20; - } - - // Font weight contributes - const fontWeight = parseInt(styles.fontWeight, 10); - if (fontWeight >= 600) { - visualWeight += 20; - } - - return { - text, - className, - visualWeight, - }; - }); - }); - } - - /** - * Validate button hierarchy - */ - private validateButtonHierarchy( - primaryButtons: ButtonInfo[], - secondaryButtons: ButtonInfo[] - ): ButtonIssue[] { - const issues: ButtonIssue[] = []; - - // Check for too many primary buttons - if (primaryButtons.length > 2) { - issues.push({ - type: 'too-many-primary', - message: `Found ${primaryButtons.length} primary buttons - consider limiting to 1-2 per section`, - }); - } - - // Check for ambiguous hierarchy (primary and secondary too similar) - if (primaryButtons.length > 0 && secondaryButtons.length > 0) { - const avgPrimaryWeight = primaryButtons.reduce((sum, b) => sum + b.visualWeight, 0) / primaryButtons.length; - const avgSecondaryWeight = secondaryButtons.reduce((sum, b) => sum + b.visualWeight, 0) / secondaryButtons.length; - - if (Math.abs(avgPrimaryWeight - avgSecondaryWeight) < 20) { - issues.push({ - type: 'ambiguous-hierarchy', - message: 'Primary and secondary buttons have similar visual weight - increase distinction', - }); - } - } - - return issues; - } - - // ========================================================================== - // Private Helper Methods - Spacing Analysis - // ========================================================================== - - /** - * Extract section information from page - */ - private async extractSections(page: any): Promise { - return page.evaluate(() => { - const sections = Array.from(document.querySelectorAll('section, [class*="section"], main > div')); - - return sections.map((section: Element, index: number) => { - const styles = window.getComputedStyle(section); - const selector = section.tagName.toLowerCase() + (section.className ? `.${section.className.split(' ')[0]}` : '') || `section-${index}`; - - return { - selector, - marginTop: parseFloat(styles.marginTop), - marginBottom: parseFloat(styles.marginBottom), - paddingTop: parseFloat(styles.paddingTop), - paddingBottom: parseFloat(styles.paddingBottom), - }; - }); - }); - } - - /** - * Calculate gaps between sections - */ - private calculateGaps(sections: SectionInfo[]): number[] { - const gaps: number[] = []; - - for (let i = 0; i < sections.length - 1; i++) { - const current = sections[i]; - const next = sections[i + 1]; - - // Gap is the sum of current's bottom margin and next's top margin - const gap = current.marginBottom + next.marginTop; - gaps.push(gap); - } - - return gaps; - } - - /** - * Detect spacing inconsistencies - */ - private detectSpacingInconsistencies( - sections: SectionInfo[], - averageGap: number - ): SpacingIssue[] { - const inconsistencies: SpacingIssue[] = []; - const tolerance = 8; // 0.5rem in pixels (assuming 16px base) - - for (let i = 0; i < sections.length - 1; i++) { - const current = sections[i]; - const next = sections[i + 1]; - - const actualGap = current.marginBottom + next.marginTop; - const deviation = Math.abs(actualGap - averageGap); - - if (deviation > tolerance) { - inconsistencies.push({ - sections: [current.selector, next.selector], - expectedGap: averageGap, - actualGap, - deviation, - }); - } - } - - return inconsistencies; - } - - // ========================================================================== - // Private Helper Methods - Accent Color Analysis - // ========================================================================== - - /** - * Determine context of color usage from CSS line - */ - private determineColorContext(cssLine: string): 'interactive' | 'decorative' { - const line = cssLine.toLowerCase(); - - // Interactive contexts (check these first) - const interactiveKeywords = [ - 'button', - 'link', - 'hover', - 'active', - 'focus', - 'input', - 'select', - 'a:', - 'a ', - '[role="button"]', - ]; - - for (const keyword of interactiveKeywords) { - if (line.includes(keyword)) { - return 'interactive'; - } - } - - // If no interactive keywords found, check for decorative contexts - // Note: We don't check decorative keywords if interactive was found - return 'decorative'; - } - - /** - * Check if accent color usage is appropriate - */ - private isAppropriateAccentUsage(context: 'interactive' | 'decorative'): boolean { - // Accent color should only be used for interactive elements - return context === 'interactive'; - } -} diff --git a/ui-analysis.config.example.json b/ui-analysis.config.example.json deleted file mode 100644 index 02810fb..0000000 --- a/ui-analysis.config.example.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "enabled": true, - "mode": "development", - "rules": { - "layout": { - "checkOverflow": true, - "checkAlignment": true, - "checkSpacing": true, - "checkZIndex": true - }, - "responsive": { - "minTouchTargetSize": { - "width": 44, - "height": 44 - }, - "breakpoints": [640, 768, 1024, 1280], - "allowHorizontalScroll": false - }, - "accessibility": { - "wcagLevel": "AA", - "checkKeyboard": true, - "checkFocus": true, - "checkContrast": true, - "checkReducedMotion": true - }, - "designSystem": { - "enforceTokens": true, - "allowedArbitraryValues": [], - "strictMode": false - } - }, - "breakpoints": [ - { - "name": "mobile", - "width": 375, - "height": 667 - }, - { - "name": "tablet", - "width": 768, - "height": 1024 - }, - { - "name": "desktop", - "width": 1280, - "height": 800 - }, - { - "name": "wide", - "width": 1920, - "height": 1080 - } - ], - "reporting": { - "console": true, - "overlay": true, - "json": false, - "outputPath": "./ui-analysis-report.json", - "minSeverity": "minor" - } -} From 5a29af3f1e757a094707acc52a5a4e0cac3dd7a4 Mon Sep 17 00:00:00 2001 From: Screen <228304862+screen-gd@users.noreply.github.com> Date: Wed, 21 Oct 2026 04:19:33 +0400 Subject: [PATCH 09/20] Add the Scout data layer, LLM client and prompt pack Scout keeps packs, resources, sources and queue jobs in one SQLite file. Prompts are versioned markdown so a pack can record which versions built it. --- package-lock.json | 1063 ++++++++++++++++++++++++++++++++++- package.json | 10 +- prompts/scout/book.v1.md | 13 + prompts/scout/extract.v1.md | 19 + prompts/scout/plan.v1.md | 23 + prompts/scout/quiz.v1.md | 15 + src/lib/scout/db.ts | 89 +++ src/lib/scout/extract.ts | 349 ++++++++++++ src/lib/scout/format.ts | 130 +++++ src/lib/scout/jobs.ts | 134 +++++ src/lib/scout/llm.ts | 146 +++++ src/lib/scout/prompts.ts | 41 ++ src/lib/scout/runner.ts | 134 +++++ src/lib/scout/schema.sql | 112 ++++ src/lib/scout/settings.ts | 54 ++ src/lib/scout/sources.ts | 81 +++ src/lib/scout/spend.ts | 75 +++ src/lib/scout/types.ts | 139 +++++ src/types/index.ts | 2 + vite.config.ts | 5 + 20 files changed, 2604 insertions(+), 30 deletions(-) create mode 100644 prompts/scout/book.v1.md create mode 100644 prompts/scout/extract.v1.md create mode 100644 prompts/scout/plan.v1.md create mode 100644 prompts/scout/quiz.v1.md create mode 100644 src/lib/scout/db.ts create mode 100644 src/lib/scout/extract.ts create mode 100644 src/lib/scout/format.ts create mode 100644 src/lib/scout/jobs.ts create mode 100644 src/lib/scout/llm.ts create mode 100644 src/lib/scout/prompts.ts create mode 100644 src/lib/scout/runner.ts create mode 100644 src/lib/scout/schema.sql create mode 100644 src/lib/scout/settings.ts create mode 100644 src/lib/scout/sources.ts create mode 100644 src/lib/scout/spend.ts create mode 100644 src/lib/scout/types.ts diff --git a/package-lock.json b/package-lock.json index 2bcd910..2b3b412 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,10 @@ "name": "syllo", "version": "1.0.0", "dependencies": { + "@ai-sdk/openai": "4.0.98", "@fontsource-variable/geist": "5.3.0", "@fontsource-variable/geist-mono": "5.3.0", + "@modelcontextprotocol/sdk": "1.32.1", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.8", @@ -17,8 +19,11 @@ "@radix-ui/react-switch": "1.2.6", "@tanstack/react-router": "1.170.45", "@tanstack/react-start": "1.169.3", + "ai": "7.0.147", + "better-sqlite3": "13.0.3", "class-variance-authority": "0.7.1", "clsx": "2.1.1", + "cross-spawn": "7.0.6", "framer-motion": "12.34.5", "lucide-react": "0.576.0", "motion": "12.35.1", @@ -26,12 +31,15 @@ "react": "19.2.4", "react-dom": "19.2.4", "sonner": "2.0.7", - "tailwind-merge": "3.5.0" + "tailwind-merge": "3.5.0", + "zod": "4.6.7" }, "devDependencies": { "@eslint/js": "9.39.5", "@tailwindcss/vite": "4.3.3", "@tanstack/router-plugin": "1.168.47", + "@types/better-sqlite3": "7.6.13", + "@types/cross-spawn": "6.0.6", "@types/node": "24.19.2", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", @@ -47,6 +55,70 @@ "vitest": "4.1.12" } }, + "node_modules/@ai-sdk/gateway": { + "version": "4.0.120", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.120.tgz", + "integrity": "sha512-hHGDQrCBgiEiPuimtc1NHPRGwte7sBG3XQuxTdurrpuGXKqCSHVlrBIDsiXjFOBYOms1+9aDz2xpTq+kOhiJWA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.31", + "@ai-sdk/provider-utils": "5.0.64", + "@vercel/oidc": "3.2.0" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "4.0.98", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.98.tgz", + "integrity": "sha512-RvrbLblENKj//cQWlzsbmVKvvV8ZUq8FFB8/68ODWnu5sS3T+lUmRoKOM7NjR2UIX0/7YDrtauJr9fxQ0cwkug==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.31", + "@ai-sdk/provider-utils": "5.0.64" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "4.0.31", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.31.tgz", + "integrity": "sha512-3wJnwVTFg8SwOyoZCv+ov7Re2GB+7IQnU5a5MYJACrtiWDDUHgqTBeeesq2MB2ddgojzL8hgWleltwpAgtUqfg==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "5.0.64", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.64.tgz", + "integrity": "sha512-BDU8rDR6mBR+na5U/Dj4B33LX980x200lqhBGRIOxxx1NFunoixuvvweqIB+sCsCoFC8cGCsa82Ce2fKoAPOqw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.31", + "@standard-schema/spec": "^1.1.0", + "@workflow/serde": "4.1.0", + "eventsource-parser": "^3.0.8", + "undici": "^7.29.0" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -484,6 +556,18 @@ "url": "https://github.com/sponsors/ayuhito" } }, + "node_modules/@hono/node-server": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz", + "integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -595,6 +679,68 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.1.tgz", + "integrity": "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/@oozcitak/dom": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz", @@ -1671,7 +1817,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, "license": "MIT" }, "node_modules/@tailwindcss/node": { @@ -2653,6 +2798,16 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2664,6 +2819,16 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -3010,6 +3175,15 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@vercel/oidc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz", + "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, "node_modules/@vitejs/plugin-react": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", @@ -3153,6 +3327,25 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@workflow/serde": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@workflow/serde/-/serde-4.1.0.tgz", + "integrity": "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==", + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", @@ -3176,6 +3369,23 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/ai": { + "version": "7.0.147", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.147.tgz", + "integrity": "sha512-9MsjkCddqIPYGDMDc6HF+i4cydkFpC2xteQGlR6Dz5cuWLIV5sCARbFCqaiTUn3/GaNxg3bIfRlfSzxPvi77fw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "4.0.120", + "@ai-sdk/provider": "4.0.31", + "@ai-sdk/provider-utils": "5.0.64" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -3193,6 +3403,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3440,6 +3689,55 @@ "node": ">=6.0.0" } }, + "node_modules/better-sqlite3": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-13.0.3.tgz", + "integrity": "sha512-RbOBxmLBG8uvFUc15X9+9SFemKcQ0WBuISBVkpuiaUB2qblC8UWlHEjdWVoZ8AdhSwmoEgsiXKfopX0CQxaACQ==", + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.0.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/brace-expansion": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", @@ -3484,6 +3782,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -3507,7 +3814,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3521,7 +3827,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3654,23 +3959,79 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-es": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz", "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", "license": "MIT" }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3809,6 +4170,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3837,7 +4207,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3848,6 +4217,12 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.462", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.462.tgz", @@ -3861,6 +4236,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.26.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", @@ -3967,7 +4351,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3977,7 +4360,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3994,7 +4376,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4062,6 +4443,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4290,6 +4677,36 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -4300,6 +4717,81 @@ "node": ">=12.0.0" } }, + "node_modules/express": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", + "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.3.0", + "content-disposition": "^2.0.1", + "content-type": "^2.0.0", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.8", + "qs": "^6.16.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.1.0", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz", + "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/exsolve": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.3.tgz", @@ -4310,7 +4802,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -4327,8 +4818,24 @@ "dev": true, "license": "MIT" }, - "node_modules/fdir": { - "version": "6.5.0", + "node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", @@ -4363,6 +4870,27 @@ "node": ">=16.0.0" } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4417,6 +4945,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/framer-motion": { "version": "12.34.5", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.5.tgz", @@ -4444,6 +4981,15 @@ } } }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4462,7 +5008,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4525,7 +5070,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4559,7 +5103,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -4634,7 +5177,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4706,7 +5248,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4735,7 +5276,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4761,6 +5301,51 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hono": { + "version": "4.13.13", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.13.tgz", + "integrity": "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4798,6 +5383,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4813,6 +5404,24 @@ "node": ">= 0.4" } }, + "node_modules/ip-address": { + "version": "10.7.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -5050,6 +5659,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -5215,7 +5830,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/jiti": { @@ -5227,6 +5841,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.13", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.13.tgz", + "integrity": "sha512-7TIg10ZE1x82YediN+lcrPNvGMKge9yFHQD+Qho/b8y2adfugU3AsvvHfxV0fzyFllT6l9xCjf4RWY9g/Z9PJQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5274,6 +5897,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5281,6 +5910,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -5687,12 +6322,61 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -5806,6 +6490,44 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/node-addon-api": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.2.tgz", + "integrity": "sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, "node_modules/node-releases": { "version": "2.0.59", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.59.tgz", @@ -5815,11 +6537,19 @@ "node": ">=18" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5911,6 +6641,27 @@ "node": ">=12.20.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/openai": { "version": "6.27.0", "resolved": "https://registry.npmjs.org/openai/-/openai-6.27.0.tgz", @@ -6014,6 +6765,15 @@ "node": ">=6" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6028,12 +6788,21 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -6058,6 +6827,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -6122,6 +6900,23 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6132,6 +6927,50 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/react": { "version": "19.2.4", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", @@ -6279,6 +7118,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6323,6 +7171,22 @@ "@rolldown/binding-win32-x64-msvc": "1.2.14" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/safe-array-concat": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", @@ -6378,6 +7242,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -6393,6 +7263,32 @@ "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/seroval": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.6.11.tgz", @@ -6414,6 +7310,25 @@ "seroval": "^1.0" } }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6463,11 +7378,16 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -6480,7 +7400,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6490,7 +7409,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6510,7 +7428,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6527,7 +7444,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -6546,7 +7462,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -6617,6 +7532,15 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", @@ -6813,6 +7737,15 @@ "node": ">=14.0.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -6845,6 +7778,37 @@ "node": ">= 0.8.0" } }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -6985,6 +7949,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", @@ -6992,6 +7965,15 @@ "devOptional": true, "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unplugin": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.4.0.tgz", @@ -7142,6 +8124,15 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", @@ -7601,7 +8592,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -7729,6 +8719,12 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/xmlbuilder2": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz", @@ -7772,6 +8768,15 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, "node_modules/zod-validation-error": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", diff --git a/package.json b/package.json index e050f10..57ca558 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,10 @@ "test:watch": "vitest" }, "dependencies": { + "@ai-sdk/openai": "4.0.98", "@fontsource-variable/geist": "5.3.0", "@fontsource-variable/geist-mono": "5.3.0", + "@modelcontextprotocol/sdk": "1.32.1", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.8", @@ -22,8 +24,11 @@ "@radix-ui/react-switch": "1.2.6", "@tanstack/react-router": "1.170.45", "@tanstack/react-start": "1.169.3", + "ai": "7.0.147", + "better-sqlite3": "13.0.3", "class-variance-authority": "0.7.1", "clsx": "2.1.1", + "cross-spawn": "7.0.6", "framer-motion": "12.34.5", "lucide-react": "0.576.0", "motion": "12.35.1", @@ -31,12 +36,15 @@ "react": "19.2.4", "react-dom": "19.2.4", "sonner": "2.0.7", - "tailwind-merge": "3.5.0" + "tailwind-merge": "3.5.0", + "zod": "4.6.7" }, "devDependencies": { "@eslint/js": "9.39.5", "@tailwindcss/vite": "4.3.3", "@tanstack/router-plugin": "1.168.47", + "@types/better-sqlite3": "7.6.13", + "@types/cross-spawn": "6.0.6", "@types/node": "24.19.2", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", diff --git a/prompts/scout/book.v1.md b/prompts/scout/book.v1.md new file mode 100644 index 0000000..e8b62ad --- /dev/null +++ b/prompts/scout/book.v1.md @@ -0,0 +1,13 @@ +--- +version: 1 +purpose: Suggest a reading outline for a book the learner added +--- + +You suggest a reading outline for a book that Syllo does not have a copy of. + +Rules: + +1. Use only the book's title and author. Do not pretend to quote the book. +2. Return 4–10 chapter-sized reading units with short descriptive titles. +3. If you do not know the book, return a single unit called "Read the book" and set `confident` to false. +4. Never include page numbers, ISBNs or quotes. diff --git a/prompts/scout/extract.v1.md b/prompts/scout/extract.v1.md new file mode 100644 index 0000000..b8e3ee8 --- /dev/null +++ b/prompts/scout/extract.v1.md @@ -0,0 +1,19 @@ +--- +version: 1 +purpose: Extract study material from one free learning resource +--- + +You turn one free learning resource into study material for a developer. + +You receive a title, creator, description, and the resource's text. For videos the text is a transcript with `[mm:ss]` markers; for articles it is cleaned markdown. + +Rules: + +1. Use only the supplied text and description. Never add facts, chapters, tools or opinions that are not there. +2. `summary` is 2–4 sentences in plain prose. No marketing language, no bullet points. +3. `keyPoints` are the ideas a learner should remember. Each one is a single sentence. +4. Every key point that makes a factual or technical claim must include a `quote` copied verbatim from the supplied text (at least 12 characters) and the `atSeconds` where that quote appears for videos. If you cannot find an exact quote, omit the key point. +5. `skills` are 1–5 short labels naming the capability the resource teaches, e.g. "layout", "state management", "sql joins". Lowercase, no version numbers. +6. Only include `chapters` for videos. Use the description's chapter list when it exists. Otherwise propose 3–8 chapters, each with the second it starts and a short title. Never invent timestamps beyond the video duration supplied. +7. Ignore sponsor reads, subscribe requests, channel plugs and course advertisements. +8. If the text is too thin to support any claim, return an empty `keyPoints` array and say so in `summary`. diff --git a/prompts/scout/plan.v1.md b/prompts/scout/plan.v1.md new file mode 100644 index 0000000..f16dc4b --- /dev/null +++ b/prompts/scout/plan.v1.md @@ -0,0 +1,23 @@ +--- +version: 1 +purpose: Turn approved resources into a Syllo workspace +--- + +You build a short, honest curriculum from resources a learner has already approved. + +You receive the learner's goal and a list of resources. Each resource has an id, title, creator, kind (`video`, `article`, `doc`, `book`), summary, skill tags and chapters. Videos also carry a duration and chapter start times. + +Rules: + +1. Group the material into 3–6 modules. A module is one capability, not one resource. +2. Order modules so each depends only on earlier ones. +3. Every task must belong to a module and point at one real `resourceId` from the input. +4. For a video with chapters, turn chapters into watch tasks. Task titles read like "Watch (12:30–25:10)". Set `startSeconds` and `endSeconds` from the chapters. +5. For an article or doc, create one or two read tasks. Set `resourceId` and leave timestamps null. +6. For a book, create one task per chapter title supplied. Never invent chapter numbers that are not in the input. +7. Do not create tasks that require paid accounts, sign-ups, or resources outside the input. +8. `estimatedMinutes` is realistic: video tasks use the chapter length; reading tasks assume 4 minutes per 1000 words when word count is known, otherwise 20 minutes. +9. `summary` is 2–3 sentences describing what the learner will be able to do at the end. +10. `objectives` are 3–5 concrete abilities, each starting with a verb. +11. `difficulty` is `beginner`, `intermediate` or `advanced`, judged from the material, not the goal's ambition. +12. Resource ids must be copied exactly. Never create new ids. diff --git a/prompts/scout/quiz.v1.md b/prompts/scout/quiz.v1.md new file mode 100644 index 0000000..0b2dcc7 --- /dev/null +++ b/prompts/scout/quiz.v1.md @@ -0,0 +1,15 @@ +--- +version: 1 +purpose: Write spaced-repetition cards grounded in approved resources +--- + +You write review cards for a learner who just finished a resource. + +Rules: + +1. Write 2–4 cards per module, not per resource. Fewer is better than vague. +2. A card is one question with one short answer. No multiple choice. +3. Every card must carry the `resourceId`, the `atSeconds` for videos, and a `quote` copied verbatim from the supplied text (at least 12 characters). +4. Ask about a decision or a mechanism, not trivia: "Why does X cause Y?" beats "What year was X released?". +5. The answer must be checkable from the quote alone. +6. Skip a card rather than inventing a fact. diff --git a/src/lib/scout/db.ts b/src/lib/scout/db.ts new file mode 100644 index 0000000..ab5cf06 --- /dev/null +++ b/src/lib/scout/db.ts @@ -0,0 +1,89 @@ +import Database from "better-sqlite3"; +import { mkdirSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import schema from "./schema.sql?raw"; + +export type ScoutDatabase = Database.Database; + +const globalKey = Symbol.for("syllo.scout.db"); + +type GlobalWithDb = typeof globalThis & { + [globalKey]?: { db: ScoutDatabase; path: string }; +}; + +export function getScoutDbPath() { + return resolve(process.env.SCOUT_DB_PATH?.trim() || join(process.cwd(), ".data", "syllo.db")); +} + +/** + * One SQLite file for the whole app. + * + * The connection is cached on globalThis because the dev server reloads + * modules on every edit; opening a fresh handle each time leaks file locks. + */ +export function getScoutDb(): ScoutDatabase { + const path = getScoutDbPath(); + const store = globalThis as GlobalWithDb; + const cached = store[globalKey]; + if (cached?.path === path) return cached.db; + + mkdirSync(dirname(path), { recursive: true }); + const db = new Database(path); + db.pragma("journal_mode = WAL"); + db.pragma("foreign_keys = ON"); + db.exec(schema); + seedDefaults(db); + + store[globalKey] = { db, path }; + return db; +} + +export function closeScoutDb() { + const store = globalThis as GlobalWithDb; + const cached = store[globalKey]; + if (!cached) return; + cached.db.close(); + delete store[globalKey]; +} + +function seedDefaults(db: ScoutDatabase) { + db.prepare( + `INSERT OR IGNORE INTO scout_settings (id, auto_check, paused, monthly_budget_cents, updated_at) + VALUES (1, 1, 0, 500, ?)`, + ).run(new Date().toISOString()); + + // First run only. A learner who deletes every source keeps it that way. + const seeded = db.prepare(`SELECT value FROM scout_kv WHERE key = 'seeded_sources'`).get() as + | { value: string } + | undefined; + if (seeded) return; + + const now = new Date().toISOString(); + const seeds: Array<[string, string, string]> = [ + ["youtube_channel", "Juxtopposed", "@juxtopposed"], + ["youtube_channel", "DesignCourse", "@DesignCourse"], + ["youtube_channel", "Kevin Powell", "@KevinPowell"], + ["site", "MDN Web Docs", "developer.mozilla.org"], + ["site", "web.dev", "web.dev"], + ]; + + const insert = db.prepare( + `INSERT OR IGNORE INTO sources (id, kind, label, value, enabled, meta, created_at) + VALUES (?, ?, ?, ?, 1, '{}', ?)`, + ); + + db.transaction(() => { + for (const [kind, label, value] of seeds) { + insert.run(newId("src"), kind, label, value, now); + } + db.prepare(`INSERT INTO scout_kv (key, value) VALUES ('seeded_sources', '1')`).run(); + })(); +} + +export function nowIso() { + return new Date().toISOString(); +} + +export function newId(prefix: string) { + return `${prefix}_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`; +} diff --git a/src/lib/scout/extract.ts b/src/lib/scout/extract.ts new file mode 100644 index 0000000..33f641f --- /dev/null +++ b/src/lib/scout/extract.ts @@ -0,0 +1,349 @@ +import { z } from "zod"; +import { clip, deepLink, parseDescriptionChapters, quoteAppearsIn, normalizeForMatch, chunkText } from "./format"; +import { scoutGenerateObject, ScoutError, type ScoutLlmConfig } from "./llm"; +import { SCOUT_PROMPTS } from "./prompts"; +import type { Chapter, KeyPoint, ResourceKind, TranscriptStatus } from "./types"; + +/** The LLM contract. Kept small so a flash model can hold it. */ +export const extractionSchema = z.object({ + summary: z.string(), + keyPoints: z + .array( + z.object({ + text: z.string(), + atSeconds: z.number().nullable().optional(), + quote: z.string().nullable().optional(), + }), + ) + .default([]), + skills: z.array(z.string()).default([]), + chapters: z + .array( + z.object({ + startSeconds: z.number(), + title: z.string(), + }), + ) + .default([]), +}); + +export type ExtractionOutput = z.infer; + +export const bookOutlineSchema = z.object({ + chapters: z.array(z.string()).default([]), + confident: z.boolean().default(false), +}); + +export interface ExtractionInput { + packId?: string | null; + resource: { + id: string; + kind: ResourceKind; + title: string; + creator?: string | null; + description?: string | null; + durationSeconds?: number | null; + externalId?: string | null; + transcriptStatus?: TranscriptStatus; + }; + /** Plain text. For videos this is the transcript without markers. */ + text: string; + /** Transcript segments, when the resource is a video. */ + segments?: Array<{ text: string; start: number }>; + config?: Partial | null; +} + +export interface ExtractionResult { + summary: string; + keyPoints: KeyPoint[]; + skills: string[]; + chapters: Chapter[]; + degraded: boolean; + error?: string; +} + +const SKILL_STOPWORDS = new Set([ + "a", + "an", + "and", + "the", + "for", + "with", + "from", + "into", + "your", + "you", + "how", + "to", + "of", + "in", + "on", + "video", + "tutorial", + "guide", + "course", + "part", +]); + +/** "React Hooks!" and "react hooks" collapse to one module name. */ +export function normalizeSkill(value: string): string | null { + const skill = value + .toLowerCase() + .replace(/^#+/, "") + .replace(/[^a-z0-9+#.\s-]/g, " ") + .replace(/\s+/g, " ") + .trim(); + + if (skill.length < 2 || skill.length > 42) return null; + if (!skill.split(" ").some((word) => word && !SKILL_STOPWORDS.has(word))) return null; + return skill; +} + +function uniqueSkills(values: string[]): string[] { + const seen = new Set(); + const result: string[] = []; + for (const value of values) { + const skill = normalizeSkill(value); + if (!skill || seen.has(skill)) continue; + seen.add(skill); + result.push(skill); + } + return result.slice(0, 6); +} + +function sentenceFallback(input: ExtractionInput): string { + const description = clip(input.resource.description ?? "", 320); + if (description.length > 40) return description; + return `${input.resource.title}${input.resource.creator ? ` by ${input.resource.creator}` : ""}.`; +} + +function stripMarkers(value: string) { + return value.replace(/\[\d{1,2}:\d{2}(?::\d{2})?\]/g, " "); +} + +function pickChunks(text: string, limit = 3) { + const chunks = chunkText(text, 24_000, 600); + if (chunks.length <= limit) return chunks; + const middle = Math.floor(chunks.length / 2); + return chunks.length >= 3 ? [chunks[0], chunks[middle], chunks[chunks.length - 1]] : chunks.slice(0, limit); +} + +/** + * The model is told to copy quotes verbatim. When it does not, the claim is + * dropped rather than shown with an invented timestamp. + */ +function resolveTimestamp( + value: number | null | undefined, + quote: string | null | undefined, + segments: ExtractionInput["segments"], + durationSeconds: number | null | undefined, +): number | null { + const max = durationSeconds && durationSeconds > 0 ? durationSeconds + 30 : Number.POSITIVE_INFINITY; + + if (typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= max) { + return Math.floor(value); + } + + if (!quote || !segments?.length) return null; + const needle = normalizeForMatch(stripMarkers(quote)); + if (needle.length < 12) return null; + + for (let index = 0; index < segments.length; index += 1) { + const window = segments + .slice(index, index + 3) + .map((segment) => segment.text) + .join(" "); + if (normalizeForMatch(window).includes(needle)) return Math.floor(segments[index].start); + } + + return null; +} + +function buildChapters( + input: ExtractionInput, + proposed: Array<{ startSeconds: number; title: string }>, +): Chapter[] { + const isVideo = input.resource.kind === "video"; + if (!isVideo) return []; + + const descriptionChapters = parseDescriptionChapters(input.resource.description); + const duration = input.resource.durationSeconds && input.resource.durationSeconds > 0 ? input.resource.durationSeconds : null; + const externalId = input.resource.externalId ?? ""; + + const fromDescription: Chapter[] = descriptionChapters + .filter((chapter) => !duration || chapter.startSeconds <= duration) + .map((chapter) => ({ + startSeconds: chapter.startSeconds, + title: clip(chapter.title, 120), + url: externalId ? deepLink(externalId, chapter.startSeconds) : `${input.resource.id}#t=${chapter.startSeconds}`, + })); + + const source = + fromDescription.length >= 2 + ? fromDescription + : proposed + .filter((chapter) => Number.isFinite(chapter.startSeconds) && chapter.startSeconds >= 0) + .filter((chapter) => !duration || chapter.startSeconds <= duration + 30) + .map((chapter) => ({ + startSeconds: Math.floor(chapter.startSeconds), + title: clip(chapter.title, 120), + url: externalId ? deepLink(externalId, chapter.startSeconds) : `${input.resource.id}#t=${chapter.startSeconds}`, + })); + + const sorted = source + .filter((chapter, index, all) => all.findIndex((other) => other.startSeconds === chapter.startSeconds) === index) + .sort((left, right) => left.startSeconds - right.startSeconds) + .slice(0, 20); + + return sorted.map((chapter, index) => ({ + ...chapter, + endSeconds: + sorted[index + 1]?.startSeconds ?? + (duration ? Math.max(chapter.startSeconds + 30, duration) : null), + })); +} + +function mergeKeyPoints( + outputs: ExtractionOutput[], + input: ExtractionInput, +): KeyPoint[] { + const source = input.text; + const isVideo = input.resource.kind === "video"; + const seen = new Set(); + const points: KeyPoint[] = []; + + for (const output of outputs) { + for (const point of output.keyPoints) { + const text = clip(point.text, 320); + if (text.length < 12) continue; + + const key = normalizeForMatch(text); + if (seen.has(key)) continue; + + const quote = point.quote ? clip(point.quote, 400) : null; + if (quote && source && !quoteAppearsIn(stripMarkers(quote), source)) continue; + if (!quote && isVideo) continue; + + const atSeconds = isVideo ? resolveTimestamp(point.atSeconds, quote, input.segments, input.resource.durationSeconds) : null; + seen.add(key); + points.push({ text, quote, atSeconds }); + } + } + + return points.slice(0, 10); +} + +function mergeSummary(outputs: ExtractionOutput[], fallback: string): string { + const seen = new Set(); + const sentences: string[] = []; + + for (const output of outputs) { + for (const sentence of output.summary.split(/(?<=[.!?])\s+/)) { + const clean = sentence.replace(/\s+/g, " ").trim(); + if (clean.length < 20) continue; + const key = clean.toLowerCase(); + if (seen.has(key)) continue; + seen.add(key); + sentences.push(clean); + } + } + + return clip(sentences.slice(0, 4).join(" ") || fallback, 900); +} + +function chunkPrompt(input: ExtractionInput, chunk: string, index: number, total: number) { + const lines = [ + `Resource: ${input.resource.title}`, + `Creator: ${input.resource.creator ?? "unknown"}`, + `Kind: ${input.resource.kind}`, + `Duration: ${input.resource.durationSeconds ? `${Math.round(input.resource.durationSeconds / 60)} minutes` : "unknown"}`, + `Description: ${clip(input.resource.description ?? "", 1200) || "(none)"}`, + ]; + + if (total > 1) lines.push(`Text part: ${index + 1} of ${total}`); + lines.push("", "Text:", chunk); + return lines.join("\n"); +} + +/** + * Runs extraction over one resource. A provider or budget failure degrades to + * a deterministic summary instead of failing the whole pack. + */ +export async function extractResource(input: ExtractionInput): Promise { + const fallbackSummary = sentenceFallback(input); + + if (!input.text.trim()) { + return { + summary: fallbackSummary, + keyPoints: [], + skills: uniqueSkills([input.resource.title.split(/[^a-zA-Z0-9+#]+/).slice(0, 3).join(" ")]), + chapters: buildChapters(input, []), + degraded: true, + error: "No text available for extraction", + }; + } + + const chunks = pickChunks(input.text); + const outputs: ExtractionOutput[] = []; + + try { + for (let index = 0; index < chunks.length; index += 1) { + const result = await scoutGenerateObject({ + packId: input.packId, + schema: extractionSchema, + system: SCOUT_PROMPTS.extract.body, + prompt: chunkPrompt(input, chunks[index], index, chunks.length), + config: input.config, + }); + outputs.push(result.object); + } + } catch (error) { + const message = error instanceof ScoutError ? error.message : error instanceof Error ? error.message : "Extraction failed"; + return { + summary: fallbackSummary, + keyPoints: [], + skills: uniqueSkills([input.resource.title]), + chapters: buildChapters(input, []), + degraded: true, + error: message, + }; + } + + const skills = uniqueSkills(outputs.flatMap((output) => output.skills)); + const chapters = buildChapters(input, outputs.flatMap((output) => output.chapters)); + + return { + summary: mergeSummary(outputs, fallbackSummary), + keyPoints: mergeKeyPoints(outputs, input), + skills: skills.length > 0 ? skills : uniqueSkills([input.resource.title]), + chapters, + degraded: false, + }; +} + +/** + * Books are never fetched. The outline is a suggestion the UI labels as such; + * a single "Read the book" task is the honest fallback. + */ +export async function suggestBookOutline(input: { + packId?: string | null; + title: string; + creator?: string | null; + config?: Partial | null; +}): Promise<{ chapters: string[]; confident: boolean }> { + try { + const result = await scoutGenerateObject({ + packId: input.packId, + schema: bookOutlineSchema, + system: SCOUT_PROMPTS.book.body, + prompt: `Title: ${input.title}\nAuthor: ${input.creator ?? "unknown"}`, + config: input.config, + }); + + const chapters = result.object.chapters.map((chapter) => clip(chapter, 120)).filter(Boolean).slice(0, 12); + if (chapters.length === 0) return { chapters: ["Read the book"], confident: false }; + return { chapters, confident: result.object.confident }; + } catch { + return { chapters: ["Read the book"], confident: false }; + } +} diff --git a/src/lib/scout/format.ts b/src/lib/scout/format.ts new file mode 100644 index 0000000..3345cba --- /dev/null +++ b/src/lib/scout/format.ts @@ -0,0 +1,130 @@ +/** Pure helpers for timestamps, durations, chapter parsing and quote checks. */ + +export interface ParsedChapter { + startSeconds: number; + title: string; +} + +/** "1:23", "01:02:03" and "0:00" all become seconds. Returns null otherwise. */ +export function parseTimestamp(value: string): number | null { + const match = value.trim().match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/); + if (!match) return null; + + const first = Number(match[1]); + const second = Number(match[2]); + const third = match[3] === undefined ? null : Number(match[3]); + + if (second > 59 || (third !== null && third > 59)) return null; + return third === null ? first * 60 + second : first * 3600 + second * 60 + third; +} + +export function formatTimestamp(totalSeconds: number): string { + const safe = Math.max(0, Math.floor(totalSeconds)); + const hours = Math.floor(safe / 3600); + const minutes = Math.floor((safe % 3600) / 60); + const seconds = safe % 60; + const pad = (value: number) => String(value).padStart(2, "0"); + return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${minutes}:${pad(seconds)}`; +} + +export function deepLink(videoId: string, startSeconds?: number | null): string { + const base = `https://youtu.be/${videoId}`; + if (startSeconds === undefined || startSeconds === null || startSeconds <= 0) return base; + return `${base}?t=${Math.floor(startSeconds)}`; +} + +/** + * Pulls chapter headings out of a YouTube description. + * + * Accepts the shapes creators actually use: "00:00 Intro", "0:00 - Intro", + * "(12:30) Demo" and "1:02:03 Deep dive". + */ +export function parseDescriptionChapters(description: string | null | undefined): ParsedChapter[] { + if (!description) return []; + + const chapters: ParsedChapter[] = []; + for (const rawLine of description.split(/\r?\n/)) { + const line = rawLine.trim().replace(/^[-*•]\s*/, ""); + const match = line.match(/^\(?(\d{1,2}:\d{2}(?::\d{2})?)\)?\s*[-–—:.)]?\s*(.+)$/); + if (!match) continue; + + const startSeconds = parseTimestamp(match[1]); + const title = match[2].trim(); + if (startSeconds === null || title.length < 2) continue; + if (chapters.some((chapter) => chapter.startSeconds === startSeconds)) continue; + chapters.push({ startSeconds, title: title.slice(0, 120) }); + } + + return chapters.sort((left, right) => left.startSeconds - right.startSeconds); +} + +/** ISO-8601 duration ("PT1H2M10S") to seconds. */ +export function parseIsoDuration(value: string | null | undefined): number | null { + if (!value) return null; + const match = value.match(/^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/); + if (!match) return null; + const [, days, hours, minutes, seconds] = match; + const total = Number(days ?? 0) * 86400 + Number(hours ?? 0) * 3600 + Number(minutes ?? 0) * 60 + Number(seconds ?? 0); + return total > 0 ? total : null; +} + +export function humanDuration(totalSeconds: number | null | undefined): string { + if (!totalSeconds || totalSeconds <= 0) return "—"; + const minutes = Math.round(totalSeconds / 60); + if (minutes < 60) return `${minutes} min`; + const hours = Math.floor(minutes / 60); + const rest = minutes % 60; + return rest === 0 ? `${hours} hr` : `${hours} hr ${rest} min`; +} + +/** Lowercase, collapse whitespace and strip punctuation so quotes can be matched. */ +export function normalizeForMatch(value: string): string { + return value + .toLowerCase() + .replace(/[\u2018\u2019]/g, "'") + .replace(/[\u201c\u201d]/g, '"') + .replace(/[^a-z0-9' ]+/g, " ") + .replace(/\s+/g, " ") + .trim(); +} + +/** + * True when the quote really appears in the source text. Short quotes are + * rejected because they match almost anything. + */ +export function quoteAppearsIn(quote: string, source: string): boolean { + const needle = normalizeForMatch(quote); + if (needle.length < 12) return false; + return normalizeForMatch(source).includes(needle); +} + +/** Split long text into overlapping chunks for summarisation. */ +export function chunkText(text: string, size = 6000, overlap = 400): string[] { + const clean = text.trim(); + if (!clean) return []; + if (clean.length <= size) return [clean]; + + const chunks: string[] = []; + let start = 0; + while (start < clean.length && chunks.length < 12) { + const end = Math.min(clean.length, start + size); + let slice = clean.slice(start, end); + + // Prefer to cut at a paragraph boundary near the end of the window. + if (end < clean.length) { + const boundary = slice.lastIndexOf("\n\n"); + if (boundary > size * 0.6) slice = slice.slice(0, boundary); + } + + chunks.push(slice.trim()); + const advance = Math.max(1, slice.length - overlap); + start += advance; + } + + return chunks.filter(Boolean); +} + +export function clip(value: string, limit: number): string { + const clean = value.replace(/\s+/g, " ").trim(); + return clean.length <= limit ? clean : `${clean.slice(0, Math.max(0, limit - 1)).trimEnd()}…`; +} diff --git a/src/lib/scout/jobs.ts b/src/lib/scout/jobs.ts new file mode 100644 index 0000000..be72ea7 --- /dev/null +++ b/src/lib/scout/jobs.ts @@ -0,0 +1,134 @@ +import { getScoutDb, newId, nowIso } from "./db"; +import type { JobKind, JobRow } from "./types"; + +export interface EnqueueOptions { + runAfter?: Date | string; + maxAttempts?: number; +} + +/** + * Thrown by a handler that cannot run yet (for example, the transcript + * throttle). The runner requeues the job without spending an attempt. + */ +export class JobDeferred extends Error { + delayMs: number; + + constructor(delayMs: number, message = "Deferred until the next slot") { + super(message); + this.name = "JobDeferred"; + this.delayMs = Math.max(0, delayMs); + } +} + +function asIso(value: Date | string | undefined) { + if (!value) return nowIso(); + return typeof value === "string" ? value : value.toISOString(); +} + +export function enqueueJob(kind: JobKind, payload: Record = {}, options: EnqueueOptions = {}): string { + const db = getScoutDb(); + const id = newId("job"); + const now = nowIso(); + db.prepare( + `INSERT INTO jobs (id, kind, status, payload, attempts, max_attempts, run_after, created_at, updated_at) + VALUES (?, ?, 'queued', ?, 0, ?, ?, ?, ?)`, + ).run(id, kind, JSON.stringify(payload), options.maxAttempts ?? 3, asIso(options.runAfter), now, now); + return id; +} + +export function getJob(id: string): JobRow | null { + return (getScoutDb().prepare(`SELECT * FROM jobs WHERE id = ?`).get(id) as JobRow | undefined) ?? null; +} + +/** + * Atomically take the oldest due job. The read and the claim happen in one + * transaction so two runners (or two dev restarts) cannot run the same job. + */ +export function claimNextJob(now = new Date()): JobRow | null { + const db = getScoutDb(); + const claim = db.transaction(() => { + const iso = now.toISOString(); + const row = db + .prepare(`SELECT * FROM jobs WHERE status = 'queued' AND run_after <= ? ORDER BY run_after, created_at LIMIT 1`) + .get(iso) as JobRow | undefined; + + if (!row) return null; + + db.prepare(`UPDATE jobs SET status = 'running', locked_at = ?, attempts = attempts + 1, updated_at = ? WHERE id = ?`).run( + iso, + iso, + row.id, + ); + + return db.prepare(`SELECT * FROM jobs WHERE id = ?`).get(row.id) as JobRow; + }); + + return claim(); +} + +export function completeJob(id: string, result: unknown) { + const now = nowIso(); + getScoutDb() + .prepare(`UPDATE jobs SET status = 'done', result = ?, error = NULL, locked_at = NULL, updated_at = ? WHERE id = ?`) + .run(JSON.stringify(result ?? null), now, id); +} + +export function failJob(id: string, error: unknown) { + const db = getScoutDb(); + const row = getJob(id); + if (!row) return; + + const message = error instanceof Error ? error.message : String(error ?? "Unknown error"); + const now = nowIso(); + + if (row.attempts >= row.max_attempts) { + db.prepare(`UPDATE jobs SET status = 'failed', error = ?, locked_at = NULL, updated_at = ? WHERE id = ?`).run( + message.slice(0, 2000), + now, + id, + ); + return; + } + + const backoffMs = Math.min(10 * 60_000, 30_000 * 4 ** Math.max(0, row.attempts - 1)); + db.prepare( + `UPDATE jobs SET status = 'queued', error = ?, locked_at = NULL, run_after = ?, updated_at = ? WHERE id = ?`, + ).run(message.slice(0, 2000), new Date(Date.now() + backoffMs).toISOString(), now, id); +} + +/** Put a job back on the queue without counting it as a failed attempt. */ +export function deferJob(id: string, delayMs: number, reason?: string) { + const now = nowIso(); + getScoutDb() + .prepare( + `UPDATE jobs SET status = 'queued', attempts = MAX(0, attempts - 1), locked_at = NULL, + run_after = ?, error = ?, updated_at = ? WHERE id = ?`, + ) + .run(new Date(Date.now() + Math.max(0, delayMs)).toISOString(), reason ?? null, now, id); +} + +/** Move jobs abandoned by a crash or a killed dev server back to the queue. */ +export function recoverStaleJobs(maxAgeMs = 30 * 60_000) { + const cutoff = new Date(Date.now() - maxAgeMs).toISOString(); + return getScoutDb() + .prepare(`UPDATE jobs SET status = 'queued', locked_at = NULL WHERE status = 'running' AND locked_at < ?`) + .run(cutoff).changes; +} + +export function listJobs(limit = 25): JobRow[] { + return getScoutDb() + .prepare(`SELECT * FROM jobs ORDER BY created_at DESC LIMIT ?`) + .all(limit) as JobRow[]; +} + +export function countJobs(status: JobRow["status"]) { + const row = getScoutDb().prepare(`SELECT COUNT(*) AS count FROM jobs WHERE status = ?`).get(status) as { count: number }; + return row.count; +} + +export function hasQueuedJob(kind: JobKind) { + const row = getScoutDb() + .prepare(`SELECT COUNT(*) AS count FROM jobs WHERE status IN ('queued', 'running') AND kind = ?`) + .get(kind) as { count: number }; + return row.count > 0; +} diff --git a/src/lib/scout/llm.ts b/src/lib/scout/llm.ts new file mode 100644 index 0000000..8406893 --- /dev/null +++ b/src/lib/scout/llm.ts @@ -0,0 +1,146 @@ +import { createOpenAI } from "@ai-sdk/openai"; +import { generateObject } from "ai"; +import type { z } from "zod"; +import { + DEFAULT_PROVIDER_MODELS, + isAiProvider, + PROVIDER_BASE_URLS, + PROVIDER_ENV_KEYS, + type AIProvider, +} from "@/lib/ai-config"; +import { getScoutSettings } from "./settings"; +import { hasBudget, monthSpendCents, recordSpend } from "./spend"; + +export interface ScoutLlmConfig { + provider: AIProvider; + model: string; + apiKey?: string | null; +} + +export class ScoutError extends Error { + code: "budget" | "config" | "provider"; + + constructor(code: ScoutError["code"], message: string) { + super(message); + this.name = "ScoutError"; + this.code = code; + } +} + +/** + * Packs hold their provider choice for the life of the run. The API key is + * kept in process memory only: it is never written to SQLite or returned by + * an endpoint, and a restart simply asks the user to start again. + */ +const globalKey = Symbol.for("syllo.scout.llm"); + +type GlobalWithSecrets = typeof globalThis & { [globalKey]?: Map }; + +function secretStore() { + const store = globalThis as GlobalWithSecrets; + store[globalKey] ??= new Map(); + return store[globalKey]; +} + +export function defaultScoutConfig(): ScoutLlmConfig { + const envProvider = process.env.SCOUT_PROVIDER?.trim(); + const provider: AIProvider = isAiProvider(envProvider) ? envProvider : "deepseek"; + return { + provider, + // DeepSeek exposes V4.1 Flash through the stable `deepseek-chat` + // alias. Override with SCOUT_MODEL when a pinned id is preferred. + model: process.env.SCOUT_MODEL?.trim() || DEFAULT_PROVIDER_MODELS[provider], + }; +} + +export function rememberScoutLlm(packId: string, config: ScoutLlmConfig) { + secretStore().set(packId, { ...config, apiKey: config.apiKey?.trim() || null }); +} + +export function recallScoutLlm(packId: string): ScoutLlmConfig | null { + return secretStore().get(packId) ?? null; +} + +export function forgetScoutLlm(packId: string) { + secretStore().delete(packId); +} + +export function resolveScoutLlm(override?: Partial | null): ScoutLlmConfig { + const base = defaultScoutConfig(); + const provider = override?.provider && isAiProvider(override.provider) ? override.provider : base.provider; + const model = override?.model?.trim() || (provider === base.provider ? base.model : DEFAULT_PROVIDER_MODELS[provider]); + const apiKey = override?.apiKey?.trim() || process.env[PROVIDER_ENV_KEYS[provider]]?.trim() || ""; + return { provider, model, apiKey }; +} + +export interface ScoutGenerateInput { + packId?: string | null; + schema: z.ZodType; + system: string; + prompt: string; + config?: Partial | null; +} + +export interface ScoutGenerateResult { + object: T; + inputTokens: number; + outputTokens: number; +} + +/** + * Structured generation with a hard budget gate. Scout uses the same provider + * map as the studio, so a DeepSeek/Gemini/OpenAI key already configured works + * here without a second settings screen. + */ +export async function scoutGenerateObject(input: ScoutGenerateInput): Promise> { + const settings = getScoutSettings(); + if (!hasBudget(settings.monthlyBudgetCents)) { + throw new ScoutError( + "budget", + `Scout reached its monthly budget of $${(settings.monthlyBudgetCents / 100).toFixed(2)} (spent $${(monthSpendCents() / 100).toFixed(2)}). Raise the budget in Scout settings to continue.`, + ); + } + + const stored = input.packId ? recallScoutLlm(input.packId) : null; + const config = resolveScoutLlm({ ...stored, ...input.config }); + + if (!config.apiKey) { + throw new ScoutError( + "config", + `Scout needs a ${config.provider} API key. Add one in Settings, set ${PROVIDER_ENV_KEYS[config.provider]} on the server, or pick another provider.`, + ); + } + + const baseURL = PROVIDER_BASE_URLS[config.provider]; + const provider = createOpenAI({ + apiKey: config.apiKey, + ...(baseURL ? { baseURL } : {}), + }); + + try { + const result = await generateObject({ + model: provider.chat(config.model), + schema: input.schema, + system: input.system, + prompt: input.prompt, + temperature: 0.2, + }); + + const inputTokens = result.usage?.inputTokens ?? 0; + const outputTokens = result.usage?.outputTokens ?? 0; + + recordSpend({ + packId: input.packId ?? null, + provider: config.provider, + model: config.model, + inputTokens, + outputTokens, + }); + + return { object: result.object as T, inputTokens, outputTokens }; + } catch (error) { + if (error instanceof ScoutError) throw error; + const message = error instanceof Error ? error.message : "Unknown provider error"; + throw new ScoutError("provider", `Scout could not reach ${config.provider}: ${message.slice(0, 300)}`); + } +} diff --git a/src/lib/scout/prompts.ts b/src/lib/scout/prompts.ts new file mode 100644 index 0000000..7750ce6 --- /dev/null +++ b/src/lib/scout/prompts.ts @@ -0,0 +1,41 @@ +import extractRaw from "../../../prompts/scout/extract.v1.md?raw"; +import planRaw from "../../../prompts/scout/plan.v1.md?raw"; +import quizRaw from "../../../prompts/scout/quiz.v1.md?raw"; +import bookRaw from "../../../prompts/scout/book.v1.md?raw"; + +export interface ScoutPrompt { + version: number; + purpose: string; + body: string; +} + +function parsePrompt(raw: string): ScoutPrompt { + const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/); + if (!match) return { version: 1, purpose: "", body: raw.trim() }; + + const frontmatter = match[1]; + const read = (key: string) => frontmatter.match(new RegExp(`^${key}:\\s*(.+)$`, "m"))?.[1]?.trim() ?? ""; + + return { + version: Number(read("version")) || 1, + purpose: read("purpose"), + body: match[2].trim(), + }; +} + +/** + * Prompts live in prompts/scout/*.v1.md so a change is reviewable in a diff. + * Bump the filename when the contract changes. + */ +export const SCOUT_PROMPTS = { + extract: parsePrompt(extractRaw), + plan: parsePrompt(planRaw), + quiz: parsePrompt(quizRaw), + book: parsePrompt(bookRaw), +} as const; + +export const SCOUT_PROMPT_VERSION = [ + `extract.v${SCOUT_PROMPTS.extract.version}`, + `plan.v${SCOUT_PROMPTS.plan.version}`, + `quiz.v${SCOUT_PROMPTS.quiz.version}`, +].join("+"); diff --git a/src/lib/scout/runner.ts b/src/lib/scout/runner.ts new file mode 100644 index 0000000..fbd689a --- /dev/null +++ b/src/lib/scout/runner.ts @@ -0,0 +1,134 @@ +import { claimNextJob, completeJob, countJobs, deferJob, failJob, recoverStaleJobs, enqueueJob, hasQueuedJob, JobDeferred } from "./jobs"; +import { getScoutSettings, markAutoCheckScheduled } from "./settings"; +import type { JobKind, JobRow } from "./types"; + +export type JobHandler = (job: JobRow, payload: Record) => Promise; + +const handlers: Partial> = {}; + +export function registerJobHandler(kind: JobKind, handler: JobHandler) { + handlers[kind] = handler; +} + +const globalKey = Symbol.for("syllo.scout.runner"); + +type GlobalWithRunner = typeof globalThis & { + [globalKey]?: { started: boolean; timer?: ReturnType; scheduler?: ReturnType }; +}; + +function runnerState() { + const store = globalThis as GlobalWithRunner; + if (!store[globalKey]) store[globalKey] = { started: false }; + return store[globalKey]; +} + +const POLL_MS = 1500; +const SCHEDULER_MS = 60_000; + +/** + * Start the in-process queue loop once per process. + * + * The loop is deliberately not awaited anywhere: server functions return + * immediately and the UI polls the pack. + */ +export function ensureScoutRunner() { + const state = runnerState(); + if (state.started) return; + state.started = true; + + recoverStaleJobs(); + void tick(); + + state.scheduler = setInterval(() => { + void scheduleNightlyCheck(); + }, SCHEDULER_MS); + state.scheduler.unref?.(); + + void scheduleNightlyCheck(); +} + +async function tick() { + const state = runnerState(); + if (!state.started) return; + + try { + await runDueJobsOnce(); + } catch (error) { + console.error("[scout] queue tick failed:", error); + } + + state.timer = setTimeout(() => { + void tick(); + }, POLL_MS); + state.timer.unref?.(); +} + +/** Runs every due job once. Exported so tests can drive the queue deterministically. */ +export async function runDueJobsOnce(limit = 5) { + for (let index = 0; index < limit; index += 1) { + const job = claimNextJob(); + if (!job) return; + + if (job.kind === "auto_check" && getScoutSettings().paused) { + deferJob(job.id, 60 * 60_000, "Scout is paused"); + continue; + } + + const handler = handlers[job.kind]; + if (!handler) { + failJob(job.id, new Error(`No handler registered for job kind "${job.kind}"`)); + continue; + } + + try { + const payload = JSON.parse(job.payload || "{}") as Record; + const result = await handler(job, payload); + completeJob(job.id, result ?? null); + } catch (error) { + if (error instanceof JobDeferred) { + deferJob(job.id, error.delayMs, error.message); + continue; + } + console.error(`[scout] job ${job.kind} (${job.id}) failed:`, error); + failJob(job.id, error); + } + } +} + +/** Dubai has no daylight saving, so a fixed zone lookup is enough. */ +export function dubaiClock(now = new Date()) { + const parts = new Intl.DateTimeFormat("en-CA", { + timeZone: "Asia/Dubai", + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + hour12: false, + }).formatToParts(now); + + const pick = (type: string) => parts.find((part) => part.type === type)?.value ?? ""; + return { date: `${pick("year")}-${pick("month")}-${pick("day")}`, hour: Number(pick("hour")) }; +} + +/** + * The nightly check runs at 03:00 Asia/Dubai. The scheduler polls every + * minute, so anything in the 03:00 hour counts, and the stored date keeps it + * to one run per night. + */ +export async function scheduleNightlyCheck(now = new Date()) { + const settings = getScoutSettings(); + if (!settings.autoCheck || settings.paused) return false; + + const clock = dubaiClock(now); + if (clock.hour !== 3) return false; + if (settings.lastAutoCheckOn === clock.date) return false; + if (hasQueuedJob("auto_check")) return false; + + enqueueJob("auto_check", { scheduledFor: clock.date }); + markAutoCheckScheduled(clock.date); + return true; +} + +export function runnerHealth() { + return { queued: countJobs("queued"), running: countJobs("running"), failed: countJobs("failed") }; +} diff --git a/src/lib/scout/schema.sql b/src/lib/scout/schema.sql new file mode 100644 index 0000000..539b5ec --- /dev/null +++ b/src/lib/scout/schema.sql @@ -0,0 +1,112 @@ +PRAGMA journal_mode = WAL; +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS jobs ( + id TEXT PRIMARY KEY, + kind TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'queued', + payload TEXT NOT NULL DEFAULT '{}', + result TEXT, + error TEXT, + attempts INTEGER NOT NULL DEFAULT 0, + max_attempts INTEGER NOT NULL DEFAULT 3, + run_after TEXT NOT NULL, + locked_at TEXT, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS jobs_claim ON jobs (status, run_after); + +CREATE TABLE IF NOT EXISTS packs ( + id TEXT PRIMARY KEY, + goal TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'discovering', + progress INTEGER NOT NULL DEFAULT 0, + stage TEXT NOT NULL DEFAULT 'Discovering free resources', + picks TEXT NOT NULL DEFAULT '[]', + roadmap_id TEXT, + roadmap TEXT, + quizzes TEXT, + cost_cents REAL NOT NULL DEFAULT 0, + error TEXT, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS resources ( + id TEXT PRIMARY KEY, + kind TEXT NOT NULL, + source TEXT NOT NULL, + external_id TEXT, + url TEXT NOT NULL, + title TEXT NOT NULL, + creator TEXT, + duration_seconds INTEGER, + published_at TEXT, + thumbnail_url TEXT, + description TEXT, + summary TEXT, + key_points TEXT NOT NULL DEFAULT '[]', + skill_tags TEXT NOT NULL DEFAULT '[]', + chapters TEXT NOT NULL DEFAULT '[]', + transcript TEXT, + transcript_status TEXT NOT NULL DEFAULT 'pending', + transcript_chars INTEGER NOT NULL DEFAULT 0, + content_hash TEXT, + error TEXT, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS resources_url ON resources (url); + +CREATE TABLE IF NOT EXISTS pack_resources ( + pack_id TEXT NOT NULL REFERENCES packs (id) ON DELETE CASCADE, + resource_id TEXT NOT NULL REFERENCES resources (id) ON DELETE CASCADE, + position INTEGER NOT NULL DEFAULT 0, + selected INTEGER NOT NULL DEFAULT 1, + PRIMARY KEY (pack_id, resource_id) +); + +CREATE TABLE IF NOT EXISTS sources ( + id TEXT PRIMARY KEY, + kind TEXT NOT NULL, + label TEXT NOT NULL, + value TEXT NOT NULL, + enabled INTEGER NOT NULL DEFAULT 1, + meta TEXT NOT NULL DEFAULT '{}', + last_checked_at TEXT, + created_at TEXT NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS sources_kind_value ON sources (kind, value); + +CREATE TABLE IF NOT EXISTS scout_settings ( + id INTEGER PRIMARY KEY CHECK (id = 1), + auto_check INTEGER NOT NULL DEFAULT 1, + paused INTEGER NOT NULL DEFAULT 0, + monthly_budget_cents INTEGER NOT NULL DEFAULT 500, + last_auto_check_on TEXT, + updated_at TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS spend_log ( + id TEXT PRIMARY KEY, + pack_id TEXT, + provider TEXT, + model TEXT, + input_tokens INTEGER NOT NULL DEFAULT 0, + output_tokens INTEGER NOT NULL DEFAULT 0, + cost_cents REAL NOT NULL DEFAULT 0, + created_at TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS spend_month ON spend_log (created_at); + +CREATE TABLE IF NOT EXISTS channel_cache ( + channel_id TEXT PRIMARY KEY, + uploads_playlist_id TEXT NOT NULL, + checked_at TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS scout_kv ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL +); diff --git a/src/lib/scout/settings.ts b/src/lib/scout/settings.ts new file mode 100644 index 0000000..d697e00 --- /dev/null +++ b/src/lib/scout/settings.ts @@ -0,0 +1,54 @@ +import { getScoutDb, nowIso } from "./db"; +import type { ScoutSettings } from "./types"; + +interface SettingsRow { + auto_check: number; + paused: number; + monthly_budget_cents: number; + last_auto_check_on: string | null; +} + +function readRow(): SettingsRow { + return getScoutDb() + .prepare(`SELECT auto_check, paused, monthly_budget_cents, last_auto_check_on FROM scout_settings WHERE id = 1`) + .get() as SettingsRow; +} + +export function getScoutSettings(): ScoutSettings { + const row = readRow(); + return { + autoCheck: row.auto_check === 1, + paused: row.paused === 1, + monthlyBudgetCents: row.monthly_budget_cents, + lastAutoCheckOn: row.last_auto_check_on, + }; +} + +export function updateScoutSettings(patch: Partial): ScoutSettings { + const current = getScoutSettings(); + const next: ScoutSettings = { ...current, ...patch }; + + getScoutDb() + .prepare( + `UPDATE scout_settings SET auto_check = ?, paused = ?, monthly_budget_cents = ?, last_auto_check_on = ?, updated_at = ? WHERE id = 1`, + ) + .run( + next.autoCheck ? 1 : 0, + next.paused ? 1 : 0, + Math.max(0, Math.round(next.monthlyBudgetCents)), + next.lastAutoCheckOn ?? null, + nowIso(), + ); + + return next; +} + +export function markAutoCheckScheduled(date: string) { + getScoutDb() + .prepare(`UPDATE scout_settings SET last_auto_check_on = ?, updated_at = ? WHERE id = 1`) + .run(date, nowIso()); +} + +export function isScoutPaused() { + return getScoutSettings().paused; +} diff --git a/src/lib/scout/sources.ts b/src/lib/scout/sources.ts new file mode 100644 index 0000000..7a5d2fd --- /dev/null +++ b/src/lib/scout/sources.ts @@ -0,0 +1,81 @@ +import { getScoutDb, newId, nowIso } from "./db"; +import type { ScoutSource, ScoutSourceKind } from "./types"; + +interface SourceRow { + id: string; + kind: string; + label: string; + value: string; + enabled: number; + meta: string; + last_checked_at: string | null; + created_at: string; +} + +const KINDS: ScoutSourceKind[] = ["youtube_channel", "site", "creator", "book"]; + +export function isSourceKind(value: unknown): value is ScoutSourceKind { + return typeof value === "string" && KINDS.includes(value as ScoutSourceKind); +} + +function toSource(row: SourceRow): ScoutSource { + return { + id: row.id, + kind: row.kind as ScoutSourceKind, + label: row.label, + value: row.value, + enabled: row.enabled === 1, + lastCheckedAt: row.last_checked_at, + createdAt: row.created_at, + }; +} + +export function listSources(): ScoutSource[] { + return (getScoutDb().prepare(`SELECT * FROM sources ORDER BY created_at`).all() as SourceRow[]).map(toSource); +} + +export function listEnabledSources(kind?: ScoutSourceKind): ScoutSource[] { + return listSources().filter((source) => source.enabled && (!kind || source.kind === kind)); +} + +export function addSource(input: { kind: ScoutSourceKind; label: string; value: string }): ScoutSource { + const db = getScoutDb(); + const value = input.value.trim(); + const label = input.label.trim() || value; + const existing = db.prepare(`SELECT * FROM sources WHERE kind = ? AND value = ?`).get(input.kind, value) as SourceRow | undefined; + if (existing) return toSource(existing); + + const id = newId("src"); + db.prepare( + `INSERT INTO sources (id, kind, label, value, enabled, meta, created_at) VALUES (?, ?, ?, ?, 1, '{}', ?)`, + ).run(id, input.kind, label, value, nowIso()); + + return toSource(db.prepare(`SELECT * FROM sources WHERE id = ?`).get(id) as SourceRow); +} + +export function setSourceEnabled(id: string, enabled: boolean) { + return getScoutDb().prepare(`UPDATE sources SET enabled = ? WHERE id = ?`).run(enabled ? 1 : 0, id).changes > 0; +} + +export function removeSource(id: string) { + return getScoutDb().prepare(`DELETE FROM sources WHERE id = ?`).run(id).changes > 0; +} + +export function touchSource(id: string) { + getScoutDb().prepare(`UPDATE sources SET last_checked_at = ? WHERE id = ?`).run(nowIso(), id); +} + +/** + * Accepts a bare @handle, a full channel URL or a channel id and returns the + * canonical channel id when one is already present. + */ +export function normalizeChannelValue(input: string): string { + const value = input.trim(); + const direct = value.match(/^UC[\w-]{20,}$/); + if (direct) return value; + + const channelUrl = value.match(/youtube\.com\/channel\/(UC[\w-]+)/i); + if (channelUrl) return channelUrl[1]; + + return value; +} diff --git a/src/lib/scout/spend.ts b/src/lib/scout/spend.ts new file mode 100644 index 0000000..3de6a36 --- /dev/null +++ b/src/lib/scout/spend.ts @@ -0,0 +1,75 @@ +import { getScoutDb, newId, nowIso } from "./db"; + +/** + * Published list prices in USD per million tokens. These are estimates for + * the cost meter, not billing. Override with SCOUT_PRICE_* env vars when a + * provider changes pricing. + */ +const PRICES: Record = { + deepseek: { input: 0.28, output: 0.42 }, + gemini: { input: 0.1, output: 0.4 }, + openai: { input: 2.5, output: 10 }, + groq: { input: 0.59, output: 0.79 }, + openrouter: { input: 0.4, output: 1.6 }, +}; + +export interface SpendInput { + packId?: string | null; + provider: string; + model: string; + inputTokens?: number; + outputTokens?: number; +} + +export function estimateCostCents(input: SpendInput): number { + const price = PRICES[input.provider] ?? PRICES.deepseek; + const usd = + ((input.inputTokens ?? 0) * price.input + (input.outputTokens ?? 0) * price.output) / 1_000_000; + return Math.round(usd * 100 * 10_000) / 10_000; +} + +export function recordSpend(input: SpendInput) { + const cost = estimateCostCents(input); + const db = getScoutDb(); + + db.prepare( + `INSERT INTO spend_log (id, pack_id, provider, model, input_tokens, output_tokens, cost_cents, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + ).run( + newId("spend"), + input.packId ?? null, + input.provider, + input.model, + input.inputTokens ?? 0, + input.outputTokens ?? 0, + cost, + nowIso(), + ); + + if (input.packId) { + db.prepare(`UPDATE packs SET cost_cents = cost_cents + ?, updated_at = ? WHERE id = ?`).run(cost, nowIso(), input.packId); + } + + return cost; +} + +function monthStartIso(now = new Date()) { + return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)).toISOString(); +} + +export function monthSpendCents(now = new Date()) { + const row = getScoutDb() + .prepare(`SELECT COALESCE(SUM(cost_cents), 0) AS cents FROM spend_log WHERE created_at >= ?`) + .get(monthStartIso(now)) as { cents: number }; + return row.cents; +} + +export function packSpendCents(packId: string) { + const row = getScoutDb().prepare(`SELECT cost_cents FROM packs WHERE id = ?`).get(packId) as { cost_cents: number } | undefined; + return row?.cost_cents ?? 0; +} + +export function hasBudget(monthlyBudgetCents: number, now = new Date()) { + if (monthlyBudgetCents <= 0) return true; + return monthSpendCents(now) < monthlyBudgetCents; +} diff --git a/src/lib/scout/types.ts b/src/lib/scout/types.ts new file mode 100644 index 0000000..0c599cd --- /dev/null +++ b/src/lib/scout/types.ts @@ -0,0 +1,139 @@ +/** + * Shared Scout types. + * + * Everything here is plain data so it can cross the API boundary. The raw + * transcript never leaves the server: `ScoutResource` deliberately has no + * transcript field. + */ + +export type ScoutSourceKind = "youtube_channel" | "site" | "creator" | "book"; + +export interface ScoutSource { + id: string; + kind: ScoutSourceKind; + label: string; + value: string; + enabled: boolean; + lastCheckedAt?: string | null; + createdAt: string; +} + +export type ResourceKind = "video" | "article" | "doc" | "book"; + +export type TranscriptStatus = "pending" | "ok" | "unavailable" | "skipped"; + +export interface Chapter { + startSeconds: number; + endSeconds?: number | null; + title: string; + url: string; + /** True when the chapter list was suggested by the model, not supplied by the creator. */ + suggested?: boolean; +} + +export interface KeyPoint { + text: string; + atSeconds?: number | null; + quote?: string | null; +} + +export interface ScoutResource { + id: string; + kind: ResourceKind; + source: string; + externalId?: string | null; + url: string; + title: string; + creator?: string | null; + durationSeconds?: number | null; + publishedAt?: string | null; + thumbnailUrl?: string | null; + description?: string | null; + summary?: string | null; + keyPoints: KeyPoint[]; + skillTags: string[]; + chapters: Chapter[]; + transcriptStatus: TranscriptStatus; + transcriptChars: number; + error?: string | null; + createdAt: string; + updatedAt: string; + /** Per-pack selection, only present when the resource is read through a pack. */ + approved?: boolean; + approvedAt?: string | null; +} + +export type PackStatus = "discovering" | "processing" | "review" | "building" | "ready" | "failed"; + +export interface ScoutPack { + id: string; + goal: string; + status: PackStatus; + progress: number; + stage: string; + picks: string[]; + roadmapId?: string | null; + costCents: number; + error?: string | null; + createdAt: string; + updatedAt: string; + resources: ScoutResource[]; +} + +export interface ScoutSettings { + autoCheck: boolean; + paused: boolean; + monthlyBudgetCents: number; + lastAutoCheckOn?: string | null; +} + +export interface SpendSummary { + monthCents: number; + budgetCents: number; + packCents: number; +} + +export interface ScoutDigest { + weekOf: string; + newResources: number; + minutesWatched: number; + tasksCompleted: number; + reviewDue: number; + headline: string; + highlights: Array<{ title: string; url: string; skill?: string }>; +} + +export interface ScoutAgentStatus { + id: "opencode" | "codex" | "claude"; + label: string; + available: boolean; + version?: string | null; + command?: string | null; +} + +export interface ScoutAgentConnection { + id: ScoutAgentStatus["id"] | "chatgpt"; + label: string; + file: string; + snippet: string; + note: string; +} + +export type JobKind = "discover" | "process_resource" | "build_workspace" | "auto_check"; + +export type JobStatus = "queued" | "running" | "done" | "failed"; + +export interface JobRow { + id: string; + kind: JobKind; + status: JobStatus; + payload: string; + result: string | null; + error: string | null; + attempts: number; + max_attempts: number; + run_after: string; + locked_at: string | null; + created_at: string; + updated_at: string; +} diff --git a/src/types/index.ts b/src/types/index.ts index b3dd704..82e3623 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -40,6 +40,8 @@ export interface Roadmap { forkCount?: number; forkedFrom?: string | null; ownerId?: string; + /** Set when the roadmap was published by Scout; marks the workspace as generated. */ + packVersion?: string; } /* Section discriminated union */ diff --git a/vite.config.ts b/vite.config.ts index 6b9780a..e168171 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,6 +13,11 @@ export default defineConfig({ "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, + ssr: { + // better-sqlite3 ships a native addon; it must stay external instead + // of being pulled through the SSR bundle. + external: ["better-sqlite3"], + }, plugins: [ tailwindcss(), tanstackStart({ From d111b71babac9adeca4bcb7e070e588497d45a9f Mon Sep 17 00:00:00 2001 From: Screen <228304862+screen-gd@users.noreply.github.com> Date: Wed, 21 Oct 2026 04:19:40 +0400 Subject: [PATCH 10/20] Discover free resources and fetch their text YouTube discovery reads channel uploads or RSS, never search.list. Articles go through Jina Reader, and transcripts try the local helper before the hosted fallback. --- src/lib/scout/discovery/combine.ts | 92 +++++++ src/lib/scout/discovery/exa.ts | 124 +++++++++ src/lib/scout/discovery/index.ts | 81 ++++++ src/lib/scout/discovery/youtube.ts | 388 +++++++++++++++++++++++++++++ src/lib/scout/fetch/reader.ts | 120 +++++++++ src/lib/scout/fetch/transcripts.ts | 293 ++++++++++++++++++++++ 6 files changed, 1098 insertions(+) create mode 100644 src/lib/scout/discovery/combine.ts create mode 100644 src/lib/scout/discovery/exa.ts create mode 100644 src/lib/scout/discovery/index.ts create mode 100644 src/lib/scout/discovery/youtube.ts create mode 100644 src/lib/scout/fetch/reader.ts create mode 100644 src/lib/scout/fetch/transcripts.ts diff --git a/src/lib/scout/discovery/combine.ts b/src/lib/scout/discovery/combine.ts new file mode 100644 index 0000000..67db695 --- /dev/null +++ b/src/lib/scout/discovery/combine.ts @@ -0,0 +1,92 @@ +import { listEnabledSources } from "../sources"; +import { dedupeResources, isLikelyFree, type DiscoveredResource } from "./index"; +import { discoverSitesRecent, discoverWebForGoal } from "./exa"; +import { discoverYouTubeForGoal, discoverYouTubeRecent } from "./youtube"; + +/** + * Discovery is intentionally conservative: it reads only the channels and + * sites the learner enabled, plus an open-web query when an Exa key exists. + * Books are never discovered, because a book is a reading task, not a file. + */ +export async function discoverForGoal(input: { goal: string; limit?: number }): Promise { + const goal = input.goal.trim(); + const limit = Math.min(20, Math.max(4, input.limit ?? 12)); + + const sources = listEnabledSources(); + const channels = sources + .filter((source) => source.kind === "youtube_channel" || source.kind === "creator") + .map((source) => source.value); + const sites = sources.filter((source) => source.kind === "site").map((source) => source.value); + + const [videos, web] = await Promise.all([ + channels.length > 0 + ? discoverYouTubeForGoal({ goal, channels, limit: Math.ceil(limit * 0.7) }).catch((error) => { + console.warn("[scout] YouTube discovery failed:", error instanceof Error ? error.message : error); + return [] as DiscoveredResource[]; + }) + : Promise.resolve([] as DiscoveredResource[]), + discoverWebForGoal({ goal, limit: Math.ceil(limit * 0.6), sites }).catch((error) => { + console.warn("[scout] web discovery failed:", error instanceof Error ? error.message : error); + return [] as DiscoveredResource[]; + }), + ]); + + const clean = (items: DiscoveredResource[]) => + items.filter((item) => isLikelyFree(item.url) && !/#shorts/i.test(`${item.title} ${item.url}`)); + + const videoPool = clean(videos); + const webPool = clean(web); + const maxVideos = Math.max(3, Math.ceil(limit * 0.7)); + + const picked: DiscoveredResource[] = []; + const seen = new Set(); + const push = (item: DiscoveredResource) => { + const key = item.url.split("#")[0].replace(/\/$/, "").toLowerCase(); + if (seen.has(key) || picked.length >= limit) return; + seen.add(key); + picked.push(item); + }; + + // Alternating keeps the pack from becoming ten videos or ten articles. + const maxLength = Math.max(videoPool.length, webPool.length); + for (let index = 0; index < maxLength; index += 1) { + if (videoPool[index] && picked.filter((item) => item.kind === "video").length < maxVideos) { + push(videoPool[index]); + } + if (webPool[index]) push(webPool[index]); + if (picked.length >= limit) break; + } + + for (const item of [...videoPool, ...webPool]) push(item); + + return picked; +} + +/** + * Nightly check. RSS and site search only: no LLM, so it still works when the + * browser is closed and no per-pack key is available. + */ +export async function discoverNewFromSources(input: { + goal: string; + since: Date; + limit?: number; +}): Promise { + const limit = input.limit ?? 12; + const sources = listEnabledSources(); + const channels = sources + .filter((source) => source.kind === "youtube_channel" || source.kind === "creator") + .map((source) => source.value); + const sites = sources.filter((source) => source.kind === "site").map((source) => source.value); + + const [videos, web] = await Promise.all([ + channels.length > 0 + ? discoverYouTubeRecent({ channels, since: input.since, limit }).catch(() => []) + : Promise.resolve([] as DiscoveredResource[]), + discoverSitesRecent({ goal: input.goal, sites, limit: Math.ceil(limit / 2) }).catch(() => []), + ]); + + return dedupeResources([...videos, ...web]) + .filter((item) => isLikelyFree(item.url)) + .filter((item) => !item.publishedAt || new Date(item.publishedAt) >= input.since) + .slice(0, limit); +} diff --git a/src/lib/scout/discovery/exa.ts b/src/lib/scout/discovery/exa.ts new file mode 100644 index 0000000..9c1cfb7 --- /dev/null +++ b/src/lib/scout/discovery/exa.ts @@ -0,0 +1,124 @@ +import { isLikelyFree, type DiscoveredResource } from "./index"; + +interface ExaResult { + title?: string; + url?: string; + publishedDate?: string | null; + author?: string | null; + text?: string | null; + score?: number; +} + +interface ExaResponse { + results?: ExaResult[]; + error?: string; +} + +export function getExaApiKey() { + return process.env.EXA_API_KEY?.trim() || ""; +} + +function looksLikeBook(url: string) { + return /(?:^|\/)(books?|ebooks?)\//i.test(url) || /\.epub$/i.test(url); +} + +export async function searchExa(input: { + query: string; + limit: number; + includeDomains?: string[]; +}): Promise { + const key = getExaApiKey(); + if (!key) return []; + + const response = await fetch("https://api.exa.ai/search", { + method: "POST", + headers: { + "content-type": "application/json", + "x-api-key": key, + }, + body: JSON.stringify({ + query: input.query, + numResults: Math.min(20, Math.max(1, input.limit)), + type: "auto", + contents: { text: { maxCharacters: 4000 } }, + ...(input.includeDomains?.length ? { includeDomains: input.includeDomains } : {}), + }), + signal: AbortSignal.timeout(25_000), + }); + + if (!response.ok) { + const body = await response.text().catch(() => ""); + throw new Error(`Exa ${response.status}: ${body.slice(0, 200)}`); + } + + const payload = (await response.json()) as ExaResponse; + if (payload.error) throw new Error(`Exa: ${payload.error}`); + + return (payload.results ?? []) + .filter((result): result is ExaResult & { url: string } => Boolean(result.url)) + .filter((result) => isLikelyFree(result.url) && !looksLikeBook(result.url)) + .filter((result) => !/youtube\.com|youtu\.be/i.test(result.url)) + .map((result) => ({ + kind: /\.pdf(?:$|\?)/i.test(result.url) ? ("doc" as const) : ("article" as const), + source: "exa", + url: result.url, + title: result.title?.trim() || result.url, + creator: result.author ?? null, + publishedAt: result.publishedDate ?? null, + description: result.text ?? null, + })); +} + +/** + * The web half of goal discovery. Exa's neural search finds free tutorials + * and reference docs; the query asks for free material explicitly so paid + * course aggregators do not dominate. + */ +export async function discoverWebForGoal(input: { + goal: string; + limit: number; + sites: string[]; +}): Promise { + const queries = [ + `${input.goal} free tutorial guide`, + `${input.goal} documentation reference`, + ]; + + const results: DiscoveredResource[] = []; + for (const query of queries) { + try { + results.push(...(await searchExa({ query, limit: Math.ceil(input.limit / queries.length) }))); + } catch (error) { + console.warn("[scout] Exa query failed:", error instanceof Error ? error.message : error); + } + } + + for (const site of input.sites.slice(0, 4)) { + try { + results.push( + ...(await searchExa({ + query: `${input.goal} site:${site}`, + limit: 5, + includeDomains: [site], + })), + ); + } catch { + // A single site failure must not fail discovery. + } + } + + return results; +} + +/** Nightly check: only look at the sites the user explicitly added. */ +export async function discoverSitesRecent(input: { goal: string; sites: string[]; limit: number }) { + const results: DiscoveredResource[] = []; + for (const site of input.sites.slice(0, 6)) { + try { + results.push(...(await searchExa({ query: input.goal || "new guides", limit: 5, includeDomains: [site] }))); + } catch { + // ignore + } + } + return results.slice(0, input.limit); +} diff --git a/src/lib/scout/discovery/index.ts b/src/lib/scout/discovery/index.ts new file mode 100644 index 0000000..d29b2ca --- /dev/null +++ b/src/lib/scout/discovery/index.ts @@ -0,0 +1,81 @@ +/** Shared shape every discovery provider returns. */ +export interface DiscoveredResource { + kind: "video" | "article" | "doc" | "book"; + source: string; + externalId?: string | null; + url: string; + title: string; + creator?: string | null; + durationSeconds?: number | null; + publishedAt?: string | null; + thumbnailUrl?: string | null; + description?: string | null; +} + +export interface DiscoveryProvider { + name: string; + discover(input: { goal: string; limit: number }): Promise; +} + +const PAID_HOSTS = [ + "udemy.com", + "coursera.org", + "skillshare.com", + "pluralsight.com", + "linkedin.com/learning", + "masterclass.com", + "frontendmasters.com", + "egghead.io", + "oreilly.com", + "packtpub.com", + "manning.com", +]; + +/** Keeps discovery on free material; books are added by hand and never fetched. */ +export function isLikelyFree(url: string): boolean { + try { + const parsed = new URL(url); + const host = parsed.hostname.replace(/^www\./, ""); + const full = `${host}${parsed.pathname}`; + + if (PAID_HOSTS.some((paid) => full.startsWith(paid) || full.includes(`.${paid}`))) { + // Udemy has a genuinely free filter; anything else on a paid host is out. + return host === "udemy.com" && parsed.searchParams.get("price") === "price-free"; + } + + return true; + } catch { + return false; + } +} + +export function dedupeResources(items: DiscoveredResource[]): DiscoveredResource[] { + const seen = new Set(); + const result: DiscoveredResource[] = []; + for (const item of items) { + const key = item.url.split("#")[0].replace(/\/$/, "").toLowerCase(); + if (seen.has(key)) continue; + seen.add(key); + result.push(item); + } + return result; +} + +/** Cheap lexical score used only to order candidates before the LLM ranks them. */ +export function goalScore(goal: string, text: string): number { + const tokens = new Set( + goal + .toLowerCase() + .replace(/[^a-z0-9\s+#.-]/g, " ") + .split(/\s+/) + .filter((token) => token.length > 2), + ); + if (tokens.size === 0) return 0; + + const haystack = text.toLowerCase(); + let score = 0; + for (const token of tokens) { + if (haystack.includes(token)) score += 1; + } + return score; +} diff --git a/src/lib/scout/discovery/youtube.ts b/src/lib/scout/discovery/youtube.ts new file mode 100644 index 0000000..5645081 --- /dev/null +++ b/src/lib/scout/discovery/youtube.ts @@ -0,0 +1,388 @@ +import { getScoutDb, nowIso } from "../db"; +import { parseIsoDuration } from "../format"; +import { dedupeResources, goalScore, type DiscoveredResource } from "./index"; + +const API_ROOT = "https://www.googleapis.com/youtube/v3"; + +interface YouTubeChannelResponse { + items?: Array<{ + id: string; + snippet?: { title?: string }; + contentDetails?: { relatedPlaylists?: { uploads?: string } }; + }>; +} + +interface PlaylistItemsResponse { + items?: Array<{ + contentDetails?: { videoId?: string }; + snippet?: { + title?: string; + description?: string; + publishedAt?: string; + channelTitle?: string; + thumbnails?: { medium?: { url?: string } }; + }; + }>; + nextPageToken?: string; +} + +interface VideosResponse { + items?: Array<{ + id: string; + snippet?: { + title?: string; + description?: string; + publishedAt?: string; + channelTitle?: string; + thumbnails?: { medium?: { url?: string } }; + }; + contentDetails?: { duration?: string }; + }>; +} + +export function getYouTubeApiKey() { + return process.env.YOUTUBE_API_KEY?.trim() || ""; +} + +async function youtubeFetch(path: string, params: Record): Promise { + const key = getYouTubeApiKey(); + if (!key) throw new Error("YOUTUBE_API_KEY is not configured"); + + const url = new URL(`${API_ROOT}/${path}`); + for (const [name, value] of Object.entries(params)) url.searchParams.set(name, value); + url.searchParams.set("key", key); + + const response = await fetch(url, { signal: AbortSignal.timeout(20_000) }); + if (!response.ok) { + const body = await response.text().catch(() => ""); + if (response.status === 403) { + throw new Error(`YouTube API quota or permission error: ${body.slice(0, 200)}`); + } + throw new Error(`YouTube API ${response.status}: ${body.slice(0, 200)}`); + } + + return (await response.json()) as T; +} + +interface ChannelInfo { + channelId: string; + uploadsPlaylistId: string; + title: string; +} + +/** Resolves an @handle, a channel URL or a raw UC id into its uploads playlist. */ +export async function resolveChannel(value: string): Promise { + const clean = value.trim(); + if (!clean) return null; + + const byUrl = clean.match(/youtube\.com\/channel\/(UC[\w-]+)/i)?.[1]; + const rawId = byUrl ?? (clean.startsWith("UC") ? clean : null); + + const params: Record = { part: "contentDetails,snippet", maxResults: "1" }; + if (rawId) params.id = rawId; + else params.forHandle = clean.startsWith("@") ? clean : `@${clean}`; + + const payload = await youtubeFetch("channels", params); + const item = payload.items?.[0]; + const uploadsPlaylistId = item?.contentDetails?.relatedPlaylists?.uploads; + if (!item || !uploadsPlaylistId) return null; + + getScoutDb() + .prepare( + `INSERT INTO channel_cache (channel_id, uploads_playlist_id, checked_at) VALUES (?, ?, ?) + ON CONFLICT(channel_id) DO UPDATE SET uploads_playlist_id = excluded.uploads_playlist_id, checked_at = excluded.checked_at`, + ) + .run(item.id, uploadsPlaylistId, nowIso()); + + return { channelId: item.id, uploadsPlaylistId, title: item.snippet?.title ?? clean }; +} + +async function cachedChannel(value: string): Promise { + const db = getScoutDb(); + const directId = value.match(/^UC[\w-]+$/)?.[0]; + if (directId) { + const cached = db + .prepare(`SELECT channel_id, uploads_playlist_id, checked_at FROM channel_cache WHERE channel_id = ?`) + .get(directId) as { channel_id: string; uploads_playlist_id: string; checked_at: string } | undefined; + + if (cached && Date.now() - new Date(cached.checked_at).getTime() < 7 * 24 * 3600_000) { + return { channelId: cached.channel_id, uploadsPlaylistId: cached.uploads_playlist_id, title: directId }; + } + } + + return resolveChannel(value); +} + +async function listUploads( + playlistId: string, + maxResults = 25, +): Promise> { + const payload = await youtubeFetch("playlistItems", { + part: "snippet,contentDetails", + playlistId, + maxResults: String(Math.min(50, maxResults)), + }); + return payload.items ?? []; +} + +async function fetchVideos(ids: string[]): Promise> { + if (ids.length === 0) return []; + const payload = await youtubeFetch("videos", { + part: "snippet,contentDetails", + id: ids.slice(0, 50).join(","), + maxResults: "50", + }); + return payload.items ?? []; +} + +function toVideo(item: NonNullable[number]): DiscoveredResource { + const duration = parseIsoDuration(item.contentDetails?.duration); + return { + kind: "video", + source: "youtube", + externalId: item.id, + url: `https://www.youtube.com/watch?v=${item.id}`, + title: item.snippet?.title ?? "Untitled video", + creator: item.snippet?.channelTitle ?? null, + durationSeconds: duration, + publishedAt: item.snippet?.publishedAt ?? null, + thumbnailUrl: item.snippet?.thumbnails?.medium?.url ?? null, + description: item.snippet?.description ?? null, + }; +} + +const MIN_VIDEO_SECONDS = 3 * 60; +const MAX_VIDEO_SECONDS = 3 * 3600; + +/** + * Goal discovery through the Data API: channels.list -> playlistItems.list -> + * videos.list. search.list is never called because it costs 100 quota units + * per request and returns less structured data. + */ +export async function discoverYouTubeForGoal(input: { + goal: string; + channels: string[]; + limit: number; +}): Promise { + // Without a Data API key the RSS feed still gives titles, descriptions and + // publish dates. That is enough to rank candidates; durations arrive when + // the transcript is fetched. + if (!getYouTubeApiKey()) return discoverYouTubeFromFeeds(input); + + const candidates: DiscoveredResource[] = []; + + for (const channel of input.channels.slice(0, 5)) { + try { + const info = await cachedChannel(channel); + if (!info) continue; + + const uploads = await listUploads(info.uploadsPlaylistId, 25); + const ids = uploads.map((item) => item.contentDetails?.videoId).filter((id): id is string => Boolean(id)); + const videos = await fetchVideos(ids); + for (const video of videos) candidates.push(toVideo(video)); + } catch (error) { + console.warn(`[scout] YouTube channel ${channel} failed:`, error instanceof Error ? error.message : error); + } + } + + const filtered = candidates.filter( + (item) => (item.durationSeconds ?? 0) >= MIN_VIDEO_SECONDS && (item.durationSeconds ?? 0) <= MAX_VIDEO_SECONDS, + ); + + return dedupeResources(filtered) + .map((item) => ({ item, score: goalScore(input.goal, `${item.title} ${item.description ?? ""}`) })) + .sort((left, right) => right.score - left.score || (right.item.publishedAt ?? "").localeCompare(left.item.publishedAt ?? "")) + .slice(0, input.limit) + .map((entry) => entry.item); +} + +const handleCache = new Map(); + +/** + * Public channel pages expose their id in the HTML. This keeps goal discovery + * working when no YOUTUBE_API_KEY is configured; it is one request per handle + * and cached for the life of the process. + */ +export async function resolveChannelIdWithoutKey(value: string): Promise { + const clean = value.trim(); + if (!clean) return null; + + const direct = clean.match(/(?:channel\/)?(UC[\w-]{20,})/)?.[1]; + if (direct) return direct; + + const handle = + clean.match(/youtube\.com\/@([\w.-]+)/i)?.[1] ?? + clean.match(/youtube\.com\/c\/([\w.-]+)/i)?.[1] ?? + clean.replace(/^@/, "").match(/^[\w.-]+$/)?.[0]; + + if (!handle) return null; + if (handleCache.has(handle)) return handleCache.get(handle) ?? null; + + try { + const response = await fetch(`https://www.youtube.com/@${handle}`, { + headers: { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36", + "accept-language": "en-US,en;q=0.8", + }, + signal: AbortSignal.timeout(20_000), + }); + + if (!response.ok) { + handleCache.set(handle, null); + return null; + } + + const html = await response.text(); + const channelId = + html.match(/"channelId":"(UC[\w-]{20,})"/)?.[1] ?? + html.match(/channel\/(UC[\w-]{20,})/)?.[1] ?? + null; + + handleCache.set(handle, channelId); + return channelId; + } catch { + handleCache.set(handle, null); + return null; + } +} + +/** RSS-based goal discovery. No API key, no quota, no search.list. */ +export async function discoverYouTubeFromFeeds(input: { + goal: string; + channels: string[]; + limit: number; +}): Promise { + const candidates: DiscoveredResource[] = []; + + for (const channel of input.channels.slice(0, 6)) { + try { + const channelId = await resolveChannelIdWithoutKey(channel); + if (!channelId) continue; + + const feed = await fetchChannelFeed(channelId); + candidates.push(...feed); + } catch (error) { + console.warn(`[scout] YouTube feed ${channel} failed:`, error instanceof Error ? error.message : error); + } + } + + return dedupeResources(candidates) + .map((item) => ({ item, score: goalScore(input.goal, `${item.title} ${item.description ?? ""}`) })) + .sort((left, right) => right.score - left.score || (right.item.publishedAt ?? "").localeCompare(left.item.publishedAt ?? "")) + .slice(0, input.limit) + .map((entry) => entry.item); +} + +/** Watch pages carry lengthSeconds; used to fill the gap left by RSS. */ +export async function fetchVideoDuration(videoId: string): Promise { + try { + const response = await fetch(`https://www.youtube.com/watch?v=${encodeURIComponent(videoId)}`, { + headers: { "user-agent": "Mozilla/5.0 (compatible; SylloScout/1.0)" }, + signal: AbortSignal.timeout(15_000), + }); + if (!response.ok) return null; + const html = await response.text(); + const match = html.match(/"lengthSeconds":"(\d+)"/) ?? html.match(/"approxDurationMs":"(\d+)"/); + if (!match) return null; + const value = Number(match[1]); + if (!Number.isFinite(value) || value <= 0) return null; + return match[0].includes("approxDurationMs") ? Math.round(value / 1000) : value; + } catch { + return null; + } +} + +function decodeXml(value: string) { + return value + .replace(//g, "$1") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'|'/g, "'") + .replace(/&/g, "&") + .trim(); +} + +function tag(entry: string, name: string): string | null { + const match = entry.match(new RegExp(`<${name}[^>]*>([\\s\\S]*?)`, "i")); + return match ? decodeXml(match[1]) : null; +} + +/** Parses a YouTube channel RSS feed without a dependency. */ +export function parseYouTubeFeed(xml: string): DiscoveredResource[] { + const items: DiscoveredResource[] = []; + + for (const entry of xml.split(//i).slice(1)) { + const videoId = tag(entry, "yt:videoId"); + if (!videoId) continue; + + const description = tag(entry, "media:description"); + const thumbnail = entry.match(/]*url="([^"]+)"/i)?.[1] ?? null; + + items.push({ + kind: "video", + source: "youtube-rss", + externalId: videoId, + url: `https://www.youtube.com/watch?v=${videoId}`, + title: tag(entry, "title") ?? "Untitled video", + creator: tag(entry, "name") ?? null, + publishedAt: tag(entry, "published") ?? null, + thumbnailUrl: thumbnail, + description, + }); + } + + return items; +} + +export async function fetchChannelFeed(channelId: string): Promise { + const response = await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${encodeURIComponent(channelId)}`, { + signal: AbortSignal.timeout(15_000), + }); + if (!response.ok) return []; + return parseYouTubeFeed(await response.text()); +} + +/** + * RSS path for the nightly check. It needs no API key, so auto-check keeps + * working for a user who never configured YouTube credentials. + */ +export async function discoverYouTubeRecent(input: { + channels: string[]; + since: Date; + limit: number; +}): Promise { + const items: DiscoveredResource[] = []; + + for (const channel of input.channels.slice(0, 8)) { + try { + const info = getYouTubeApiKey() ? await cachedChannel(channel) : null; + const channelId = + info?.channelId ?? channel.match(/^UC[\w-]+$/)?.[0] ?? (await resolveChannelIdWithoutKey(channel)); + if (!channelId) continue; + + const feed = await fetchChannelFeed(channelId); + items.push(...feed.filter((item) => !item.publishedAt || new Date(item.publishedAt) >= input.since)); + } catch (error) { + console.warn(`[scout] RSS feed for ${channel} failed:`, error instanceof Error ? error.message : error); + } + } + + const deduped = dedupeResources(items); + + // Enrich durations when the API key is available; RSS does not carry them. + if (getYouTubeApiKey()) { + const ids = deduped.map((item) => item.externalId).filter((id): id is string => Boolean(id)); + try { + const videos = await fetchVideos(ids); + const durations = new Map(videos.map((video) => [video.id, parseIsoDuration(video.contentDetails?.duration)])); + for (const item of deduped) { + item.durationSeconds = durations.get(item.externalId ?? "") ?? null; + } + } catch (error) { + console.warn("[scout] duration enrichment failed:", error instanceof Error ? error.message : error); + } + } + + return deduped.slice(0, input.limit); +} diff --git a/src/lib/scout/fetch/reader.ts b/src/lib/scout/fetch/reader.ts new file mode 100644 index 0000000..2a0bf40 --- /dev/null +++ b/src/lib/scout/fetch/reader.ts @@ -0,0 +1,120 @@ +import { chunkText } from "../format"; + +export interface ReaderResult { + status: "ok" | "unavailable"; + title: string | null; + text: string; + via: "jina" | "direct" | "none"; + error?: string; +} + +function tidy(markdown: string) { + return markdown + .replace(/!\[[^\]]*\]\([^)]*\)/g, "") + .replace(/\[([^\]]+)\]\((?:[^)]+)\)/g, "$1") + .replace(/^[ \t]*[-*+][ \t]+/gm, "- ") + .replace(/\n{3,}/g, "\n\n") + .replace(/[ \t]{2,}/g, " ") + .trim(); +} + +function htmlToText(html: string) { + const withoutHead = html + .replace(//gi, "") + .replace(//gi, "") + .replace(//gi, "") + .replace(//gi, "") + .replace(//gi, ""); + + const text = withoutHead + .replace(/<(h[1-6])[^>]*>([\s\S]*?)<\/\1>/gi, "\n\n## $2\n\n") + .replace(/<\/(p|div|li|tr|blockquote)>/gi, "\n") + .replace(//gi, "\n") + .replace(/<[^>]+>/g, " "); + + return text + .replace(/ /g, " ") + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'"); +} + +function extractTitle(markdown: string, html: string) { + const mdTitle = markdown.match(/^Title:\s*(.+)$/m)?.[1]?.trim(); + if (mdTitle) return mdTitle; + + const htmlTitle = html.match(/]*>([\s\S]*?)<\/title>/i)?.[1]?.trim(); + return htmlTitle ? htmlTitle.replace(/\s+/g, " ") : null; +} + +/** Jina Reader turns any article into clean markdown. Direct fetch is the fallback. */ +export async function readArticle(url: string): Promise { + const jinaKey = process.env.JINA_API_KEY?.trim(); + + try { + const response = await fetch(`https://r.jina.ai/${url}`, { + headers: { + accept: "text/plain", + ...(jinaKey ? { authorization: `Bearer ${jinaKey}` } : {}), + }, + signal: AbortSignal.timeout(60_000), + }); + + if (response.ok) { + const body = await response.text(); + const text = tidy(body); + if (text.length > 400) { + return { + status: "ok", + title: extractTitle(body, ""), + text, + via: "jina", + }; + } + } + } catch { + // fall through to a direct fetch + } + + try { + const response = await fetch(url, { + headers: { "user-agent": "SylloScout/1.0 (+https://syllo.app)" }, + signal: AbortSignal.timeout(30_000), + }); + if (!response.ok) { + return { status: "unavailable", title: null, text: "", via: "none", error: `HTTP ${response.status}` }; + } + + const contentType = response.headers.get("content-type") ?? ""; + if (!contentType.includes("text/html") && !contentType.includes("text/plain")) { + return { status: "unavailable", title: null, text: "", via: "none", error: "Not a text page" }; + } + + const body = await response.text(); + const markdown = contentType.includes("text/plain") ? body : htmlToText(body); + const text = tidy(markdown); + if (text.length < 200) { + return { status: "unavailable", title: null, text: "", via: "none", error: "Page had no readable text" }; + } + + return { status: "ok", title: extractTitle("", body), text, via: "direct" }; + } catch (error) { + return { + status: "unavailable", + title: null, + text: "", + via: "none", + error: error instanceof Error ? error.message : "Fetch failed", + }; + } +} + +/** + * Long docs get summarised chunk by chunk. Three chunks is the ceiling for a + * single resource so one 80-page guide cannot eat the whole monthly budget. + */ +export function readerChunks(text: string, maxChunks = 3) { + return chunkText(text, 8000, 500).slice(0, maxChunks); +} diff --git a/src/lib/scout/fetch/transcripts.ts b/src/lib/scout/fetch/transcripts.ts new file mode 100644 index 0000000..2a0f975 --- /dev/null +++ b/src/lib/scout/fetch/transcripts.ts @@ -0,0 +1,293 @@ +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { getScoutDb } from "../db"; + +const run = promisify(execFile); + +export interface TranscriptSegment { + text: string; + start: number; + duration: number; +} + +export interface TranscriptResult { + status: "ok" | "unavailable" | "skipped"; + segments: TranscriptSegment[]; + /** Plain text with segments joined by newlines. Used for quote validation. */ + text: string; + /** Text prefixed with [mm:ss] markers. Used for LLM prompts. */ + timestampedText: string; + source: "python" | "supadata" | "none"; + error?: string; +} + +const EMPTY: TranscriptResult = { + status: "unavailable", + segments: [], + text: "", + timestampedText: "", + source: "none", +}; + +/** + * Supports both youtube-transcript-api 0.6.x (static get_transcript) and + * 1.x (instance .fetch returning segment objects). The video id travels as + * argv, never through a shell, so nothing is interpolated. + */ +const PYTHON_SCRIPT = ` +import sys, json + +video_id = sys.argv[1] + +try: + from youtube_transcript_api import YouTubeTranscriptApi +except Exception as exc: + print(json.dumps({"ok": False, "reason": "missing_module", "error": str(exc)[:200]})) + raise SystemExit(0) + +try: + segments = None + try: + api = YouTubeTranscriptApi() + fetched = api.fetch(video_id, languages=["en", "en-US", "en-GB"]) + segments = [ + {"text": seg.text, "start": seg.start, "duration": seg.duration} + for seg in fetched + ] + except AttributeError: + segments = None + + if segments is None: + raw = YouTubeTranscriptApi.get_transcript(video_id, languages=["en", "en-US", "en-GB"]) + segments = [ + {"text": item.get("text", ""), "start": item.get("start", 0), "duration": item.get("duration", 0)} + for item in raw + ] + + print(json.dumps({"ok": True, "segments": segments})) +except Exception as exc: + print(json.dumps({"ok": False, "reason": "error", "error": str(exc)[:300]})) +`; + +interface PythonCandidate { + command: string; + available: boolean; + checkedAt: number; +} + +const globalKey = Symbol.for("syllo.scout.pythonTranscript"); + +type GlobalWithPython = typeof globalThis & { [globalKey]?: PythonCandidate[] }; + +function candidates(): PythonCandidate[] { + const store = globalThis as GlobalWithPython; + store[globalKey] ??= process.platform === "win32" + ? [ + { command: "python", available: true, checkedAt: 0 }, + { command: "py", available: true, checkedAt: 0 }, + { command: "python3", available: true, checkedAt: 0 }, + ] + : [ + { command: "python3", available: true, checkedAt: 0 }, + { command: "python", available: true, checkedAt: 0 }, + ]; + return store[globalKey]; +} + +function buildText(segments: TranscriptSegment[]) { + return segments.map((segment) => segment.text.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n"); +} + +function buildTimestamped(segments: TranscriptSegment[]) { + return segments + .map((segment) => { + const total = Math.max(0, Math.floor(segment.start)); + const minutes = Math.floor(total / 60); + const seconds = total % 60; + return `[${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}] ${segment.text.replace(/\s+/g, " ").trim()}`; + }) + .filter((line) => line.length > 9) + .join("\n"); +} + +function normaliseSegments(raw: unknown): TranscriptSegment[] { + if (!Array.isArray(raw)) return []; + const segments: TranscriptSegment[] = []; + + for (const item of raw) { + if (!item || typeof item !== "object") continue; + const record = item as Record; + const text = typeof record.text === "string" ? record.text : ""; + if (!text.trim()) continue; + + const start = Number(record.start ?? 0); + const duration = Number(record.duration ?? 0); + segments.push({ + text, + start: Number.isFinite(start) ? Math.max(0, start) : 0, + duration: Number.isFinite(duration) ? Math.max(0, duration) : 0, + }); + } + + return segments; +} + +async function fromPython(videoId: string): Promise { + for (const candidate of candidates()) { + if (!candidate.available) continue; + + try { + const { stdout } = await run(candidate.command, ["-c", PYTHON_SCRIPT, videoId], { + timeout: 60_000, + windowsHide: true, + maxBuffer: 32 * 1024 * 1024, + env: { ...process.env, PYTHONIOENCODING: "utf-8" }, + }); + + const line = stdout.trim().split(/\r?\n/).filter(Boolean).pop(); + if (!line) continue; + + const payload = JSON.parse(line) as { ok?: boolean; reason?: string; error?: string; segments?: unknown }; + if (payload.ok) { + const segments = normaliseSegments(payload.segments); + if (segments.length === 0) return { ...EMPTY, error: "Transcript was empty" }; + return { + status: "ok", + segments, + text: buildText(segments), + timestampedText: buildTimestamped(segments), + source: "python", + }; + } + + if (payload.reason === "missing_module") { + candidate.available = false; + continue; + } + + // The module works but this video has no captions. No point trying + // another interpreter. + return { ...EMPTY, error: payload.error ?? "No transcript available" }; + } catch { + candidate.available = false; + } + } + + return null; +} + +interface SupadataResponse { + content?: unknown; + error?: string; + message?: string; +} + +async function fromSupadata(videoId: string): Promise { + const key = process.env.SUPADATA_API_KEY?.trim(); + if (!key) return null; + + try { + const url = new URL("https://api.supadata.ai/v1/youtube/transcript"); + url.searchParams.set("videoId", videoId); + url.searchParams.set("lang", "en"); + + const response = await fetch(url, { + headers: { "x-api-key": key }, + signal: AbortSignal.timeout(30_000), + }); + if (!response.ok) return null; + + const payload = (await response.json()) as SupadataResponse; + if (!payload.content) return null; + + if (typeof payload.content === "string") { + const segments = payload.content + .split(/\r?\n/) + .map((text, index) => ({ text, start: index * 5, duration: 5 })) + .filter((segment) => segment.text.trim()); + return { + status: "ok", + segments, + text: buildText(segments), + timestampedText: buildTimestamped(segments), + source: "supadata", + }; + } + + if (Array.isArray(payload.content)) { + const segments = normaliseSegments( + payload.content.map((item) => { + const record = (item ?? {}) as Record; + const offset = Number(record.offset ?? record.start ?? 0); + return { + text: record.text, + // Supadata offsets are milliseconds; anything already + // in seconds stays untouched. + start: offset > 10_000 ? offset / 1000 : offset, + duration: Number(record.duration ?? 0) > 10_000 ? Number(record.duration) / 1000 : Number(record.duration ?? 0), + }; + }), + ); + + if (segments.length === 0) return null; + return { + status: "ok", + segments, + text: buildText(segments), + timestampedText: buildTimestamped(segments), + source: "supadata", + }; + } + } catch { + return null; + } + + return null; +} + +/** Local captions first, hosted fallback second, skip and flag third. */ +export async function fetchTranscript(videoId: string): Promise { + if (!videoId.trim()) return { ...EMPTY, error: "Missing video id" }; + + const local = await fromPython(videoId); + if (local?.status === "ok") return local; + + const hosted = await fromSupadata(videoId); + if (hosted?.status === "ok") return hosted; + + return { + ...EMPTY, + status: "skipped", + error: local?.error ?? "No transcript available", + }; +} + +const KV_LAST_TRANSCRIPT = "last_transcript_at"; + +export function getLastTranscriptAt(): number | null { + const row = getScoutDb().prepare(`SELECT value FROM scout_kv WHERE key = ?`).get(KV_LAST_TRANSCRIPT) as + | { value: string } + | undefined; + if (!row) return null; + const value = Number(row.value); + return Number.isFinite(value) ? value : null; +} + +export function markTranscriptFetched() { + getScoutDb() + .prepare( + `INSERT INTO scout_kv (key, value) VALUES (?, ?) + ON CONFLICT(key) DO UPDATE SET value = excluded.value`, + ) + .run(KV_LAST_TRANSCRIPT, String(Date.now())); +} + +/** + * YouTube throttles transcript scraping. One request every 2-3 minutes with + * backoff is slow but survives a whole pack without a block. + */ +export function transcriptDelayMs(intervalMs = Number(process.env.SCOUT_TRANSCRIPT_INTERVAL_MS ?? 150_000)) { + const last = getLastTranscriptAt(); + if (!last) return 0; + return Math.max(0, last + Math.max(30_000, intervalMs) - Date.now()); +} From fbad1ee1988f8844a8cd954c776d95486ef06d99 Mon Sep 17 00:00:00 2001 From: Screen <228304862+screen-gd@users.noreply.github.com> Date: Wed, 21 Oct 2026 04:19:55 +0400 Subject: [PATCH 11/20] Run the Scout pipeline and publish approved packs One queue drives discovery, fetching, extraction and workspace builds. Approval copies the picks into an idempotent roadmap, and quiz cards keep their quotes and timestamps. --- src/lib/scout/digest.ts | 34 ++ src/lib/scout/http.ts | 46 +++ src/lib/scout/library.ts | 183 ++++++++++ src/lib/scout/pack.ts | 407 ++++++++++++++++++++++ src/lib/scout/pipeline.ts | 346 +++++++++++++++++++ src/lib/scout/plan.ts | 704 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 1720 insertions(+) create mode 100644 src/lib/scout/digest.ts create mode 100644 src/lib/scout/http.ts create mode 100644 src/lib/scout/library.ts create mode 100644 src/lib/scout/pack.ts create mode 100644 src/lib/scout/pipeline.ts create mode 100644 src/lib/scout/plan.ts diff --git a/src/lib/scout/digest.ts b/src/lib/scout/digest.ts new file mode 100644 index 0000000..0393ead --- /dev/null +++ b/src/lib/scout/digest.ts @@ -0,0 +1,34 @@ +import { countResourcesSince, listLibrary } from "./library"; +import type { ScoutDigest } from "./types"; + +/** + * Weekly digest. The server knows how many resources arrived and which ones + * are worth a look. Minutes watched, tasks completed and review-due live in + * the learner's browser, so the client merges those into this shape. + */ +export function buildDigest(now = new Date()): ScoutDigest { + const weekStart = new Date(now.getTime() - 7 * 24 * 3600_000); + const since = weekStart.toISOString(); + const newResources = countResourcesSince(since); + + const highlights = listLibrary({ since, limit: 4 }).map((resource) => ({ + title: resource.title, + url: resource.url, + skill: resource.skillTags[0], + })); + + const headline = + newResources === 0 + ? "No new free resources this week yet. Scout checks again tonight." + : `${newResources} new free resource${newResources === 1 ? "" : "s"} arrived this week.`; + + return { + weekOf: since.slice(0, 10), + newResources, + minutesWatched: 0, + tasksCompleted: 0, + reviewDue: 0, + headline, + highlights, + }; +} diff --git a/src/lib/scout/http.ts b/src/lib/scout/http.ts new file mode 100644 index 0000000..ae6ae9d --- /dev/null +++ b/src/lib/scout/http.ts @@ -0,0 +1,46 @@ +import { registerPipelineHandlers } from "./pipeline"; +import { ensureScoutRunner } from "./runner"; + +let registered = false; + +/** + * Every Scout endpoint calls this first. Registering handlers is idempotent, + * and starting the queue is cheap; a cold server starts working on the first + * request instead of waiting for a boot hook that TanStack Start does not + * give us. + */ +export function scoutReady() { + if (!registered) { + registerPipelineHandlers(); + registered = true; + } + ensureScoutRunner(); +} + +export function scoutJson(data: unknown, status = 200) { + return Response.json(data, { status }); +} + +export function scoutFailure(error: unknown, fallback = "Scout could not finish that request") { + const message = error instanceof Error && error.message ? error.message : fallback; + console.error("[scout] api error:", error); + return scoutJson({ success: false, error: message }, 500); +} + +export function scoutBadRequest(message: string) { + return scoutJson({ success: false, error: message }, 400); +} + +/** Path segments of the request URL, so route files can read their own ids. */ +export function pathSegments(request: Request): string[] { + return new URL(request.url).pathname.split("/").filter(Boolean); +} + +export async function readJsonBody>(request: Request): Promise { + try { + const parsed = (await request.json()) as unknown; + return parsed && typeof parsed === "object" ? (parsed as T) : null; + } catch { + return null; + } +} diff --git a/src/lib/scout/library.ts b/src/lib/scout/library.ts new file mode 100644 index 0000000..a631260 --- /dev/null +++ b/src/lib/scout/library.ts @@ -0,0 +1,183 @@ +import { getScoutDb, nowIso } from "./db"; +import { normalizeSkill } from "./extract"; +import { findResourceByUrl, toScoutResource, upsertDiscoveredResource, type ResourceRow } from "./pack"; +import type { Chapter, ResourceKind, ScoutResource } from "./types"; + +function parseSkills(values: string[] | string | undefined): string[] { + const raw = Array.isArray(values) ? values : typeof values === "string" ? values.split(",") : []; + const cleaned: string[] = []; + for (const value of raw) { + const skill = normalizeSkill(value); + if (skill && !cleaned.includes(skill)) cleaned.push(skill); + } + return cleaned.slice(0, 6); +} + +function inferKind(url: string, requested?: ResourceKind): ResourceKind { + if (requested) return requested; + if (/youtu\.be|youtube\.com\/(?:watch|shorts)/i.test(url)) return "video"; + if (/\.pdf(?:$|\?)/i.test(url)) return "doc"; + return "article"; +} + +function slugify(value: string) { + return ( + value + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-|-$/g, "") + .slice(0, 40) || "book" + ); +} + +function youtubeId(url: string) { + return ( + url.match(/youtu\.be\/([\w-]{6,})/)?.[1] ?? + url.match(/youtube\.com\/watch\?.*v=([\w-]{6,})/)?.[1] ?? + url.match(/youtube\.com\/shorts\/([\w-]{6,})/)?.[1] ?? + null + ); +} + +/** + * Manual additions never trigger fetching. They are library entries the + * learner chose; a later pack can still process a link if needed. + */ +export function addManualResource(input: { + url?: string | null; + kind?: ResourceKind; + title?: string | null; + creator?: string | null; + note?: string | null; + skills?: string[] | string; + outline?: string[]; +}): ScoutResource { + const rawUrl = input.url?.trim() || ""; + const kind = inferKind(rawUrl, input.kind); + + if (rawUrl && !/^https?:\/\//i.test(rawUrl) && kind !== "book") { + throw new Error("Add a full http(s) URL"); + } + + const url = rawUrl || `book://${slugify(input.title ?? "book")}-${Date.now().toString(36)}`; + const existing = findResourceByUrl(url); + const externalId = kind === "video" ? youtubeId(url) : null; + + const row = upsertDiscoveredResource({ + kind, + source: kind === "book" ? "shelf" : "manual", + externalId, + url, + title: input.title?.trim() || existing?.title || (kind === "book" ? "Untitled book" : url), + creator: input.creator?.trim() || existing?.creator || null, + description: existing?.description ?? null, + durationSeconds: existing?.duration_seconds ?? null, + publishedAt: existing?.published_at ?? null, + thumbnailUrl: existing?.thumbnail_url ?? null, + }); + + const skills = parseSkills(input.skills); + const chapters: Chapter[] = + kind === "book" && input.outline?.length + ? input.outline.map((title, index) => ({ + startSeconds: index, + title: title.slice(0, 160), + url, + suggested: true, + })) + : (JSON.parse(row.chapters || "[]") as Chapter[]) ?? []; + + getScoutDb() + .prepare( + `UPDATE resources SET + summary = COALESCE(?, summary, ?), + skill_tags = ?, + chapters = ?, + transcript_status = CASE WHEN transcript_status = 'pending' THEN 'skipped' ELSE transcript_status END, + updated_at = ? + WHERE id = ?`, + ) + .run( + input.note?.trim() || null, + kind === "book" ? "Added to your reading shelf." : "Saved for later.", + JSON.stringify(skills.length > 0 ? skills : (JSON.parse(row.skill_tags || "[]") as string[])), + JSON.stringify(chapters), + nowIso(), + row.id, + ); + + return toScoutResource(getScoutDb().prepare(`SELECT * FROM resources WHERE id = ?`).get(row.id) as ResourceRow); +} + +export function listLibrary(input: { query?: string; kind?: ResourceKind | "all"; since?: string; limit?: number } = {}) { + const clauses: string[] = []; + const values: unknown[] = []; + const query = input.query?.trim() ?? ""; + + if (query) { + clauses.push(`(title LIKE ? OR summary LIKE ? OR skill_tags LIKE ? OR creator LIKE ?)`); + const like = `%${query.replace(/[%_]/g, "")}%`; + values.push(like, like, like, like); + } + + if (input.kind && input.kind !== "all") { + clauses.push(`kind = ?`); + values.push(input.kind); + } + + if (input.since) { + clauses.push(`updated_at >= ?`); + values.push(input.since); + } + + const limit = Math.min(200, Math.max(1, input.limit ?? 60)); + const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : ""; + + const rows = getScoutDb() + .prepare(`SELECT * FROM resources ${where} ORDER BY updated_at DESC LIMIT ?`) + .all(...values, limit) as ResourceRow[]; + + return rows.map((row) => toScoutResource(row)); +} + +/** Used by the MCP server and the Sources panel. */ +export function searchLibrary(query: string, limit = 8): ScoutResource[] { + const tokens = query + .toLowerCase() + .split(/\s+/) + .map((token) => token.replace(/[^a-z0-9+#.-]/g, "")) + .filter((token) => token.length > 1) + .slice(0, 6); + + if (tokens.length === 0) return listLibrary({ limit }); + if (tokens.length === 1) return listLibrary({ query: tokens[0], limit }); + + // Any token can match; the JS pass ranks by how many did. + const likeClauses = tokens.map(() => `(title LIKE ? OR summary LIKE ? OR skill_tags LIKE ?)`).join(" OR "); + const values = tokens.flatMap((token) => [`%${token}%`, `%${token}%`, `%${token}%`]); + + const rows = getScoutDb() + .prepare(`SELECT * FROM resources WHERE ${likeClauses} ORDER BY updated_at DESC LIMIT ?`) + .all(...values, Math.max(limit * 4, 24)) as ResourceRow[]; + + return rows + .map((row) => { + const haystack = `${row.title} ${row.summary ?? ""} ${row.skill_tags} ${row.creator ?? ""}`.toLowerCase(); + const score = tokens.reduce((total, token) => total + (haystack.includes(token) ? 1 : 0), 0); + return { row, score }; + }) + .sort((left, right) => right.score - left.score || right.row.updated_at.localeCompare(left.row.updated_at)) + .slice(0, limit) + .map((entry) => toScoutResource(entry.row)); +} + +export function countResourcesSince(since: string) { + const row = getScoutDb() + .prepare(`SELECT COUNT(*) AS count FROM resources WHERE created_at >= ?`) + .get(since) as { count: number }; + return row.count; +} + +export function listBooks(): ScoutResource[] { + return listLibrary({ kind: "book", limit: 50 }); +} diff --git a/src/lib/scout/pack.ts b/src/lib/scout/pack.ts new file mode 100644 index 0000000..dbe8ea1 --- /dev/null +++ b/src/lib/scout/pack.ts @@ -0,0 +1,407 @@ +import type { Roadmap, SrsItem } from "@/types"; +import { getScoutDb, newId, nowIso } from "./db"; +import type { Chapter, KeyPoint, PackStatus, ResourceKind, ScoutPack, ScoutResource, TranscriptStatus } from "./types"; + +export interface PackRow { + id: string; + goal: string; + status: PackStatus; + progress: number; + stage: string; + picks: string; + roadmap_id: string | null; + roadmap: string | null; + quizzes: string | null; + cost_cents: number; + error: string | null; + created_at: string; + updated_at: string; +} + +export interface ResourceRow { + id: string; + kind: ResourceKind; + source: string; + external_id: string | null; + url: string; + title: string; + creator: string | null; + duration_seconds: number | null; + published_at: string | null; + thumbnail_url: string | null; + description: string | null; + summary: string | null; + key_points: string; + skill_tags: string; + chapters: string; + transcript: string | null; + transcript_status: TranscriptStatus; + transcript_chars: number; + content_hash: string | null; + error: string | null; + created_at: string; + updated_at: string; +} + +function parseJson(value: string | null | undefined, fallback: T): T { + if (!value) return fallback; + try { + return JSON.parse(value) as T; + } catch { + return fallback; + } +} + +export function toScoutResource(row: ResourceRow, approved?: boolean): ScoutResource { + return { + id: row.id, + kind: row.kind, + source: row.source, + externalId: row.external_id, + url: row.url, + title: row.title, + creator: row.creator, + durationSeconds: row.duration_seconds, + publishedAt: row.published_at, + thumbnailUrl: row.thumbnail_url, + description: row.description, + summary: row.summary, + keyPoints: parseJson(row.key_points, []), + skillTags: parseJson(row.skill_tags, []), + chapters: parseJson(row.chapters, []), + transcriptStatus: row.transcript_status, + transcriptChars: row.transcript_chars, + error: row.error, + createdAt: row.created_at, + updatedAt: row.updated_at, + ...(approved === undefined ? {} : { approved }), + }; +} + +export function getPackRow(id: string): PackRow | null { + return (getScoutDb().prepare(`SELECT * FROM packs WHERE id = ?`).get(id) as PackRow | undefined) ?? null; +} + +export function getResourceRow(id: string): ResourceRow | null { + return (getScoutDb().prepare(`SELECT * FROM resources WHERE id = ?`).get(id) as ResourceRow | undefined) ?? null; +} + +export function findResourceByUrl(url: string): ResourceRow | null { + return (getScoutDb().prepare(`SELECT * FROM resources WHERE url = ?`).get(url) as ResourceRow | undefined) ?? null; +} + +export function packResourceRows(packId: string): ResourceRow[] { + return getScoutDb() + .prepare( + `SELECT r.* FROM resources r + JOIN pack_resources pr ON pr.resource_id = r.id + WHERE pr.pack_id = ? + ORDER BY pr.position, r.created_at`, + ) + .all(packId) as ResourceRow[]; +} + +function packResourceRowsWithSelection(packId: string): Array { + return getScoutDb() + .prepare( + `SELECT r.*, pr.selected FROM resources r + JOIN pack_resources pr ON pr.resource_id = r.id + WHERE pr.pack_id = ? + ORDER BY pr.position, r.created_at`, + ) + .all(packId) as Array; +} + +export function toScoutPack(row: PackRow): ScoutPack { + const picks = parseJson(row.picks, []); + const resources = packResourceRowsWithSelection(row.id).map((resource) => + toScoutResource(resource, picks.includes(resource.id)), + ); + + return { + id: row.id, + goal: row.goal, + status: row.status, + progress: row.progress, + stage: row.stage, + picks, + roadmapId: row.roadmap_id, + costCents: Math.round(row.cost_cents * 100) / 100, + error: row.error, + createdAt: row.created_at, + updatedAt: row.updated_at, + resources, + }; +} + +export function getPack(id: string): ScoutPack | null { + const row = getPackRow(id); + return row ? toScoutPack(row) : null; +} + +export function getPackRoadmap(id: string): Roadmap | null { + const row = getPackRow(id); + if (!row) return null; + return parseJson(row.roadmap, null); +} + +export function getPackQuizzes(id: string): SrsItem[] { + const row = getPackRow(id); + if (!row) return []; + return parseJson(row.quizzes, []); +} + +/** The shape the pack endpoint returns. Transcripts are never included. */ +export interface ScoutPackDetail extends ScoutPack { + roadmap: Roadmap | null; + quizzes: SrsItem[]; +} + +export function getPackDetail(id: string): ScoutPackDetail | null { + const row = getPackRow(id); + if (!row) return null; + return { + ...toScoutPack(row), + roadmap: parseJson(row.roadmap, null), + quizzes: parseJson(row.quizzes, []), + }; +} + +export function createPack(goal: string): ScoutPack { + const db = getScoutDb(); + const id = newId("pack"); + const now = nowIso(); + db.prepare( + `INSERT INTO packs (id, goal, status, progress, stage, picks, created_at, updated_at) + VALUES (?, ?, 'discovering', 0, 'Looking for free resources', '[]', ?, ?)`, + ).run(id, goal.trim(), now, now); + + return toScoutPack(getPackRow(id) as PackRow); +} + +export interface PackPatch { + status?: PackStatus; + progress?: number; + stage?: string; + error?: string | null; + picks?: string[]; + roadmapId?: string | null; + roadmap?: Roadmap | null; + quizzes?: SrsItem[] | null; +} + +export function updatePack(id: string, patch: PackPatch) { + const fields: string[] = []; + const values: unknown[] = []; + + const add = (column: string, value: unknown) => { + fields.push(`${column} = ?`); + values.push(value); + }; + + if (patch.status !== undefined) add("status", patch.status); + if (patch.progress !== undefined) add("progress", Math.max(0, Math.min(100, Math.round(patch.progress)))); + if (patch.stage !== undefined) add("stage", patch.stage); + if (patch.error !== undefined) add("error", patch.error); + if (patch.picks !== undefined) add("picks", JSON.stringify(patch.picks)); + if (patch.roadmapId !== undefined) add("roadmap_id", patch.roadmapId); + if (patch.roadmap !== undefined) add("roadmap", patch.roadmap ? JSON.stringify(patch.roadmap) : null); + if (patch.quizzes !== undefined) add("quizzes", patch.quizzes ? JSON.stringify(patch.quizzes) : null); + + if (fields.length === 0) return; + + fields.push("updated_at = ?"); + values.push(nowIso(), id); + getScoutDb().prepare(`UPDATE packs SET ${fields.join(", ")} WHERE id = ?`).run(...values); +} + +export function failPack(id: string, error: unknown) { + const message = error instanceof Error ? error.message : String(error ?? "Scout stopped unexpectedly"); + updatePack(id, { + status: "failed", + stage: "Scout hit a problem", + progress: 100, + error: message.slice(0, 2000), + }); +} + +/** + * Upserts a discovered resource by URL. Re-running a pack reuses the earlier + * extraction, so the learner is not charged twice for the same video. + */ +export function upsertDiscoveredResource(item: { + kind: ResourceKind; + source: string; + externalId?: string | null; + url: string; + title: string; + creator?: string | null; + durationSeconds?: number | null; + publishedAt?: string | null; + thumbnailUrl?: string | null; + description?: string | null; +}): ResourceRow { + const db = getScoutDb(); + const now = nowIso(); + const id = newId("res"); + + const row = db + .prepare( + `INSERT INTO resources ( + id, kind, source, external_id, url, title, creator, duration_seconds, + published_at, thumbnail_url, description, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(url) DO UPDATE SET + kind = excluded.kind, + source = excluded.source, + external_id = COALESCE(excluded.external_id, resources.external_id), + title = excluded.title, + creator = COALESCE(excluded.creator, resources.creator), + duration_seconds = COALESCE(excluded.duration_seconds, resources.duration_seconds), + published_at = COALESCE(excluded.published_at, resources.published_at), + thumbnail_url = COALESCE(excluded.thumbnail_url, resources.thumbnail_url), + description = COALESCE(excluded.description, resources.description), + updated_at = excluded.updated_at + RETURNING *`, + ) + .get( + id, + item.kind, + item.source, + item.externalId ?? null, + item.url, + item.title, + item.creator ?? null, + item.durationSeconds ?? null, + item.publishedAt ?? null, + item.thumbnailUrl ?? null, + item.description ?? null, + now, + now, + ) as ResourceRow; + + return row; +} + +export function attachResourceToPack(packId: string, resourceId: string, position: number) { + getScoutDb() + .prepare( + `INSERT INTO pack_resources (pack_id, resource_id, position, selected) + VALUES (?, ?, ?, 1) + ON CONFLICT(pack_id, resource_id) DO UPDATE SET position = excluded.position`, + ) + .run(packId, resourceId, position); +} + +export function isResourceInPack(packId: string, resourceId: string) { + const row = getScoutDb() + .prepare(`SELECT 1 AS ok FROM pack_resources WHERE pack_id = ? AND resource_id = ?`) + .get(packId, resourceId) as { ok: number } | undefined; + return Boolean(row); +} + +/** Approving picks is idempotent; only resources already in the pack survive. */ +export function approvePackPicks(packId: string, picks: string[]): string[] { + const allowed = new Set(packResourceRows(packId).map((row) => row.id)); + const accepted = [...new Set(picks.filter((id) => allowed.has(id)))]; + + const db = getScoutDb(); + db.transaction(() => { + db.prepare(`UPDATE pack_resources SET selected = 0 WHERE pack_id = ?`).run(packId); + const select = db.prepare(`UPDATE pack_resources SET selected = 1 WHERE pack_id = ? AND resource_id = ?`); + for (const id of accepted) select.run(packId, id); + updatePack(packId, { picks: accepted }); + })(); + + return accepted; +} + +export interface ExtractionWrite { + summary: string; + keyPoints: KeyPoint[]; + skillTags: string[]; + chapters: Chapter[]; + transcript?: string | null; + transcriptStatus: TranscriptStatus; + transcriptChars: number; + durationSeconds?: number | null; + error?: string | null; +} + +export function saveResourceExtraction(id: string, data: ExtractionWrite) { + getScoutDb() + .prepare( + `UPDATE resources SET + summary = ?, key_points = ?, skill_tags = ?, chapters = ?, + transcript = COALESCE(?, transcript), + transcript_status = ?, transcript_chars = ?, + duration_seconds = COALESCE(?, duration_seconds), + error = ?, updated_at = ? + WHERE id = ?`, + ) + .run( + data.summary, + JSON.stringify(data.keyPoints), + JSON.stringify(data.skillTags), + JSON.stringify(data.chapters), + data.transcript ?? null, + data.transcriptStatus, + data.transcriptChars, + data.durationSeconds ?? null, + data.error ?? null, + nowIso(), + id, + ); +} + +export function setResourceError(id: string, error: string | null) { + getScoutDb().prepare(`UPDATE resources SET error = ?, updated_at = ? WHERE id = ?`).run(error, nowIso(), id); +} + +export function recentPacks(limit = 8): ScoutPack[] { + const rows = getScoutDb() + .prepare(`SELECT * FROM packs ORDER BY created_at DESC LIMIT ?`) + .all(limit) as PackRow[]; + return rows.map(toScoutPack); +} + +export function latestPack(): ScoutPack | null { + return recentPacks(1)[0] ?? null; +} + +export function latestGoal(): string | null { + const row = getScoutDb() + .prepare(`SELECT goal FROM packs ORDER BY created_at DESC LIMIT 1`) + .get() as { goal: string } | undefined; + return row?.goal ?? null; +} + +export function countPackResources(packId: string) { + const row = getScoutDb() + .prepare(`SELECT COUNT(*) AS count FROM pack_resources WHERE pack_id = ?`) + .get(packId) as { count: number }; + return row.count; +} + +export function countUnprocessedPackResources(packId: string) { + const row = getScoutDb() + .prepare( + `SELECT COUNT(*) AS count FROM resources r + JOIN pack_resources pr ON pr.resource_id = r.id + WHERE pr.pack_id = ? AND r.summary IS NULL AND r.transcript_status = 'pending'`, + ) + .get(packId) as { count: number }; + return row.count; +} + +/** True when every resource in the pack has been processed (or failed softly). */ +export function packProcessingFinished(packId: string) { + const row = getScoutDb() + .prepare( + `SELECT COUNT(*) AS count FROM resources r + JOIN pack_resources pr ON pr.resource_id = r.id + WHERE pr.pack_id = ? AND r.summary IS NULL AND r.error IS NULL`, + ) + .get(packId) as { count: number }; + return row.count === 0; +} diff --git a/src/lib/scout/pipeline.ts b/src/lib/scout/pipeline.ts new file mode 100644 index 0000000..4da1810 --- /dev/null +++ b/src/lib/scout/pipeline.ts @@ -0,0 +1,346 @@ +import { discoverForGoal, discoverNewFromSources } from "./discovery/combine"; +import { extractResource } from "./extract"; +import { readArticle } from "./fetch/reader"; +import { fetchTranscript, markTranscriptFetched, transcriptDelayMs } from "./fetch/transcripts"; +import { JobDeferred, enqueueJob } from "./jobs"; +import { recallScoutLlm, rememberScoutLlm, type ScoutLlmConfig } from "./llm"; +import { + approvePackPicks, + attachResourceToPack, + countPackResources, + countUnprocessedPackResources, + createPack, + failPack, + getPackRow, + getResourceRow, + latestGoal, + packProcessingFinished, + packResourceRows, + saveResourceExtraction, + setResourceError, + toScoutResource, + updatePack, + upsertDiscoveredResource, + type ResourceRow, +} from "./pack"; +import { buildWorkspace } from "./plan"; +import { registerJobHandler } from "./runner"; +import { getScoutSettings } from "./settings"; +import { listEnabledSources, touchSource } from "./sources"; +import type { JobRow } from "./types"; + +const DISCOVERY_LIMIT = 14; + +function asString(value: unknown) { + return typeof value === "string" && value.trim() ? value.trim() : null; +} + +function messageFor(error: unknown) { + return error instanceof Error ? error.message : String(error ?? "Scout stopped unexpectedly"); +} + +/* ------------------------------------------------------------------ * + * discover + * ------------------------------------------------------------------ */ + +async function handleDiscover(_job: JobRow, payload: Record) { + const packId = asString(payload.packId); + if (!packId) throw new Error("discover job is missing a packId"); + + const pack = getPackRow(packId); + if (!pack) throw new Error(`Pack ${packId} no longer exists`); + if (pack.status !== "discovering") return { skipped: true }; + + updatePack(packId, { progress: 6, stage: "Looking for free resources" }); + + const discovered = await discoverForGoal({ goal: pack.goal, limit: DISCOVERY_LIMIT }); + if (discovered.length === 0) { + failPack( + packId, + new Error("No free resources matched that goal. Add a channel or site in Sources and try again."), + ); + return { found: 0 }; + } + + const rows = discovered.map((item, index) => { + const row = upsertDiscoveredResource(item); + attachResourceToPack(packId, row.id, index); + return row; + }); + + updatePack(packId, { + status: "processing", + progress: 10, + stage: `Found ${rows.length} candidates — reading them now`, + }); + + // A small stagger avoids fifteen identical requests in the same tick; the + // transcript throttle in the handler does the real pacing. + rows.forEach((row, index) => { + enqueueJob( + "process_resource", + { packId, resourceId: row.id }, + { maxAttempts: 4, runAfter: new Date(Date.now() + index * 1_000) }, + ); + }); + + return { found: rows.length }; +} + +/* ------------------------------------------------------------------ * + * process_resource + * ------------------------------------------------------------------ */ + +interface TextResult { + text: string; + segments?: Array<{ text: string; start: number }>; + transcript?: string | null; + transcriptStatus: "ok" | "unavailable" | "skipped"; + transcriptChars: number; + error?: string; +} + +async function textFor(row: ResourceRow): Promise { + if (row.kind === "video") { + if (!row.external_id) { + return { + text: row.description ?? "", + transcriptStatus: "skipped", + transcriptChars: 0, + error: "No video id for this resource", + }; + } + + // Transcript scraping is the slow, rate-limited step. Deferring keeps + // the queue honest instead of sleeping inside a handler. + const delay = transcriptDelayMs(); + if (delay > 0) throw new JobDeferred(delay, "Waiting for the next transcript slot"); + + const transcript = await fetchTranscript(row.external_id); + markTranscriptFetched(); + + if (transcript.status === "ok") { + return { + text: transcript.text, + segments: transcript.segments.map((segment) => ({ text: segment.text, start: segment.start })), + transcript: transcript.text, + transcriptStatus: "ok", + transcriptChars: transcript.text.length, + }; + } + + return { + text: row.description ?? "", + transcriptStatus: transcript.status, + transcriptChars: 0, + error: transcript.error, + }; + } + + const article = await readArticle(row.url); + if (article.status === "ok") { + return { text: article.text, transcriptStatus: "ok", transcriptChars: article.text.length }; + } + + return { + text: row.description ?? "", + transcriptStatus: "skipped", + transcriptChars: 0, + error: article.error ?? "Could not read this page", + }; +} + +async function handleProcessResource(job: JobRow, payload: Record) { + const packId = asString(payload.packId); + const resourceId = asString(payload.resourceId); + if (!packId || !resourceId) throw new Error("process_resource job is missing ids"); + + const row = getResourceRow(resourceId); + if (!row) return { skipped: true }; + if (row.summary && row.transcript_status !== "pending") { + settlePackIfProcessed(packId); + return { cached: true }; + } + + try { + const source = await textFor(row); + + const extraction = await extractResource({ + packId, + resource: { + id: row.id, + kind: row.kind, + title: row.title, + creator: row.creator, + description: row.description, + durationSeconds: row.duration_seconds, + externalId: row.external_id, + transcriptStatus: source.transcriptStatus, + }, + text: source.text, + segments: source.segments, + config: recallScoutLlm(packId), + }); + + const existingChapters = JSON.parse(row.chapters || "[]") as typeof extraction.chapters; + const chapters = extraction.chapters.length > 0 ? extraction.chapters : existingChapters; + const lastEnd = chapters.reduce((max, chapter) => Math.max(max, chapter.endSeconds ?? 0), 0); + + saveResourceExtraction(row.id, { + summary: extraction.summary || "No summary could be produced for this resource yet.", + keyPoints: extraction.keyPoints, + skillTags: extraction.skills, + chapters, + transcript: source.transcript ?? null, + transcriptStatus: source.transcriptStatus, + transcriptChars: source.transcriptChars, + durationSeconds: row.duration_seconds ?? (lastEnd > 0 ? lastEnd : null), + error: source.error ?? extraction.error ?? null, + }); + } catch (error) { + // A deferral is not a failure: the runner will requeue it untouched. + if (error instanceof JobDeferred) throw error; + + if (job.attempts >= job.max_attempts) { + setResourceError(resourceId, messageFor(error).slice(0, 500)); + } + settlePackIfProcessed(packId); + throw error; + } + + settlePackIfProcessed(packId); + return { resourceId }; +} + +/** + * Moves a pack from "processing" to "review" once every resource has either + * been extracted or finally failed. Picks default to everything, so the + * approval screen opens with all cards selected. + */ +function settlePackIfProcessed(packId: string) { + const pack = getPackRow(packId); + if (!pack || pack.status !== "processing") return; + + const total = countPackResources(packId); + if (total === 0) return; + + if (!packProcessingFinished(packId)) { + const pending = countUnprocessedPackResources(packId); + const done = Math.max(0, total - pending); + updatePack(packId, { + progress: Math.min(42, 10 + Math.round((done / total) * 30)), + stage: `Reading resources (${done} of ${total})`, + }); + return; + } + + approvePackPicks( + packId, + packResourceRows(packId).map((row) => row.id), + ); + updatePack(packId, { + status: "review", + progress: 45, + stage: "Ready for your review", + }); +} + +/* ------------------------------------------------------------------ * + * build_workspace + * ------------------------------------------------------------------ */ + +async function handleBuildWorkspace(_job: JobRow, payload: Record) { + const packId = asString(payload.packId); + if (!packId) throw new Error("build_workspace job is missing a packId"); + + const pack = getPackRow(packId); + if (!pack) throw new Error(`Pack ${packId} no longer exists`); + if (pack.status === "ready") return { roadmapId: pack.roadmap_id }; + + const picks = new Set(JSON.parse(pack.picks || "[]") as string[]); + const resources = packResourceRows(packId) + .filter((row) => picks.has(row.id)) + .map((row) => toScoutResource(row, true)); + + if (resources.length === 0) { + failPack(packId, new Error("Approve at least one resource before building the roadmap.")); + return { built: false }; + } + + updatePack(packId, { status: "building", progress: 55, stage: "Writing your roadmap" }); + + const workspace = await buildWorkspace({ + packId, + goal: pack.goal, + resources, + config: recallScoutLlm(packId), + }); + + updatePack(packId, { + status: "ready", + progress: 100, + stage: "Your workspace is ready", + roadmapId: workspace.roadmap.id, + roadmap: workspace.roadmap, + quizzes: workspace.quizzes, + error: null, + }); + + return { roadmapId: workspace.roadmap.id, quizzes: workspace.quizzes.length, degraded: workspace.degraded }; +} + +/* ------------------------------------------------------------------ * + * auto_check + * ------------------------------------------------------------------ */ + +/** + * Nightly check. Discovery only: it has no per-pack key, so it never calls the + * model and never touches an approved pack. + */ +async function handleAutoCheck() { + if (getScoutSettings().paused) return { paused: true }; + + const sources = listEnabledSources(); + if (sources.length === 0) return { found: 0 }; + + const since = new Date(Date.now() - 7 * 24 * 3600_000); + const goal = latestGoal() ?? "new free tutorials"; + const discovered = await discoverNewFromSources({ goal, since, limit: 12 }); + + let added = 0; + for (const item of discovered) { + const row = upsertDiscoveredResource(item); + if (!row.summary) added += 1; + } + + for (const source of sources) touchSource(source.id); + + return { + found: discovered.length, + added, + highlights: discovered.slice(0, 4).map((item) => ({ title: item.title, url: item.url })), + }; +} + +/* ------------------------------------------------------------------ */ + +export function registerPipelineHandlers() { + registerJobHandler("discover", handleDiscover); + registerJobHandler("process_resource", handleProcessResource); + registerJobHandler("build_workspace", handleBuildWorkspace); + registerJobHandler("auto_check", handleAutoCheck); +} + +/** Creates a pack and queues discovery without waiting for the next tick. */ +export function startPack(goal: string, llm?: ScoutLlmConfig | null) { + const created = createPack(goal); + if (llm) rememberScoutLlm(created.id, llm); + enqueueJob("discover", { packId: created.id }); + return created; +} + +/** Queues roadmap generation for an approved pack. */ +export function queueWorkspaceBuild(packId: string) { + updatePack(packId, { status: "building", progress: 50, stage: "Preparing your roadmap" }); + return enqueueJob("build_workspace", { packId }); +} diff --git a/src/lib/scout/plan.ts b/src/lib/scout/plan.ts new file mode 100644 index 0000000..a1ceba2 --- /dev/null +++ b/src/lib/scout/plan.ts @@ -0,0 +1,704 @@ +import { z } from "zod"; +import type { + ModuleSection, + Note, + NoteSection, + Resource, + ResourceSection, + Roadmap, + SrsItem, + Task, + Video, +} from "@/types"; +import { clip, deepLink, formatTimestamp, quoteAppearsIn } from "./format"; +import { scoutGenerateObject } from "./llm"; +import { SCOUT_PROMPTS, SCOUT_PROMPT_VERSION } from "./prompts"; +import type { ScoutResource } from "./types"; + +const planSchema = z.object({ + title: z.string(), + summary: z.string(), + objectives: z.array(z.string()).default([]), + difficulty: z.enum(["beginner", "intermediate", "advanced"]).default("intermediate"), + modules: z + .array( + z.object({ + title: z.string(), + description: z.string().default(""), + skill: z.string().default(""), + tasks: z + .array( + z.object({ + title: z.string(), + resourceId: z.string(), + startSeconds: z.number().nullable().optional(), + endSeconds: z.number().nullable().optional(), + estimatedMinutes: z.number().nullable().optional(), + }), + ) + .default([]), + }), + ) + .default([]), +}); + +export type ScoutPlan = z.infer; + +type PlanTask = ScoutPlan["modules"][number]["tasks"][number]; +type PlanModuleInput = ScoutPlan["modules"][number]; + +const quizSchema = z.object({ + cards: z + .array( + z.object({ + moduleIndex: z.number(), + resourceId: z.string(), + prompt: z.string(), + answer: z.string(), + atSeconds: z.number().nullable().optional(), + quote: z.string(), + }), + ) + .default([]), +}); + +type ScoutQuizPlan = z.infer; + +interface PlanModule { + title: string; + description: string; + skill: string; + resourceIds: string[]; + tasks: Array<{ + title: string; + resourceId: string; + startSeconds: number | null; + endSeconds: number | null; + estimatedMinutes: number | null; + }>; +} + +export interface BuiltWorkspace { + roadmap: Roadmap; + quizzes: SrsItem[]; + degraded: boolean; +} + +export interface BuildWorkspaceInput { + packId: string; + goal: string; + resources: ScoutResource[]; + config?: Parameters[0]["config"]; +} + +function unique(values: T[]): T[] { + return [...new Set(values)]; +} + +function resourceMap(resources: ScoutResource[]) { + return new Map(resources.map((resource) => [resource.id, resource])); +} + +function taskMinutes(resource: ScoutResource, startSeconds: number | null, endSeconds: number | null) { + if (resource.kind === "video" && startSeconds !== null && endSeconds !== null && endSeconds > startSeconds) { + return Math.max(2, Math.round((endSeconds - startSeconds) / 60)); + } + if (resource.kind === "book") return 45; + if (resource.kind === "article" || resource.kind === "doc") return 20; + return 15; +} + +function chapterFor(resource: ScoutResource, startSeconds: number | null) { + if (startSeconds === null) return null; + return resource.chapters.find((chapter) => chapter.startSeconds === startSeconds) ?? null; +} + +function attachment(resource: ScoutResource, startSeconds: number | null): Resource | null { + if (resource.kind === "book") return null; + + const url = + resource.kind === "video" && resource.externalId && startSeconds + ? deepLink(resource.externalId, startSeconds) + : resource.url; + + const type: Resource["type"] = + resource.kind === "video" ? "video" : resource.kind === "doc" ? "doc" : "link"; + + return { + id: `link_${resource.id}_${startSeconds ?? 0}`, + title: resource.title, + url, + type, + description: resource.summary ?? resource.description ?? "", + }; +} + +function taskNotes(resource: ScoutResource, startSeconds: number | null, endSeconds: number | null) { + const lines: string[] = []; + if (resource.summary) lines.push(resource.summary); + + const relevant = resource.keyPoints + .filter((point) => { + if (point.atSeconds == null) return true; + if (startSeconds === null) return true; + return point.atSeconds >= startSeconds && (endSeconds === null || point.atSeconds <= endSeconds); + }) + .slice(0, 4); + + for (const point of relevant) { + const link = + resource.kind === "video" && resource.externalId && point.atSeconds != null + ? `[${formatTimestamp(point.atSeconds)}](${deepLink(resource.externalId, point.atSeconds)})` + : point.atSeconds != null + ? formatTimestamp(point.atSeconds) + : ""; + lines.push(`- ${link ? `${link} — ` : ""}${point.text}`); + if (point.quote) lines.push(` > ${point.quote}`); + } + + return lines.join("\n").slice(0, 2000); +} + +function videoFor(resource: ScoutResource): Video | null { + if (resource.kind !== "video" || !resource.externalId) return null; + return { + id: `video_${resource.id}`, + title: resource.title, + url: resource.url, + videoId: resource.externalId, + platform: "youtube", + description: resource.summary ?? "", + duration: resource.durationSeconds ? `${Math.round(resource.durationSeconds / 60)} min` : undefined, + timestamps: resource.chapters.slice(0, 20).map((chapter) => ({ + time: formatTimestamp(chapter.startSeconds), + label: chapter.title, + })), + }; +} + +function taskTitle( + resource: ScoutResource, + verb: "Watch" | "Read", + startSeconds: number | null, + endSeconds: number | null, + proposed?: string, +) { + const chapter = chapterFor(resource, startSeconds); + + if (verb === "Watch" && startSeconds !== null && endSeconds !== null) { + const label = chapter?.title ?? proposed ?? resource.title; + return `Watch ${label} (${formatTimestamp(startSeconds)}–${formatTimestamp(endSeconds)})`; + } + + if (verb === "Watch" && startSeconds !== null) { + const label = chapter?.title ?? proposed ?? resource.title; + return `Watch ${label} from ${formatTimestamp(startSeconds)}`; + } + + return proposed || `${verb} ${resource.title}`; +} + +function buildTask(input: { + packId: string; + moduleIndex: number; + taskIndex: number; + resource: ScoutResource; + verb: "Watch" | "Read"; + startSeconds: number | null; + endSeconds: number | null; + title?: string; + estimatedMinutes?: number | null; +}) { + const { resource, startSeconds, endSeconds } = input; + const link = attachment(resource, startSeconds); + const minutes = input.estimatedMinutes ?? taskMinutes(resource, startSeconds, endSeconds); + + const task: Task = { + id: `task_${input.packId}_${input.moduleIndex}_${input.taskIndex}`, + title: taskTitle(resource, input.verb, startSeconds, endSeconds, input.title), + completed: false, + notes: taskNotes(resource, startSeconds, endSeconds), + subtasks: [], + attachments: link ? [link] : [], + estimatedTime: `${Math.max(2, Math.min(300, Math.round(minutes)))} min`, + priority: "core", + }; + + return task; +} + +function fallbackPlan(goal: string, resources: ScoutResource[]): ScoutPlan { + const groups = new Map(); + for (const resource of resources) { + const label = + resource.skillTags[0] ?? + (resource.kind === "book" ? "Reading" : resource.kind === "video" ? "Watch" : "Read"); + const key = label.charAt(0).toUpperCase() + label.slice(1); + groups.set(key, [...(groups.get(key) ?? []), resource]); + } + + const modules: PlanModuleInput[] = [...groups.entries()].slice(0, 5).map(([skill, items]): PlanModuleInput => ({ + title: skill, + description: `Work through ${items.length} free resource${items.length === 1 ? "" : "s"} on ${skill.toLowerCase()}.`, + skill, + tasks: items.flatMap((resource): PlanTask[] => { + if (resource.kind === "book") { + const chapters = resource.chapters.length > 0 ? resource.chapters : [{ title: resource.title, startSeconds: 0, url: resource.url }]; + return chapters.slice(0, 10).map((chapter) => ({ + title: `Read ${chapter.title}`, + resourceId: resource.id, + startSeconds: null, + endSeconds: null, + estimatedMinutes: 45, + })); + } + if (resource.kind === "video") { + if (resource.chapters.length > 0) { + return resource.chapters.slice(0, 10).map((chapter) => ({ + title: `Watch ${chapter.title}`, + resourceId: resource.id, + startSeconds: chapter.startSeconds, + endSeconds: chapter.endSeconds ?? null, + estimatedMinutes: taskMinutes(resource, chapter.startSeconds, chapter.endSeconds ?? null), + })); + } + return [ + { + title: `Watch ${resource.title}`, + resourceId: resource.id, + startSeconds: null, + endSeconds: null, + estimatedMinutes: taskMinutes(resource, null, null), + }, + ]; + } + return [ + { + title: `Read ${resource.title}`, + resourceId: resource.id, + startSeconds: null, + endSeconds: null, + estimatedMinutes: taskMinutes(resource, null, null), + }, + ]; + }), + })); + + const skills = unique(resources.flatMap((resource) => resource.skillTags)).slice(0, 4); + + return { + title: goal || "Your Scout workspace", + summary: `A practical path through ${resources.length} free resources chosen for "${goal}".`, + objectives: skills.map((skill) => `Practise ${skill} on a real project`), + difficulty: "intermediate", + modules, + }; +} + +function normalizeModules(plan: ScoutPlan, resources: ScoutResource[]): PlanModule[] { + const byId = resourceMap(resources); + const seen = new Set(); + const modules: PlanModule[] = []; + + for (const module of plan.modules.slice(0, 6)) { + const tasks: PlanModule["tasks"] = []; + const resourceIds: string[] = []; + + for (const task of module.tasks) { + const resource = byId.get(task.resourceId); + if (!resource) continue; + + const startSeconds = + typeof task.startSeconds === "number" && Number.isFinite(task.startSeconds) && task.startSeconds >= 0 + ? Math.floor(task.startSeconds) + : null; + const duration = resource.durationSeconds ?? null; + const endSeconds = + typeof task.endSeconds === "number" && Number.isFinite(task.endSeconds) && task.endSeconds > (startSeconds ?? 0) + ? Math.min(Math.floor(task.endSeconds), duration ? duration + 30 : Number.MAX_SAFE_INTEGER) + : null; + + const key = `${resource.id}:${startSeconds ?? ""}`; + if (seen.has(key)) continue; + seen.add(key); + + tasks.push({ + title: clip(task.title, 140), + resourceId: resource.id, + startSeconds, + endSeconds, + estimatedMinutes: task.estimatedMinutes ?? null, + }); + resourceIds.push(resource.id); + } + + if (tasks.length === 0) continue; + modules.push({ + title: clip(module.title || module.skill || "Module", 120), + description: clip(module.description ?? "", 600), + skill: module.skill ?? "", + resourceIds: unique(resourceIds), + tasks, + }); + } + + // Every approved resource must appear somewhere, even when the model + // forgot it. The appended module stays honest about why it exists. + const missing = resources.filter((resource) => !seen.has(`${resource.id}:`)); + if (missing.length > 0) { + modules.push({ + title: "Also included", + description: "Resources from your approved picks that did not fit an earlier module.", + skill: "Additional practice", + resourceIds: missing.map((resource) => resource.id), + tasks: missing.map((resource) => ({ + title: `${resource.kind === "video" ? "Watch" : "Read"} ${resource.title}`, + resourceId: resource.id, + startSeconds: null, + endSeconds: null, + estimatedMinutes: taskMinutes(resource, null, null), + })), + }); + } + + return modules; +} + +function estimatedDuration(tasks: Task[]) { + const minutes = tasks.reduce((total, task) => { + const value = Number(task.estimatedTime?.replace(/[^0-9]/g, "") ?? 0); + return total + (Number.isFinite(value) ? value : 0); + }, 0); + + if (minutes < 60) return `${minutes} min`; + const hours = Math.floor(minutes / 60); + const rest = minutes % 60; + return rest > 0 ? `${hours}h ${rest}m` : `${hours}h`; +} + +function buildRoadmap( + packId: string, + goal: string, + plan: ScoutPlan, + modules: PlanModule[], + resources: ScoutResource[], +): Roadmap { + const byId = resourceMap(resources); + const now = new Date().toISOString(); + const sections: Roadmap["sections"] = []; + + modules.forEach((module, moduleIndex) => { + const moduleResources = module.resourceIds.map((id) => byId.get(id)).filter((item): item is ScoutResource => Boolean(item)); + const tasks: Task[] = []; + + module.tasks.forEach((task, taskIndex) => { + const resource = byId.get(task.resourceId); + if (!resource) return; + tasks.push( + buildTask({ + packId, + moduleIndex, + taskIndex, + resource, + verb: resource.kind === "video" ? "Watch" : "Read", + startSeconds: task.startSeconds, + endSeconds: task.endSeconds, + title: task.title, + estimatedMinutes: task.estimatedMinutes, + }), + ); + }); + + const videos = moduleResources.map(videoFor).filter((video): video is Video => Boolean(video)); + const links = moduleResources + .map((resource) => attachment(resource, resource.kind === "video" ? (resource.chapters[0]?.startSeconds ?? null) : null)) + .filter((link): link is Resource => Boolean(link)); + + const section: ModuleSection = { + id: `sec_${packId}_m${moduleIndex}`, + title: module.title, + order: moduleIndex, + type: "module", + metadata: { + estimatedDuration: estimatedDuration(tasks), + taskCount: tasks.length, + keyOutcome: module.skill || undefined, + }, + data: { + description: module.description, + estimatedTime: estimatedDuration(tasks), + concepts: moduleResources.flatMap((resource) => resource.skillTags).slice(0, 10).join(", "), + objectives: module.skill ? [`Practise ${module.skill}`] : [], + notes: module.skill, + tasks, + resources: links, + videos, + completed: false, + }, + }; + + sections.push(section); + }); + + const allTasks = sections.flatMap((section) => (section.type === "module" ? section.data.tasks : [])); + + const notes: NoteSection = { + id: `sec_${packId}_notes`, + title: "Scout notes", + order: sections.length, + type: "notes", + metadata: { taskCount: 0 }, + data: resources.map((resource, index) => { + const body = [ + resource.summary ?? "", + "", + ...resource.keyPoints.slice(0, 5).map((point) => { + const stamp = + resource.kind === "video" && resource.externalId && point.atSeconds != null + ? `[${formatTimestamp(point.atSeconds)}](${deepLink(resource.externalId, point.atSeconds)}) — ` + : ""; + return `- ${stamp}${point.text}${point.quote ? `\n > ${point.quote}` : ""}`; + }), + ] + .join("\n") + .trim(); + + const note: Note = { + id: `note_${packId}_${index}`, + title: resource.title, + content: body || "No notes were extracted for this resource.", + createdAt: now, + updatedAt: now, + }; + return note; + }), + }; + sections.push(notes); + + const allLinks = resources + .map((resource) => + attachment(resource, resource.kind === "video" ? (resource.chapters[0]?.startSeconds ?? null) : null), + ) + .filter((link): link is Resource => Boolean(link)); + + if (allLinks.length > 0) { + const resourceSection: ResourceSection = { + id: `sec_${packId}_resources`, + title: "Resources", + order: sections.length, + type: "resources", + metadata: { taskCount: 0 }, + data: allLinks, + }; + sections.push(resourceSection); + } + + const summary = clip(plan.summary, 900); + const objectives = plan.objectives.map((objective) => clip(objective, 140)).filter(Boolean).slice(0, 6); + + return { + id: `rm_${packId}`, + title: clip(plan.title || goal, 140), + mode: "general", + summary, + objectives, + sections, + rawContent: [ + `# ${plan.title || goal}`, + "", + summary, + "", + ...modules.map((module, index) => `## ${index + 1}. ${module.title}\n\n${module.description}`), + ].join("\n"), + createdAt: now, + updatedAt: now, + contentType: "curriculum", + detectedContext: "scout", + totalEstimatedDuration: estimatedDuration(allTasks), + difficulty: plan.difficulty, + goal, + packVersion: `scout-${SCOUT_PROMPT_VERSION}`, + }; +} + +function deterministicQuizzes(roadmapId: string, packId: string, modules: ModuleSection[], resources: ScoutResource[]): SrsItem[] { + const byId = resourceMap(resources); + const cards: SrsItem[] = []; + const now = Date.now(); + + for (const module of modules) { + let moduleCards = 0; + + for (const resource of byId.values()) { + if (moduleCards >= 2) break; + if (!module.data.tasks.some((task) => task.attachments.some((link) => link.id.includes(resource.id)))) continue; + + for (const point of resource.keyPoints) { + if (moduleCards >= 2) break; + if (!point.quote || point.quote.length < 12) continue; + + cards.push({ + id: `srs_${packId}_${cards.length}`, + roadmapId, + sectionId: module.id, + prompt: `In your own words: ${clip(point.text, 180)}`, + answer: point.quote, + easeFactor: 2.5, + intervalDays: 0, + repetitions: 0, + dueAt: new Date(now + (cards.length < 3 ? 0 : 24 * 3600_000)).toISOString(), + lastReviewedAt: null, + }); + moduleCards += 1; + } + } + } + + return cards; +} + +function validateQuizCards( + plan: ScoutQuizPlan, + modules: ModuleSection[], + resources: ScoutResource[], + roadmapId: string, +): SrsItem[] { + const byId = resourceMap(resources); + const now = Date.now(); + const cards: SrsItem[] = []; + + for (const card of plan.cards) { + const module = modules[card.moduleIndex]; + const resource = byId.get(card.resourceId); + if (!module || !resource) continue; + + const quote = clip(card.quote, 400); + if (quote.length < 12) continue; + + const source = [ + resource.summary ?? "", + ...resource.keyPoints.map((point) => `${point.text} ${point.quote ?? ""}`), + ...resource.chapters.map((chapter) => chapter.title), + ].join("\n"); + + if (!quoteAppearsIn(quote, source)) continue; + + const point = resource.keyPoints.find((item) => item.quote && quoteAppearsIn(quote, item.quote)); + const atSeconds = + typeof card.atSeconds === "number" && Number.isFinite(card.atSeconds) && card.atSeconds >= 0 + ? Math.floor(card.atSeconds) + : (point?.atSeconds ?? null); + + cards.push({ + id: `srs_${resource.id}_${cards.length}`, + roadmapId, + sectionId: module.id, + prompt: clip(card.prompt, 240), + answer: atSeconds === null ? clip(card.answer, 500) : `${clip(card.answer, 460)} — ${formatTimestamp(atSeconds)}`, + easeFactor: 2.5, + intervalDays: 0, + repetitions: 0, + dueAt: new Date(now + (cards.length < 3 ? 0 : 24 * 3600_000)).toISOString(), + lastReviewedAt: null, + }); + } + + return cards.slice(0, 12); +} + +/** + * Builds the roadmap and review cards from approved resources. The model gets + * one shot; anything it returns is checked against the resources before it is + * allowed into the workspace. + */ +export async function buildWorkspace(input: BuildWorkspaceInput): Promise { + const resources = input.resources.slice(0, 14); + if (resources.length === 0) throw new Error("No approved resources to build from"); + + const payload = { + goal: input.goal, + resources: resources.map((resource) => ({ + id: resource.id, + title: resource.title, + creator: resource.creator ?? null, + kind: resource.kind, + durationSeconds: resource.durationSeconds ?? null, + summary: clip(resource.summary ?? "", 500), + skills: resource.skillTags, + chapters: resource.chapters.slice(0, 14).map((chapter) => ({ + title: chapter.title, + startSeconds: chapter.startSeconds, + endSeconds: chapter.endSeconds ?? null, + })), + keyPoints: resource.keyPoints.slice(0, 3).map((point) => ({ + text: point.text, + atSeconds: point.atSeconds ?? null, + })), + })), + }; + + let plan: ScoutPlan; + let degraded = false; + + try { + const result = await scoutGenerateObject({ + packId: input.packId, + schema: planSchema, + system: SCOUT_PROMPTS.plan.body, + prompt: JSON.stringify(payload), + config: input.config, + }); + plan = result.object; + } catch (error) { + console.warn("[scout] plan generation degraded:", error instanceof Error ? error.message : error); + plan = fallbackPlan(input.goal, resources); + degraded = true; + } + + if (plan.modules.length === 0) { + plan = fallbackPlan(input.goal, resources); + degraded = true; + } + + const modules = normalizeModules(plan, resources); + const roadmap = buildRoadmap(input.packId, input.goal, plan, modules, resources); + const moduleSections = roadmap.sections.filter((section): section is ModuleSection => section.type === "module"); + + let quizzes: SrsItem[] = []; + try { + const moduleInput = moduleSections.map((section, index) => ({ + index, + title: section.title, + resources: unique(section.data.tasks.flatMap((task) => task.attachments.map((link) => link.title))).slice(0, 4), + keyPoints: resources + .filter((resource) => section.data.tasks.some((task) => task.attachments.some((link) => link.id.includes(resource.id)))) + .flatMap((resource) => + resource.keyPoints + .filter((point) => point.quote) + .slice(0, 3) + .map((point) => ({ + resourceId: resource.id, + text: point.text, + atSeconds: point.atSeconds ?? null, + quote: point.quote, + })), + ) + .slice(0, 8), + })); + + const result = await scoutGenerateObject({ + packId: input.packId, + schema: quizSchema, + system: SCOUT_PROMPTS.quiz.body, + prompt: JSON.stringify({ goal: input.goal, modules: moduleInput }), + config: input.config, + }); + quizzes = validateQuizCards(result.object, moduleSections, resources, roadmap.id); + } catch (error) { + console.warn("[scout] quiz generation degraded:", error instanceof Error ? error.message : error); + } + + if (quizzes.length === 0) quizzes = deterministicQuizzes(roadmap.id, input.packId, moduleSections, resources); + + return { roadmap, quizzes, degraded }; +} From 4f5d4e7ad61da73f6436e9a5f6c52667201a1efc Mon Sep 17 00:00:00 2001 From: Screen <228304862+screen-gd@users.noreply.github.com> Date: Wed, 21 Oct 2026 04:20:21 +0400 Subject: [PATCH 12/20] Ship the Scout wizard, its API and the MCP bridge The wizard starts a pack, shows progress, and publishes approved picks into local storage. Agents reach the same SQLite library through a small stdio MCP server, and the research endpoint only accepts validated links. --- mcp/server.mjs | 213 +++++++++ src/components/scout/CostMeter.tsx | 43 ++ src/components/scout/PickList.tsx | 108 +++++ src/components/scout/ScoutProgress.tsx | 52 +++ src/components/scout/SourcesPanel.tsx | 293 ++++++++++++ src/lib/scout/agent-runner.ts | 358 +++++++++++++++ src/lib/scout/api-client.ts | 161 +++++++ src/lib/scout/client.ts | 198 +++++++++ src/routeTree.gen.ts | 220 +++++++++ src/routes/_studio/scout.tsx | 13 + src/routes/api/scout/agents.ts | 66 +++ src/routes/api/scout/digest.ts | 19 + src/routes/api/scout/packs.$id.approve.ts | 38 ++ src/routes/api/scout/packs.$id.ts | 25 ++ src/routes/api/scout/recent.ts | 19 + src/routes/api/scout/resources.ts | 67 +++ src/routes/api/scout/settings.ts | 58 +++ src/routes/api/scout/sources.ts | 64 +++ src/routes/api/scout/start.ts | 52 +++ src/views/studio/scout.tsx | 515 ++++++++++++++++++++++ 20 files changed, 2582 insertions(+) create mode 100644 mcp/server.mjs create mode 100644 src/components/scout/CostMeter.tsx create mode 100644 src/components/scout/PickList.tsx create mode 100644 src/components/scout/ScoutProgress.tsx create mode 100644 src/components/scout/SourcesPanel.tsx create mode 100644 src/lib/scout/agent-runner.ts create mode 100644 src/lib/scout/api-client.ts create mode 100644 src/lib/scout/client.ts create mode 100644 src/routes/_studio/scout.tsx create mode 100644 src/routes/api/scout/agents.ts create mode 100644 src/routes/api/scout/digest.ts create mode 100644 src/routes/api/scout/packs.$id.approve.ts create mode 100644 src/routes/api/scout/packs.$id.ts create mode 100644 src/routes/api/scout/recent.ts create mode 100644 src/routes/api/scout/resources.ts create mode 100644 src/routes/api/scout/settings.ts create mode 100644 src/routes/api/scout/sources.ts create mode 100644 src/routes/api/scout/start.ts create mode 100644 src/views/studio/scout.tsx diff --git a/mcp/server.mjs b/mcp/server.mjs new file mode 100644 index 0000000..cff3b74 --- /dev/null +++ b/mcp/server.mjs @@ -0,0 +1,213 @@ +#!/usr/bin/env node +/** + * Syllo Scout MCP server. + * + * Any MCP-capable agent (opencode, Codex, Claude) can run this file over stdio + * to add links to the Scout library and search what is already there. It talks + * to the same SQLite file as the app; there is no HTTP hop and no API key. + * + * Run: node mcp/server.mjs + * Optional: SCOUT_DB_PATH=/absolute/path/syllo.db node mcp/server.mjs + */ +import Database from "better-sqlite3"; +import { mkdirSync, readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { z } from "zod"; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(here, ".."); +const dbPath = resolve(process.env.SCOUT_DB_PATH?.trim() || resolve(repoRoot, ".data", "syllo.db")); + +mkdirSync(dirname(dbPath), { recursive: true }); + +const db = new Database(dbPath); +db.pragma("journal_mode = WAL"); +db.pragma("foreign_keys = ON"); +db.exec(readFileSync(resolve(repoRoot, "src", "lib", "scout", "schema.sql"), "utf8")); + +function nowIso() { + return new Date().toISOString(); +} + +function newId(prefix) { + return `${prefix}_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`; +} + +function slugify(value) { + return ( + String(value) + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(0, 60) || "resource" + ); +} + +function youtubeId(url) { + const match = /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|shorts\/|embed\/))([A-Za-z0-9_-]{11})/.exec(url); + return match ? match[1] : null; +} + +function inferKind(url) { + if (youtubeId(url)) return "video"; + if (/\.pdf(?:$|[?#])/i.test(url)) return "doc"; + return "article"; +} + +function parseJsonArray(value) { + try { + const parsed = JSON.parse(value || "[]"); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +function toResource(row) { + return { + id: row.id, + kind: row.kind, + title: row.title, + url: row.url, + creator: row.creator ?? null, + summary: row.summary ?? null, + skills: parseJsonArray(row.skill_tags), + chapters: parseJsonArray(row.chapters).length, + createdAt: row.created_at, + updatedAt: row.updated_at, + }; +} + +function addResource({ url, title, note, skill }) { + const cleanUrl = String(url ?? "").trim(); + if (!/^https?:\/\//i.test(cleanUrl)) { + throw new Error("url must start with http:// or https://"); + } + + const existing = db.prepare("SELECT * FROM resources WHERE url = ?").get(cleanUrl); + const now = nowIso(); + + if (existing) { + const skills = parseJsonArray(existing.skill_tags); + if (skill) { + const normalized = String(skill).trim().toLowerCase().slice(0, 60); + if (normalized && !skills.includes(normalized)) skills.push(normalized); + } + db.prepare( + `UPDATE resources SET + summary = COALESCE(?, summary), + skill_tags = ?, + updated_at = ? + WHERE id = ?`, + ).run(note ? String(note).trim().slice(0, 400) : null, JSON.stringify(skills), now, existing.id); + + return { created: false, resource: toResource(db.prepare("SELECT * FROM resources WHERE id = ?").get(existing.id)) }; + } + + const id = newId("res"); + const kind = inferKind(cleanUrl); + const skills = skill ? [String(skill).trim().toLowerCase().slice(0, 60)] : []; + const cleanTitle = String(title ?? "").trim().slice(0, 200) || `${kind === "video" ? "Video" : "Link"} · ${slugify(cleanUrl)}`; + + db.prepare( + `INSERT INTO resources ( + id, kind, source, external_id, url, title, creator, duration_seconds, + published_at, thumbnail_url, description, summary, key_points, skill_tags, + chapters, transcript, transcript_status, transcript_chars, content_hash, + error, created_at, updated_at + ) VALUES (?, ?, 'agent', ?, ?, ?, NULL, NULL, NULL, NULL, NULL, ?, '[]', ?, '[]', NULL, 'skipped', 0, NULL, NULL, ?, ?)`, + ).run( + id, + kind, + kind === "video" ? youtubeId(cleanUrl) : null, + cleanUrl, + cleanTitle, + note ? String(note).trim().slice(0, 400) : "Saved by an agent. Not read yet.", + JSON.stringify(skills), + now, + now, + ); + + return { created: true, resource: toResource(db.prepare("SELECT * FROM resources WHERE id = ?").get(id)) }; +} + +function searchLibrary({ query, limit }) { + const clean = String(query ?? "").trim(); + if (!clean) throw new Error("query is required"); + + const like = `%${clean.replace(/[%_]/g, "")}%`; + const max = Math.min(50, Math.max(1, Number(limit) || 20)); + const rows = db + .prepare( + `SELECT * FROM resources + WHERE title LIKE ? OR summary LIKE ? OR skill_tags LIKE ? OR creator LIKE ? + ORDER BY updated_at DESC + LIMIT ?`, + ) + .all(like, like, like, like, max); + + return { count: rows.length, resources: rows.map(toResource) }; +} + +const server = new McpServer({ name: "syllo-scout", version: "1.0.0" }); + +server.registerTool( + "add_resource", + { + title: "Add a resource to Syllo Scout", + description: + "Save a free learning resource (video, article or doc) to the learner's Syllo Scout library. Use the exact public http(s) URL. Duplicate URLs are updated, not duplicated.", + inputSchema: z.object({ + url: z.string().describe("Public http(s) URL of the resource"), + title: z.string().max(200).optional().describe("Resource title"), + note: z.string().max(400).optional().describe("One or two sentences on why it is useful"), + skill: z.string().max(60).optional().describe("Skill this resource teaches, e.g. 'UI design'"), + }), + }, + async ({ url, title, note, skill }) => { + const result = addResource({ url, title, note, skill }); + const verb = result.created ? "Saved" : "Updated"; + return { + content: [ + { + type: "text", + text: `${verb}: ${result.resource.title}\n${result.resource.url}\nID: ${result.resource.id}`, + }, + ], + structuredContent: result, + }; + }, +); + +server.registerTool( + "search_library", + { + title: "Search the Syllo Scout library", + description: + "Search saved resources by title, summary, skill or creator. Use it before adding a link, and to build a reading or watching list from what the learner already has.", + inputSchema: z.object({ + query: z.string().min(1).describe("Words to search for"), + limit: z.number().int().min(1).max(50).optional().describe("Maximum results (default 20)"), + }), + }, + async ({ query, limit }) => { + const result = searchLibrary({ query, limit }); + const text = + result.count === 0 + ? `Nothing found for "${query}".` + : result.resources + .map((resource) => `• ${resource.title}\n ${resource.url}${resource.skills.length ? `\n Skills: ${resource.skills.join(", ")}` : ""}`) + .join("\n\n"); + + return { + content: [{ type: "text", text }], + structuredContent: result, + }; + }, +); + +const transport = new StdioServerTransport(); +await server.connect(transport); diff --git a/src/components/scout/CostMeter.tsx b/src/components/scout/CostMeter.tsx new file mode 100644 index 0000000..d5888ee --- /dev/null +++ b/src/components/scout/CostMeter.tsx @@ -0,0 +1,43 @@ +import { formatCents } from "@/lib/scout/api-client"; +import { cn } from "@/lib/utils"; + +interface CostMeterProps { + monthCents: number; + budgetCents: number; + packCents?: number; + className?: string; +} + +/** Estimated Scout spend against the learner's monthly budget. */ +export default function CostMeter({ monthCents, budgetCents, packCents = 0, className }: CostMeterProps) { + const hasBudget = budgetCents > 0; + const percent = hasBudget ? Math.min(100, Math.round((monthCents / budgetCents) * 100)) : 0; + const nearLimit = hasBudget && percent >= 80; + + return ( +
+
+ Monthly cost + + {formatCents(monthCents)} + {hasBudget ? ` / ${formatCents(budgetCents)}` : " / no cap"} + +
+ +
+
0 ? 2 : 0)}%` : "0%" }} + /> +
+ +

+ {packCents > 0 ? `This pack: ${formatCents(packCents)}. ` : ""} + Estimated from published token prices, not a bill. +

+
+ ); +} diff --git a/src/components/scout/PickList.tsx b/src/components/scout/PickList.tsx new file mode 100644 index 0000000..5b59084 --- /dev/null +++ b/src/components/scout/PickList.tsx @@ -0,0 +1,108 @@ +import { Check, FileText, Video } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { formatTimestamp, humanDuration } from "@/lib/scout/format"; +import type { ScoutResource } from "@/lib/scout/types"; + +interface PickListProps { + resources: ScoutResource[]; + selected: Set; + onToggle: (id: string) => void; +} + +export default function PickList({ resources, selected, onToggle }: PickListProps) { + return ( +
    + {resources.map((resource) => { + const checked = selected.has(resource.id); + const Icon = resource.kind === "video" ? Video : FileText; + const quote = resource.keyPoints.find((point) => point.quote)?.quote; + const chapters = resource.chapters.slice(0, 4); + + return ( +
  • +
    + + +
    +
    + + {resource.kind} + + {resource.creator ? {resource.creator} : null} + {resource.durationSeconds ? {humanDuration(resource.durationSeconds)} : null} + {resource.transcriptStatus === "unavailable" || resource.transcriptStatus === "skipped" ? ( + description only + ) : null} +
    + + + {resource.title} + + + {resource.summary ? ( +

    {resource.summary}

    + ) : null} + + {quote ? ( +

    + “{quote}” +

    + ) : null} + + {chapters.length > 0 ? ( + + ) : null} + + {resource.skillTags.length > 0 ? ( +
    + {resource.skillTags.slice(0, 4).map((tag) => ( + + {tag} + + ))} +
    + ) : null} +
    +
    +
  • + ); + })} +
+ ); +} diff --git a/src/components/scout/ScoutProgress.tsx b/src/components/scout/ScoutProgress.tsx new file mode 100644 index 0000000..f631063 --- /dev/null +++ b/src/components/scout/ScoutProgress.tsx @@ -0,0 +1,52 @@ +import { Check } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const STEPS = [ + { number: 1, label: "Set a goal" }, + { number: 2, label: "Approve picks" }, + { number: 3, label: "Roadmap" }, +] as const; + +interface ScoutProgressProps { + /** 1 = goal, 2 = finding/approving, 3 = building/ready. */ + step: 1 | 2 | 3; + detail?: string; +} + +export default function ScoutProgress({ step, detail }: ScoutProgressProps) { + return ( +
+
    + {STEPS.map((item) => { + const done = item.number < step; + const active = item.number === step; + + return ( +
  1. + + + {item.label} + +
  2. + ); + })} +
+ {detail ?

{detail}

: null} +
+ ); +} diff --git a/src/components/scout/SourcesPanel.tsx b/src/components/scout/SourcesPanel.tsx new file mode 100644 index 0000000..6dda459 --- /dev/null +++ b/src/components/scout/SourcesPanel.tsx @@ -0,0 +1,293 @@ +import { useState } from "react"; +import { BookOpen, Link2, Plus, Trash2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { Textarea } from "@/components/ui/textarea"; +import CostMeter from "@/components/scout/CostMeter"; +import { cn } from "@/lib/utils"; +import type { ScoutResource, ScoutSettings, ScoutSource, ScoutSourceKind, SpendSummary } from "@/lib/scout/types"; + +interface SourcesPanelProps { + sources: ScoutSource[]; + settings: ScoutSettings | null; + spend: SpendSummary | null; + books: ScoutResource[]; + onAddSource: (input: { kind: ScoutSourceKind; label?: string; value: string }) => Promise; + onRemoveSource: (id: string) => Promise; + onToggleSource: (id: string, enabled: boolean) => Promise; + onAddBook: (title: string, chapters: string[]) => Promise; + onAddUrl: (url: string, note?: string) => Promise; + onToggleAutoCheck: (enabled: boolean) => Promise; + onPause: (paused: boolean) => Promise; +} + +const KIND_LABEL: Record = { + youtube_channel: "YouTube channel", + creator: "Creator", + site: "Site", + book: "Book", +}; + +const ADD_KINDS: Array<{ value: ScoutSourceKind; label: string; placeholder: string }> = [ + { value: "creator", label: "Creator", placeholder: "@handle, channel URL, or name" }, + { value: "youtube_channel", label: "Channel", placeholder: "@handle or youtube.com/@handle" }, + { value: "site", label: "Site", placeholder: "developer.mozilla.org" }, +]; + +export default function SourcesPanel({ + sources, + settings, + spend, + books, + onAddSource, + onRemoveSource, + onToggleSource, + onAddBook, + onAddUrl, + onToggleAutoCheck, + onPause, +}: SourcesPanelProps) { + const [kind, setKind] = useState("creator"); + const [value, setValue] = useState(""); + const [bookTitle, setBookTitle] = useState(""); + const [chapters, setChapters] = useState(""); + const [url, setUrl] = useState(""); + const [note, setNote] = useState(""); + const [working, setWorking] = useState(null); + const [localError, setLocalError] = useState(null); + + const activeKind = ADD_KINDS.find((item) => item.value === kind) ?? ADD_KINDS[0]; + + async function run(key: string, task: () => Promise) { + setWorking(key); + setLocalError(null); + try { + await task(); + } catch (error) { + setLocalError(error instanceof Error ? error.message : "That did not work."); + } finally { + setWorking(null); + } + } + + return ( +