From d143aeeab951151e8deb0c8dd0f277f820194690 Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Fri, 16 Oct 2026 21:20:02 -0400 Subject: [PATCH 1/7] docs: llm generated frontend docs --- docs/README.md | 1 + docs/frontend-architecture.md | 98 + scripts/fake-backend.mjs | 1061 ++++++++ web-interface/package.json | 1 + web-interface/pnpm-lock.yaml | 4599 +++++++++++++++++++++++++++++++++ 5 files changed, 5760 insertions(+) create mode 100644 docs/frontend-architecture.md create mode 100644 scripts/fake-backend.mjs create mode 100644 web-interface/pnpm-lock.yaml diff --git a/docs/README.md b/docs/README.md index 17a1067..ce6536a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ host-specific settings when provisioning another environment. | Authenticate and submit from a terminal | [CLI](../cli/docs/setup.md) | | Run automated checks and read recorded live evidence | [Testing](testing.md) | | Maintain UI components and interactions | [Design system](design-system.md) | +| Understand frontend architecture and backend offloading | [Frontend architecture](frontend-architecture.md) | | Read completed scope, exclusions and release verification | [Execution record](department-rollout-execution.md) | | See what repository cleanup removed and verified | [Cleanup record](repository-cleanup.md) | diff --git a/docs/frontend-architecture.md b/docs/frontend-architecture.md new file mode 100644 index 0000000..41e2c50 --- /dev/null +++ b/docs/frontend-architecture.md @@ -0,0 +1,98 @@ +# Frontend Architecture & Backend Offloading + +Documentation of the current Mist frontend ([`web-interface`](../web-interface)), its structural organization, and a roadmap of client-side processing to offload to the backend. + +## Architecture overview + +```mermaid +flowchart TD + User([User Browser]) --> Shell[Root Shell: __root.tsx] + Shell --> AG[AccountGate: auth.tsx] + AG --> TG[TeamGate: teams.tsx] + TG --> Nav[Navbar.tsx] + TG --> RouterOutlet[TanStack Router Outlet] + + RouterOutlet --> DPage[Dashboard: dashboard.tsx] + RouterOutlet --> JPage[Jobs Page: jobs.tsx] + RouterOutlet --> SPage[Datasets Page: datasets.tsx] + RouterOutlet --> MPage[Machines Page: machines.tsx] + RouterOutlet --> TPage[Teams Page: teams.tsx] + RouterOutlet --> PPage[Profile: profile.tsx] + + JPage --> JSF[JobSubmissionForm.tsx] + JPage --> JC[JobCard.tsx] + JC --> JL[JobLogs.tsx] + JC --> JF[JobFiles.tsx] + + SPage --> TSB[TeamStorageBrowser.tsx] +``` + +### Technology stack + +- **Vite 7.3**: Build tool and development proxy (`/api` and `/auth` forwards). +- **React 19.3**: Functional component architecture with custom hooks. +- **TanStack Router**: File-based routing located in `src/routes/` with code-splitting. +- **Tailwind CSS 4**: Modern styling via `@tailwindcss/vite` and customized Mist theme classes. +- **Better Auth Client**: Authentication state and admin member management. +- **Vitest 5 & Testing Library**: Unit and component tests. + +### Source layout + +| Location | Purpose | +|---|---| +| `src/main.tsx`, `router.tsx` | Router initialization and DOM hydration | +| `src/routes/__root.tsx` | Shell wrapper containing `AccountGate`, `TeamGate`, and `Navbar` | +| `src/routes/` | File-based routes (`dashboard.tsx`, `jobs.tsx`, `datasets.tsx`, `machines.tsx`, `teams.tsx`, `profile.tsx`) | +| `src/auth.tsx` | Better Auth client configuration, `AccountContext`, and `AccountGate` | +| `src/teams.tsx` | `TeamContext`, active team selection, workspace selector | +| `src/api.ts` | Typed HTTP client for jobs, datasets, hardware, and teams | +| `src/hooks/usePolling.ts` | In-flight request deduplication and abortable periodic polling | +| `src/components/jobs/` | `JobSubmissionForm`, `JobCard`, `JobLogs`, `JobFiles` | +| `src/components/` | `HardwarePanel`, `TeamStorageBrowser`, `Modal`, `Picker`, `Tabs`, `Card`, `Buttons` | + +--- + +## Backend offloading plan + +To keep the frontend as lightweight and simple as possible, the following responsibilities currently performed on the client should be handled by the backend: + +1. **Pagination and filtering**: + - `JobsPage`: Replace client-side slicing and array filtering with query parameters on `GET /jobs?status=...&q=...&page=...&limit=...`. + - `DatasetsPage`: Add `GET /datasets?q=...`. + - `AccountPage`: Replace Better Auth `listUsers({ limit: 1000 })` with a paginated endpoint `GET /members?page=...&q=...`. +2. **Metrics aggregation**: + - Provide `GET /metrics/summary` or summary counters in list endpoints to avoid transferring full item arrays just to compute badge totals. +3. **Image catalog and validation**: + - Move registry string parsing and image approval checks from `JobSubmissionForm` to server-side submission validation. +4. **Quota and limit calculation**: + - Replace client-side calculation of `maxDevices` and `maxTimeout` with authoritative constraints provided by the team policy API. +5. **Form parsing**: + - Accept raw multiline environment variable text and shell commands on `POST /jobs`, delegating parsing and validation to the server. +6. **Error status handling**: + - Replace English error string matching (e.g. `isPreparing`) with typed machine-readable status fields (`{ status: "provisioning" }`). +7. **Permission flags**: + - Return explicit capability booleans (`can_cancel`, `can_delete`, `can_manage`) on entities rather than parsing role strings on the client. +8. **Atomic operations**: + - Consolidate multi-call client sequences (e.g. team creation + creator enrollment, password reset + session revocation) into atomic backend operations. +9. **Event-driven updates**: + - Replace high-frequency polling with Server-Sent Events (SSE) or WebSockets for live job status and log streaming. + +--- + +## Testing with the fake backend + +A self-contained testing backend is provided in `scripts/fake-backend.mjs`. It simulates all Mist API endpoints and Better Auth handlers in memory. + +### Running locally + +```bash +# Terminal 1: Start fake backend (default port 3000) +npm --prefix web-interface run fake-backend +``` + +```bash +# Terminal 2: Start frontend dev server (port 3001) +npm --prefix web-interface run dev -- --host 127.0.0.1 --port 3001 --strictPort +``` + +Open http://127.0.0.1:3001 in your browser. Pre-seeded users include `ada@utmist.gitlab.io` (admin), `alan@utmist.gitlab.io`, and `grace@utmist.gitlab.io`. diff --git a/scripts/fake-backend.mjs b/scripts/fake-backend.mjs new file mode 100644 index 0000000..90c8c29 --- /dev/null +++ b/scripts/fake-backend.mjs @@ -0,0 +1,1061 @@ +#!/usr/bin/env node +/** + * Mist Fake Backend Server + * + * A standalone, zero-dependency Node.js HTTP server simulating the Mist Go API + * and Better Auth service. Designed for local frontend development, testing, + * and rapid prototyping without requiring k3s, GPUs, or Better Auth SQLite. + * + * Usage: + * node scripts/fake-backend.mjs + * PORT=3000 node scripts/fake-backend.mjs + * npm --prefix web-interface run fake-backend + */ + +import http from 'node:http' +import { URL } from 'node:url' +import crypto from 'node:crypto' + +const PORT = Number(process.env.PORT || process.env.MIST_FAKE_PORT || 3000) +const HOST = process.env.HOST || '127.0.0.1' + +// --- In-Memory State & Seed Data --- + +const state = { + users: [ + { + id: 'usr_ada', + name: 'Ada Lovelace', + email: 'ada@utmist.gitlab.io', + role: 'admin', + banned: false, + }, + { + id: 'usr_alan', + name: 'Alan Turing', + email: 'alan@utmist.gitlab.io', + role: 'user', + banned: false, + }, + { + id: 'usr_grace', + name: 'Grace Hopper', + email: 'grace@utmist.gitlab.io', + role: 'user', + banned: false, + }, + ], + currentUserIndex: 0, // Ada Lovelace by default + teams: [ + { + id: 'team-vision', + name: 'Computer Vision', + disabled: false, + policy: { + cpu: '16', + memory: '64Gi', + nvidia: 2, + tenstorrent: 4, + concurrent: 4, + queued: 20, + storage_gib: 300, + model_storage_gib: 600, + runtime_seconds: 86400, + registries: ['docker.io', 'ghcr.io', 'quay.io'], + }, + members: [ + { + id: 'usr_ada', + name: 'Ada Lovelace', + email: 'ada@utmist.gitlab.io', + common_writer: true, + }, + { + id: 'usr_alan', + name: 'Alan Turing', + email: 'alan@utmist.gitlab.io', + common_writer: false, + }, + ], + grants: [ + { + id: 'grant-nlp-share', + target_team: 'team-nlp', + scope: 'common', + }, + ], + }, + { + id: 'team-nlp', + name: 'Natural Language Processing', + disabled: false, + policy: { + cpu: '8', + memory: '32Gi', + nvidia: 1, + tenstorrent: 2, + concurrent: 2, + queued: 10, + storage_gib: 200, + model_storage_gib: 400, + runtime_seconds: 43200, + registries: ['docker.io', 'ghcr.io'], + }, + members: [ + { + id: 'usr_ada', + name: 'Ada Lovelace', + email: 'ada@utmist.gitlab.io', + common_writer: true, + }, + { + id: 'usr_grace', + name: 'Grace Hopper', + email: 'grace@utmist.gitlab.io', + common_writer: false, + }, + ], + grants: [], + }, + ], + storageBudgetGiB: 2000, + jobs: [ + { + id: 'job-seed-001', + name: 'ResNet50 Vision Benchmark', + created: new Date(Date.now() - 3600000).toISOString(), + job_state: 'Success', + accelerator: 'nvidia', + device_count: 2, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '8', + memory: '32Gi', + node: 'mist-gpu-node-01', + exit_code: 0, + message: 'Job completed successfully in 1420s', + checkpoint_directory: '/outputs/checkpoints/job-seed-001', + output_directory: '/outputs/runs/job-seed-001', + timeout_seconds: 7200, + team_id: 'team-vision', + creator_id: 'usr_ada', + creator_name: 'Ada Lovelace', + storage_scope: 'common', + can_cancel: false, + }, + { + id: 'job-seed-002', + name: 'BERT Fine-tuning Test', + created: new Date(Date.now() - 1800000).toISOString(), + job_state: 'InProgress', + accelerator: 'nvidia', + device_count: 1, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '4', + memory: '16Gi', + node: 'mist-gpu-node-01', + checkpoint_directory: '/outputs/checkpoints/job-seed-002', + output_directory: '/outputs/runs/job-seed-002', + timeout_seconds: 3600, + team_id: 'team-nlp', + creator_id: 'usr_grace', + creator_name: 'Grace Hopper', + storage_scope: 'usr_grace', + can_cancel: true, + }, + { + id: 'job-seed-003', + name: 'Tenstorrent Wormhole Matrix Multiplication', + created: new Date(Date.now() - 600000).toISOString(), + job_state: 'Scheduled', + accelerator: 'tenstorrent', + device_count: 2, + image: 'ghcr.io/utmist/tt-metal-runtime:latest', + cpu: '4', + memory: '16Gi', + message: 'Queued; waiting for 2 Tenstorrent n300 board allocations', + checkpoint_directory: '/outputs/checkpoints/job-seed-003', + output_directory: '/outputs/runs/job-seed-003', + timeout_seconds: 1800, + team_id: 'team-vision', + creator_id: 'usr_alan', + creator_name: 'Alan Turing', + storage_scope: 'usr_alan', + can_cancel: true, + }, + ], + datasets: [ + { + id: 'ds-cifar100', + name: 'CIFAR-100 Benchmark Dataset', + filename: 'cifar100.zip', + size: 169000000, + files: 100, + sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + created: new Date(Date.now() - 86400000).toISOString(), + state: 'ready', + team_id: 'team-vision', + team_name: 'Computer Vision', + scope: 'common', + member_name: 'Ada Lovelace', + shared: false, + }, + { + id: 'ds-wikitext', + name: 'Wikitext-103 Subset', + filename: 'wikitext.tar.gz', + size: 512000000, + files: 1, + sha256: 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', + created: new Date(Date.now() - 43200000).toISOString(), + state: 'ready', + team_id: 'team-nlp', + team_name: 'Natural Language Processing', + scope: 'common', + member_name: 'Grace Hopper', + shared: true, + }, + ], + jobLogs: new Map([ + [ + 'job-seed-001', + `[2026-10-16 20:00:01] [INFO] Initializing PyTorch runtime on NVIDIA node 'mist-gpu-node-01' +[2026-10-16 20:00:03] [INFO] Allocated 2x NVIDIA RTX A6000 GPUs (CUDA devices 0, 1) +[2026-10-16 20:00:05] [INFO] Loading CIFAR-100 dataset from /inputs/cifar100 +[2026-10-16 20:00:12] [INFO] Epoch 1/10: train_loss=2.31, val_acc=68.4% +[2026-10-16 20:10:45] [INFO] Epoch 10/10: train_loss=0.42, val_acc=84.2% +[2026-10-16 20:23:41] [INFO] Checkpoint saved to /outputs/runs/job-seed-001/checkpoint_final.pt +[2026-10-16 20:23:42] [INFO] Training complete. Process exited with code 0.`, + ], + [ + 'job-seed-002', + `[2026-10-16 20:30:00] [INFO] BERT fine-tuning initialized. +[2026-10-16 20:30:02] [INFO] Using GPU 0 (Allocated 1x GPU) +[2026-10-16 20:30:15] [INFO] Warmup steps complete. Training batch 450/2000... +[2026-10-16 20:30:30] [INFO] Current loss: 1.284`, + ], + [ + 'job-seed-003', + `[2026-10-16 20:50:00] [INFO] Job queued in team 'team-vision'. Awaiting Tenstorrent admission controller.`, + ], + ]), + jobFiles: new Map([ + [ + 'job-seed-001', + [ + { + path: 'checkpoint_final.pt', + size: 104857600, + modified: new Date(Date.now() - 3600000).toISOString(), + }, + { + path: 'training_metrics.json', + size: 4096, + modified: new Date(Date.now() - 3600000).toISOString(), + }, + { + path: 'run_summary.txt', + size: 512, + modified: new Date(Date.now() - 3600000).toISOString(), + }, + ], + ], + ]), +} + +// Automatically progress queued jobs to running and completed +setInterval(() => { + for (const job of state.jobs) { + if (job.job_state === 'Scheduled') { + job.job_state = 'InProgress' + job.node = + job.accelerator === 'tenstorrent' + ? 'mist-tt-worker-01' + : 'mist-gpu-node-01' + job.message = 'Pod running on ' + job.node + const logs = + state.jobLogs.get(job.id) || + `[${new Date().toISOString()}] Job submitted.\n` + state.jobLogs.set( + job.id, + logs + + `[${new Date().toISOString()}] Admitted to cluster. Initializing pod on ${job.node}...\n`, + ) + } else if (job.job_state === 'InProgress') { + const elapsed = Date.now() - new Date(job.created).getTime() + if (elapsed > 20000) { + // Complete job after 20s + job.job_state = 'Success' + job.exit_code = 0 + job.message = 'Job completed successfully' + job.can_cancel = false + const logs = state.jobLogs.get(job.id) || '' + state.jobLogs.set( + job.id, + logs + + `[${new Date().toISOString()}] Execution finished with code 0.\nOutputs written to /outputs.\n`, + ) + if (!state.jobFiles.has(job.id)) { + state.jobFiles.set(job.id, [ + { + path: 'output.log', + size: 2048, + modified: new Date().toISOString(), + }, + { + path: 'model_weights.bin', + size: 52428800, + modified: new Date().toISOString(), + }, + ]) + } + } + } + } +}, 5000) + +// --- Helper Functions --- + +function parseJSONBody(req) { + return new Promise((resolve, reject) => { + let data = '' + req.on('data', (chunk) => { + data += chunk + if (data.length > 50 * 1024 * 1024) { + req.destroy() + reject(new Error('Request payload too large')) + } + }) + req.on('end', () => { + if (!data.trim()) return resolve({}) + try { + resolve(JSON.parse(data)) + } catch (err) { + reject(new Error('Malformed JSON: ' + err.message)) + } + }) + req.on('error', reject) + }) +} + +function sendJSON(res, statusCode, payload) { + const body = JSON.stringify(payload) + res.writeHead(statusCode, { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(body), + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, X-Mist-Team, Authorization', + }) + res.end(body) +} + +function sendError(res, statusCode, message) { + sendJSON(res, statusCode, { error: message }) +} + +// --- Request Router --- + +const server = http.createServer(async (req, res) => { + // CORS Preflight + if (req.method === 'OPTIONS') { + res.writeHead(204, { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': + 'Content-Type, X-Mist-Team, Authorization', + 'Access-Control-Max-Age': '86400', + }) + res.end() + return + } + + const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`) + let pathname = parsedUrl.pathname + const query = Object.fromEntries(parsedUrl.searchParams.entries()) + const activeTeam = req.headers['x-mist-team'] || query.team_id || '' + + // Normalize path by stripping optional '/api' prefix (Vite proxy rewrites /api, direct hits do not) + if (pathname.startsWith('/api/')) { + pathname = pathname.slice(4) + } else if (pathname === '/api') { + pathname = '/' + } + + const currentUser = state.users[state.currentUserIndex] || null + + try { + // ---------------------------------------------------- + // AUTH & SESSION ENDPOINTS + // ---------------------------------------------------- + + // Session check used by AccountGate + if (pathname === '/session' && req.method === 'GET') { + sendJSON(res, 200, { + enabled: true, + user: currentUser, + }) + return + } + + // Better Auth: Session retrieval + if (pathname === '/auth/get-session' && req.method === 'GET') { + if (!currentUser) { + sendJSON(res, 200, null) + } else { + sendJSON(res, 200, { + user: currentUser, + session: { + id: 'sess_' + currentUser.id, + userId: currentUser.id, + expiresAt: new Date(Date.now() + 86400000).toISOString(), + }, + }) + } + return + } + + // Better Auth: Email Sign-in + if (pathname === '/auth/sign-in/email' && req.method === 'POST') { + const body = await parseJSONBody(req) + const user = state.users.find( + (u) => u.email.toLowerCase() === (body.email || '').toLowerCase(), + ) + if (!user) { + sendJSON(res, 401, { + error: { message: 'Invalid credentials or user not found' }, + }) + return + } + if (user.banned) { + sendJSON(res, 403, { + error: { message: 'Account is deactivated' }, + }) + return + } + state.currentUserIndex = state.users.findIndex((u) => u.id === user.id) + sendJSON(res, 200, { + user, + session: { + id: 'sess_' + user.id, + userId: user.id, + expiresAt: new Date(Date.now() + 86400000).toISOString(), + }, + }) + return + } + + // Better Auth: Sign-out + if (pathname === '/auth/sign-out' && req.method === 'POST') { + state.currentUserIndex = -1 // signed out + sendJSON(res, 200, { success: true }) + return + } + + // Better Auth: Change Password + if (pathname === '/auth/change-password' && req.method === 'POST') { + await parseJSONBody(req) + sendJSON(res, 200, { success: true }) + return + } + + // Better Auth Admin: List Users + if ( + (pathname === '/auth/admin/list-users' || + pathname === '/auth/admin/listUsers') && + (req.method === 'GET' || req.method === 'POST') + ) { + sendJSON(res, 200, { + users: state.users, + total: state.users.length, + }) + return + } + + // Better Auth Admin: Create User + if (pathname === '/auth/admin/create-user' && req.method === 'POST') { + const body = await parseJSONBody(req) + if (!body.name || !body.email) { + sendError(res, 400, 'Name and email are required') + return + } + const newUser = { + id: 'usr_' + crypto.randomBytes(4).toString('hex'), + name: body.name, + email: body.email, + role: body.role || 'user', + banned: false, + } + state.users.push(newUser) + sendJSON(res, 200, { user: newUser }) + return + } + + // Better Auth Admin: Set User Password + if (pathname === '/auth/admin/set-user-password' && req.method === 'POST') { + await parseJSONBody(req) + sendJSON(res, 200, { success: true }) + return + } + + // Better Auth Admin: Revoke User Sessions + if ( + pathname === '/auth/admin/revoke-user-sessions' && + req.method === 'POST' + ) { + await parseJSONBody(req) + sendJSON(res, 200, { success: true }) + return + } + + // Better Auth Admin: Ban / Unban User + if (pathname === '/auth/admin/ban-user' && req.method === 'POST') { + const body = await parseJSONBody(req) + const target = state.users.find((u) => u.id === body.userId) + if (target) target.banned = true + sendJSON(res, 200, { success: true }) + return + } + if (pathname === '/auth/admin/unban-user' && req.method === 'POST') { + const body = await parseJSONBody(req) + const target = state.users.find((u) => u.id === body.userId) + if (target) target.banned = false + sendJSON(res, 200, { success: true }) + return + } + + // ---------------------------------------------------- + // HARDWARE & IMAGES ENDPOINTS + // ---------------------------------------------------- + + if (pathname === '/hardware' && req.method === 'GET') { + sendJSON(res, 200, { + observed_at: new Date().toISOString(), + machines: [ + { + name: 'mist-gpu-node-01', + address: '10.0.1.10', + ready: true, + schedulable: true, + cpu_allocatable: '32', + memory_allocatable: '128Gi', + }, + { + name: 'mist-tt-worker-01', + address: '10.0.1.11', + ready: true, + schedulable: true, + cpu_allocatable: '16', + memory_allocatable: '64Gi', + }, + ], + pools: [ + { + accelerator: 'nvidia', + node: 'mist-gpu-node-01', + unit: 'GPU', + total: 4, + allocated: state.jobs + .filter( + (j) => + j.accelerator === 'nvidia' && j.job_state === 'InProgress', + ) + .reduce((sum, j) => sum + (j.device_count || 1), 0), + available: Math.max( + 0, + 4 - + state.jobs + .filter( + (j) => + j.accelerator === 'nvidia' && + j.job_state === 'InProgress', + ) + .reduce((sum, j) => sum + (j.device_count || 1), 0), + ), + ready: true, + }, + { + accelerator: 'tenstorrent', + node: 'mist-tt-worker-01', + unit: 'board', + total: 4, + allocated: state.jobs + .filter( + (j) => + j.accelerator === 'tenstorrent' && + j.job_state === 'InProgress', + ) + .reduce((sum, j) => sum + (j.device_count || 1), 0), + available: Math.max( + 0, + 4 - + state.jobs + .filter( + (j) => + j.accelerator === 'tenstorrent' && + j.job_state === 'InProgress', + ) + .reduce((sum, j) => sum + (j.device_count || 1), 0), + ), + ready: true, + chips_per_device: 2, + }, + ], + }) + return + } + + if (pathname === '/images' && req.method === 'GET') { + sendJSON(res, 200, { + self_service: true, + teams_enabled: true, + registries: ['docker.io', 'ghcr.io', 'quay.io', 'localhost:5000'], + images: [ + { + reference: 'docker.io/library/python:3.11-slim', + accelerators: ['cpu'], + }, + { + reference: 'docker.io/library/pytorch-cpu:2.2.0', + accelerators: ['cpu'], + }, + { + reference: 'ghcr.io/utmist/pytorch-cuda:latest', + accelerators: ['nvidia'], + }, + { + reference: 'ghcr.io/utmist/tt-metal-runtime:latest', + accelerators: ['tenstorrent'], + }, + ], + profiles: [ + { + accelerator: 'cpu', + default_image: 'docker.io/library/python:3.11-slim', + device_unit: 'core', + max_devices: 16, + note: 'Standard CPU runtime', + }, + { + accelerator: 'nvidia', + default_image: 'ghcr.io/utmist/pytorch-cuda:latest', + device_unit: 'GPU', + max_devices: 4, + note: 'NVIDIA CUDA 12.4 + PyTorch runtime', + }, + { + accelerator: 'tenstorrent', + default_image: 'ghcr.io/utmist/tt-metal-runtime:latest', + device_unit: 'board', + max_devices: 4, + note: 'Tenstorrent Wormhole n300 PCIe runtime', + }, + ], + }) + return + } + + // ---------------------------------------------------- + // TEAMS ENDPOINTS + // ---------------------------------------------------- + + if (pathname === '/teams' && req.method === 'GET') { + sendJSON(res, 200, { + teams: state.teams, + storage_budget_gib: state.storageBudgetGiB, + }) + return + } + + if (pathname === '/teams' && req.method === 'POST') { + const body = await parseJSONBody(req) + const newTeam = { + id: 'team-' + (body.name || 'unnamed').toLowerCase().replace(/\W+/g, '-'), + name: body.name || 'New Team', + disabled: false, + policy: { + cpu: '8', + memory: '32Gi', + nvidia: 1, + tenstorrent: 2, + concurrent: 2, + queued: 10, + storage_gib: Number(body.storage_gib || 200), + model_storage_gib: Number(body.model_storage_gib || 500), + runtime_seconds: 86400, + registries: ['docker.io', 'ghcr.io'], + }, + members: currentUser + ? [ + { + id: currentUser.id, + name: currentUser.name, + email: currentUser.email, + common_writer: true, + }, + ] + : [], + grants: [], + } + state.teams.push(newTeam) + sendJSON(res, 200, newTeam) + return + } + + const teamMatch = pathname.match(/^\/teams\/([^/]+)$/) + if (teamMatch && req.method === 'PATCH') { + const teamId = teamMatch[1] + const team = state.teams.find((t) => t.id === teamId) + if (!team) return sendError(res, 404, 'Team not found') + const body = await parseJSONBody(req) + if (body.name !== undefined) team.name = body.name + if (body.disabled !== undefined) team.disabled = body.disabled + if (body.policy !== undefined) team.policy = { ...team.policy, ...body.policy } + sendJSON(res, 200, team) + return + } + + const teamMembersMatch = pathname.match(/^\/teams\/([^/]+)\/members$/) + if (teamMembersMatch && req.method === 'POST') { + const teamId = teamMembersMatch[1] + const team = state.teams.find((t) => t.id === teamId) + if (!team) return sendError(res, 404, 'Team not found') + const body = await parseJSONBody(req) + const user = state.users.find((u) => u.id === body.user_id) + if (!user) return sendError(res, 404, 'User not found') + const existing = team.members.find((m) => m.id === user.id) + if (existing) { + existing.common_writer = !!body.common_writer + } else { + team.members.push({ + id: user.id, + name: user.name, + email: user.email, + common_writer: !!body.common_writer, + }) + } + sendJSON(res, 200, team) + return + } + + const teamMemberDeleteMatch = pathname.match(/^\/teams\/([^/]+)\/members\/([^/]+)$/) + if (teamMemberDeleteMatch && req.method === 'DELETE') { + const teamId = teamMemberDeleteMatch[1] + const userId = decodeURIComponent(teamMemberDeleteMatch[2]) + const team = state.teams.find((t) => t.id === teamId) + if (!team) return sendError(res, 404, 'Team not found') + team.members = team.members.filter((m) => m.id !== userId) + sendJSON(res, 200, team) + return + } + + const teamGrantsMatch = pathname.match(/^\/teams\/([^/]+)\/grants$/) + if (teamGrantsMatch && req.method === 'POST') { + const teamId = teamGrantsMatch[1] + const team = state.teams.find((t) => t.id === teamId) + if (!team) return sendError(res, 404, 'Team not found') + const body = await parseJSONBody(req) + const newGrant = { + id: 'grant-' + crypto.randomBytes(3).toString('hex'), + target_team: body.target_team, + scope: body.scope || 'common', + } + team.grants.push(newGrant) + sendJSON(res, 200, team) + return + } + + const teamGrantDeleteMatch = pathname.match(/^\/teams\/([^/]+)\/grants\/([^/]+)$/) + if (teamGrantDeleteMatch && req.method === 'DELETE') { + const teamId = teamGrantDeleteMatch[1] + const grantId = teamGrantDeleteMatch[2] + const team = state.teams.find((t) => t.id === teamId) + if (!team) return sendError(res, 404, 'Team not found') + team.grants = team.grants.filter((g) => g.id !== grantId) + sendJSON(res, 200, team) + return + } + + // ---------------------------------------------------- + // JOBS ENDPOINTS + // ---------------------------------------------------- + + if (pathname === '/jobs' && req.method === 'GET') { + let filtered = state.jobs + if (activeTeam) { + filtered = filtered.filter((j) => j.team_id === activeTeam) + } + sendJSON(res, 200, { + jobs: filtered, + count: filtered.length, + }) + return + } + + if (pathname === '/jobs' && req.method === 'POST') { + const body = await parseJSONBody(req) + const jobId = 'job-' + crypto.randomBytes(4).toString('hex') + const newJob = { + id: jobId, + name: body.name || `Experiment ${jobId}`, + created: new Date().toISOString(), + job_state: 'Scheduled', + accelerator: body.accelerator || 'cpu', + device_count: body.device_count || (body.accelerator === 'cpu' ? 0 : 1), + image: body.image || 'docker.io/library/python:3.11-slim', + cpu: body.cpu || '2', + memory: body.memory || '4Gi', + message: 'Job submitted; awaiting placement', + checkpoint_directory: `/outputs/checkpoints/${jobId}`, + output_directory: `/outputs/runs/${jobId}`, + working_directory: body.working_directory || '/workspace', + timeout_seconds: body.timeout_seconds || 3600, + team_id: activeTeam || 'legacy', + creator_id: currentUser?.id || 'usr_anonymous', + creator_name: currentUser?.name || 'Local Developer', + storage_scope: body.storage_scope || currentUser?.id || 'common', + can_cancel: true, + } + state.jobs.unshift(newJob) + state.jobLogs.set( + jobId, + `[${newJob.created}] Job created: ${newJob.name}\n[${newJob.created}] Image: ${newJob.image}\n[${newJob.created}] Queueing job on Kubernetes admission controller...\n`, + ) + sendJSON(res, 200, { + job_id: jobId, + job: newJob, + }) + return + } + + const jobCancelMatch = pathname.match(/^\/jobs\/([^/]+)\/cancel$/) + if (jobCancelMatch && req.method === 'POST') { + const jobId = decodeURIComponent(jobCancelMatch[1]) + const job = state.jobs.find((j) => j.id === jobId) + if (!job) return sendError(res, 404, 'Job not found') + job.job_state = 'Cancelled' + job.message = 'Cancelled by user' + job.can_cancel = false + const logs = state.jobLogs.get(jobId) || '' + state.jobLogs.set( + jobId, + logs + `[${new Date().toISOString()}] Job cancelled by user.\n`, + ) + sendJSON(res, 200, job) + return + } + + const jobLogsMatch = pathname.match(/^\/jobs\/([^/]+)\/logs$/) + if (jobLogsMatch && req.method === 'GET') { + const jobId = decodeURIComponent(jobLogsMatch[1]) + const logs = state.jobLogs.get(jobId) || 'No logs recorded for this job yet.' + sendJSON(res, 200, { logs }) + return + } + + const jobFilesMatch = pathname.match(/^\/jobs\/([^/]+)\/files$/) + if (jobFilesMatch && req.method === 'GET') { + const jobId = decodeURIComponent(jobFilesMatch[1]) + const files = state.jobFiles.get(jobId) || [] + sendJSON(res, 200, { files }) + return + } + + const jobDownloadMatch = pathname.match(/^\/jobs\/([^/]+)\/files\/download$/) + if (jobDownloadMatch && req.method === 'GET') { + const filePath = query.path || 'output.txt' + res.writeHead(200, { + 'Content-Type': 'text/plain; charset=utf-8', + 'Content-Disposition': `attachment; filename="${filePath.split('/').pop()}"`, + 'Access-Control-Allow-Origin': '*', + }) + res.end(`[Mock file content for ${filePath} from job]`) + return + } + + // ---------------------------------------------------- + // STORAGE & DATASETS ENDPOINTS + // ---------------------------------------------------- + + if (pathname === '/storage' && req.method === 'GET') { + sendJSON(res, 200, { + enabled: true, + capacity_bytes: 1099511627776, // 1 TiB + available_bytes: 879609302220, // 800 GiB + model_capacity_bytes: 2199023255552, // 2 TiB + model_available_bytes: 1759218604441, // 1.6 TiB + dataset_allocation_gib: 500, + model_allocation_gib: 1000, + upload_limit_bytes: 5368709120, // 5 GiB + }) + return + } + + if (pathname === '/storage/folders' && req.method === 'GET') { + const folders = [] + for (const t of state.teams) { + folders.push({ + team_id: t.id, + team_name: t.name, + scope: 'common', + name: 'Common Space', + shared: false, + writable: true, + }) + for (const m of t.members) { + folders.push({ + team_id: t.id, + team_name: t.name, + scope: m.id, + name: `${m.name}'s Folder`, + shared: false, + writable: m.id === currentUser?.id, + }) + } + } + sendJSON(res, 200, { folders }) + return + } + + if (pathname === '/storage/files' && req.method === 'GET') { + if (query.download === 'true') { + const filePath = query.path || 'downloaded-file.dat' + res.writeHead(200, { + 'Content-Type': 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${filePath.split('/').pop()}"`, + 'Access-Control-Allow-Origin': '*', + }) + res.end(`[Mock content of ${filePath} in team ${query.source_team}]`) + return + } + sendJSON(res, 200, { + files: [ + { + path: 'data/train_manifest.json', + size: 15360, + modified: new Date(Date.now() - 7200000).toISOString(), + }, + { + path: 'models/model_final.pt', + size: 89128960, + modified: new Date(Date.now() - 3600000).toISOString(), + }, + ], + }) + return + } + + if (pathname === '/datasets' && req.method === 'GET') { + let filtered = state.datasets + if (activeTeam) { + filtered = filtered.filter((d) => !d.team_id || d.team_id === activeTeam) + } + sendJSON(res, 200, { + datasets: filtered, + upload_limit_bytes: 5368709120, + }) + return + } + + // Dataset upload (streaming / raw binary or multipart) + if (pathname === '/datasets' && req.method === 'POST') { + const filename = query.filename || 'dataset.bin' + const name = query.name || filename + const scope = query.scope || currentUser?.id || 'common' + const activeTeamObj = state.teams.find((t) => t.id === activeTeam) + + let totalBytes = 0 + req.on('data', (chunk) => { + totalBytes += chunk.length + }) + + req.on('end', () => { + const newDs = { + id: 'ds-' + crypto.randomBytes(4).toString('hex'), + name, + filename, + size: totalBytes || 1048576, + files: query.format === 'zip' ? 12 : 1, + sha256: crypto.randomBytes(32).toString('hex'), + created: new Date().toISOString(), + state: 'ready', + team_id: activeTeam || 'legacy', + team_name: activeTeamObj?.name || 'Legacy Workspace', + scope, + member_name: currentUser?.name || 'Local Pilot', + shared: false, + } + state.datasets.unshift(newDs) + sendJSON(res, 200, newDs) + }) + return + } + + const datasetDeleteMatch = pathname.match(/^\/datasets\/([^/]+)$/) + if (datasetDeleteMatch && req.method === 'DELETE') { + const datasetId = datasetDeleteMatch[1] + state.datasets = state.datasets.filter((d) => d.id !== datasetId) + sendJSON(res, 200, { success: true }) + return + } + + const datasetFilesMatch = pathname.match(/^\/datasets\/([^/]+)\/files$/) + if (datasetFilesMatch && req.method === 'GET') { + sendJSON(res, 200, { + files: [ + { + path: 'metadata.json', + size: 2048, + modified: new Date().toISOString(), + }, + { + path: 'samples/sample_001.png', + size: 45056, + modified: new Date().toISOString(), + }, + ], + }) + return + } + + const datasetDownloadMatch = pathname.match(/^\/datasets\/([^/]+)\/files\/download$/) + if (datasetDownloadMatch && req.method === 'GET') { + const filePath = query.path || 'dataset-file.dat' + res.writeHead(200, { + 'Content-Type': 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${filePath.split('/').pop()}"`, + 'Access-Control-Allow-Origin': '*', + }) + res.end(`[Mock content of dataset file ${filePath}]`) + return + } + + // Health check + if (pathname === '/healthz' && req.method === 'GET') { + sendJSON(res, 200, { status: 'ok', executor: 'fake-backend' }) + return + } + + // 404 Fallback + sendError(res, 404, `Endpoint not found: ${req.method} ${pathname}`) + } catch (err) { + console.error('[Fake Backend Error]', err) + sendError(res, 500, 'Internal Server Error: ' + err.message) + } +}) + +server.listen(PORT, HOST, () => { + console.log(`======================================================`) + console.log(`Mist Fake Backend running at http://${HOST}:${PORT}`) + console.log(`Ready for web-interface testing!`) + console.log(`- Simulated User: ${state.users[state.currentUserIndex]?.name} (${state.users[state.currentUserIndex]?.role})`) + console.log(`- Teams: ${state.teams.map((t) => t.name).join(', ')}`) + console.log(`- Jobs: ${state.jobs.length} pre-populated`) + console.log(`======================================================`) +}) + +process.on('SIGINT', () => { + server.close(() => { + console.log('\nFake backend stopped.') + process.exit(0) + }) +}) diff --git a/web-interface/package.json b/web-interface/package.json index dbb88b7..5200d90 100644 --- a/web-interface/package.json +++ b/web-interface/package.json @@ -14,6 +14,7 @@ "preview": "vite preview", "test": "vitest run", "lint": "eslint", + "fake-backend": "node ../scripts/fake-backend.mjs", "format": "prettier --check .", "check": "prettier --write . && eslint --fix" }, diff --git a/web-interface/pnpm-lock.yaml b/web-interface/pnpm-lock.yaml new file mode 100644 index 0000000..08effeb --- /dev/null +++ b/web-interface/pnpm-lock.yaml @@ -0,0 +1,4599 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tailwindcss/vite': + specifier: ^4.1.18 + version: 4.3.3(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + '@tanstack/react-devtools': + specifier: latest + version: 0.10.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(csstype@3.2.3)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(solid-js@1.9.17) + '@tanstack/react-router': + specifier: latest + version: 1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@tanstack/react-router-devtools': + specifier: latest + version: 1.167.2(@tanstack/react-router@1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(@tanstack/router-core@1.171.38)(csstype@3.2.3)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@tanstack/router-plugin': + specifier: ^1.132.0 + version: 1.168.47(@tanstack/react-router@1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(esbuild@0.28.2)(rollup@4.64.7)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + better-auth: + specifier: 1.7.7 + version: 1.7.7(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(solid-js@1.9.17)(vitest@5.0.5(@types/node@22.20.5)(jsdom@28.1.0(@noble/hashes@2.4.0))(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))) + lucide-react: + specifier: ^0.545.0 + version: 0.545.0(react@19.3.1) + react: + specifier: ^19.2.0 + version: 19.3.1 + react-dom: + specifier: ^19.2.0 + version: 19.3.1(react@19.3.1) + tailwindcss: + specifier: ^4.1.18 + version: 4.3.3 + devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.16 + version: 0.5.20(tailwindcss@4.3.3) + '@tanstack/devtools-vite': + specifier: latest + version: 0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + '@tanstack/eslint-config': + specifier: latest + version: 0.4.0(@typescript-eslint/utils@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + '@testing-library/dom': + specifier: ^10.4.1 + version: 10.4.2 + '@testing-library/react': + specifier: ^16.3.0 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@types/node': + specifier: ^22.10.2 + version: 22.20.5 + '@types/react': + specifier: ^19.2.0 + version: 19.3.0 + '@types/react-dom': + specifier: ^19.2.0 + version: 19.3.0(@types/react@19.3.0) + '@vitejs/plugin-react': + specifier: ^5.1.4 + version: 5.2.0(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + jsdom: + specifier: ^28.1.0 + version: 28.1.0(@noble/hashes@2.4.0) + prettier: + specifier: ^3.8.1 + version: 3.9.10 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.3)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + vitest: + specifier: ^5.0.3 + version: 5.0.5(@types/node@22.20.5)(jsdom@28.1.0(@noble/hashes@2.4.0))(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + +packages: + + '@acemir/cssom@0.9.32': + resolution: {integrity: sha512-OijrAUu7rrJhSIoHNg6LMPS5e344nwUHA+1EFYYjrh64D+GmytUsfBQjci45TjT+rd5Spsp8B5xsZDT+TgyXXQ==} + + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@6.8.1': + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.10': + resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.10': + resolution: {integrity: sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.10': + resolution: {integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@better-auth/core@1.7.7': + resolution: {integrity: sha512-srgFzHEjB2WWlokFz1i4BfG41sNhsA105Pukgqu6RrEh5A+LSkzf1mCHydVipkjt9l5qswtBMi7IL6ouHbZL4Q==} + peerDependencies: + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + '@opentelemetry/api': ^1.9.0 + better-call: 1.4.0 + jose: ^6.1.0 + kysely: ^0.28.5 || ^0.29.0 + nanostores: ^1.0.1 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@better-auth/drizzle-adapter@1.7.7': + resolution: {integrity: sha512-qon0U94PR5FQysuyGoAlGVQpIIiNg5dtDpwjE63sN/HjoRWZadZzHPcS9mn7JsJyQZK6iTNhd7LD0AaXt2f78w==} + peerDependencies: + '@better-auth/core': ^1.7.7 + '@better-auth/utils': 0.4.2 + drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 + peerDependenciesMeta: + drizzle-orm: + optional: true + + '@better-auth/kysely-adapter@1.7.7': + resolution: {integrity: sha512-9FONOCgOcrQI9wJ5v1oDGIg9sT7pa9RjZZQ4fsCTudo8R87SrIGNUvUxDFTJhZvUblvFfGaIBR9Vb8LA+8bqLQ==} + peerDependencies: + '@better-auth/core': ^1.7.7 + '@better-auth/utils': 0.4.2 + kysely: ^0.28.17 || ^0.29.0 + peerDependenciesMeta: + kysely: + optional: true + + '@better-auth/memory-adapter@1.7.7': + resolution: {integrity: sha512-FqKFEe+b5tXXV0gRbyirca+qXgMpLOeJ8Wk19yykb/scQKy0WJ4k22WzEV3TTPctQV9DZhwCXPlDtQhnlOv5Gg==} + peerDependencies: + '@better-auth/core': ^1.7.7 + '@better-auth/utils': 0.4.2 + + '@better-auth/mongo-adapter@1.7.7': + resolution: {integrity: sha512-ZnVDuRrXqbf0oHphrEN27oTImNH5NTO26dtWatq5cXyYAsH7goof9QIoTOmxTbBxUQahKBw9T6+9h6n+RtOE+g==} + peerDependencies: + '@better-auth/core': ^1.7.7 + '@better-auth/utils': 0.4.2 + mongodb: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + mongodb: + optional: true + + '@better-auth/prisma-adapter@1.7.7': + resolution: {integrity: sha512-4YcnrcVdvWiAZw0sZl63tWUClPXg5r3QsLn7C2sG7kKEBY06zOng0ibecyxRBixKq1zE1ceHuEUqez5Rhi+nQA==} + peerDependencies: + '@better-auth/core': ^1.7.7 + '@better-auth/utils': 0.4.2 + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + '@prisma/client': + optional: true + prisma: + optional: true + + '@better-auth/telemetry@1.7.7': + resolution: {integrity: sha512-PaRA6i+kAioVYmza2gHPcdrxP0hAotph4KV9hft2GeNJq+AZXDiBu3FuLM9HxvcmiZeUT74Vufqd9ypmB8veTA==} + peerDependencies: + '@better-auth/core': ^1.7.7 + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + + '@better-auth/utils@0.4.2': + resolution: {integrity: sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==} + + '@better-auth/utils@0.5.0': + resolution: {integrity: sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==} + + '@better-fetch/fetch@1.3.2': + resolution: {integrity: sha512-Gs7n99b5tqUC6cQAPbV0uED3IraHB6xQbHLQ/C3l7ZFafHScOx9pQ+DYmP5blbLFShVWLqxNUlI9wi4xU/X+ow==} + + '@bramus/specificity@2.4.3': + resolution: {integrity: sha512-0GEz8qJWokkUVYQYRukmkSHIFNoBH8QTreeHrAFbvz1XFYc3Y+puhMs8PQ5x8GNugiC2l5jvDBXKLbBIByeSBw==} + hasBin: true + + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + + '@csstools/color-helpers@6.1.2': + resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.4.3': + resolution: {integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.2 + '@csstools/css-tokenizer': ^4.0.2 + + '@csstools/css-color-parser@4.2.6': + resolution: {integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.2 + '@csstools/css-tokenizer': ^4.0.2 + + '@csstools/css-parser-algorithms@4.0.2': + resolution: {integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.2 + + '@csstools/css-syntax-patches-for-csstree@1.1.15': + resolution: {integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.2': + resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==} + engines: {node: '>=20.19.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.7.0': + resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.3': + resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@exodus/bytes@1.16.0': + resolution: {integrity: sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + + '@napi-rs/wasm-runtime@1.2.5': + resolution: {integrity: sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@neodrag/core@3.0.0-next.11': + resolution: {integrity: sha512-3WQWxyrbxiaK9zS5JU2wJsW2gpoQlZBXVghduBh61JpqaeE0T0cte8R0qYK2RuJo3J2TYQYqxO19CpG/C1i5eg==} + + '@neodrag/solid@3.0.0-next.11': + resolution: {integrity: sha512-vCBIn/pimjWMQ6vhTS2/O1XNAwzVtc4eUhdbQ91WykbZWWqQ5NocDXt/1OdYrEkeRzJcpCv8wEz5PnMkgKP81Q==} + peerDependencies: + '@neodrag/core': 3.0.0-next.11 + solid-js: ^1.0.0 + + '@noble/ciphers@2.4.0': + resolution: {integrity: sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw==} + engines: {node: '>= 20.19.0'} + + '@noble/hashes@2.4.0': + resolution: {integrity: sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==} + engines: {node: '>= 20.19.0'} + + '@opentelemetry/semantic-conventions@1.43.0': + resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} + engines: {node: '>=14'} + + '@oxc-parser/binding-android-arm-eabi@0.120.0': + resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.120.0': + resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.120.0': + resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.120.0': + resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.120.0': + resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.120.0': + resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-openharmony-arm64@0.120.0': + resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.120.0': + resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.120.0': + resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + + '@rolldown/pluginutils@1.0.0-rc.3': + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + + '@rollup/rollup-android-arm-eabi@4.64.7': + resolution: {integrity: sha512-GFF2tHk0NqYVObrK0+/ps/LEXsV/v2q6Lyiy7mZr+Ej24fpQScvqK2ziz9NHeEQYnKWKym38Ml7dZbPtC9tuAw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.64.7': + resolution: {integrity: sha512-BOymcXJFYYXqluDidj6sLelNjXruC3u1BqVxLn2OPCXH9xaPSOTH+MS4uychsGI1NU7tJoiv9KvqijDHFe+hhQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.64.7': + resolution: {integrity: sha512-jgVpGweIbls2H5jfBB70tnX52abMd5pJr3xmtoDILx6JdmGl+Un+tMb9R+MbHPVB46tTBpY8emtn7M71SnQhEQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.64.7': + resolution: {integrity: sha512-AVMpt94mrXH7WjyWKmd9nT4RC5yM0AN3Lt6A89xVIsuLDi3dDX6dxMytLbW36FlppY9JsoqUoS3LUUpUrYxiMQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.64.7': + resolution: {integrity: sha512-Nje29c/MyajxRNFGU05oyeTDS6SWdMg/wWMSlZSBTBju5BKz2NVc0kFiFSQGzRELNiB+CBk/ZvAVsWsENgjGag==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.64.7': + resolution: {integrity: sha512-xxgN5/kMzCNz/NYJ4t49B0/WS6InNlWv6xvgwUQqNnWM9WJMMwMUGWrAxdNEgYWdHL0sMnuh8I87StGrIMTA+w==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.64.7': + resolution: {integrity: sha512-5U/9gVn/X657I8+NHoHO9Wnfyd0lEXwqbslobRoB9TC8SwYzOitcIr6TLE7531Q/NWs3L4mb+YOGzlf3vHIeYw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.64.7': + resolution: {integrity: sha512-hcDS0a/Y8ggM3dcOfzH7UpSaogCViWTtNDSl0EXpWIj8g8/r86ITqptjil2hayTXS0Umzb+Yt3mjgpQVFVf2+A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.64.7': + resolution: {integrity: sha512-awbAr4N7Qd/yjZAQBEEPzgcPveYG2Gi5L6gq1/Bp+3SMRjn5F68Wv1zKsntzvvoioBR6yKvhLtWqu11am1TsxA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.64.7': + resolution: {integrity: sha512-Ut7xQJfeLhzu2yN3rHzVvL9ccyPJvN4aCdP1V7hJ5lGxX/eCzgB9SiMk/Md3J+rWWZgvoYsDW4BUFggJr32pBA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.64.7': + resolution: {integrity: sha512-trqoni/vHkuM5oo5iQhoKPh6fjjD7E80pMgCXWgGxU8vSRPUlwvG8GeYvEEcB6eJAxNa99WH9iVHPHelgtKiHA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.64.7': + resolution: {integrity: sha512-gvlUy0XFIwtUWiQmhc+4GT2Knm3KosinpUVhr7CtTFA78gFDnE5NWwMPzHrJsYpKkh2LlY7yvBIboN1FAV6Xeg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.64.7': + resolution: {integrity: sha512-8yS/kSXTJ/47Z62JW+C2AJ3vd/8zZw016vFjtNpZwmZlwJumLua4s1i78dkYuSSqQ+0aIMxJcBp/4uIwp2ehjA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.64.7': + resolution: {integrity: sha512-mxVSKvvty0hOP1AFo/l4Kpv3i3HK0zx4rYUrirZDToYlWhS3xzmIkq3vUJ7rtrM2cPyXveDyDT6M1FfXZ9hTRA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.64.7': + resolution: {integrity: sha512-lEse1zra1CagM+uHH2bl28GInDsZZ45NJzHei2OMaXQFzZWKBO1hFo07rS0xB6ayV4WU360UrSmPDSyCMpYsUQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.64.7': + resolution: {integrity: sha512-d/cqCoiG9WMZ5VuJZ5Qdlv9g97QyoGwnmvHccvGhKSacTy7xFrAS+jMcJSBdMNOw1oacSkl6p7K6O/DRYTKL3g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.64.7': + resolution: {integrity: sha512-kVEA//CeDOZZP1XCH2JBgqXb9+ALlwuA/4civaazMVdgzaSQC9WM38w65zG/LLk0lA7F9Od19CSBZW7J9mCbNg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.64.7': + resolution: {integrity: sha512-14oLfDsOSr0TSntT/34jRg/V8B2XW/Jomh9zX9CjIM9gwwNlI1OCymS/GVn0DV32h67eRf7U0GGem0rvfXgNwQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.64.7': + resolution: {integrity: sha512-dFVtPfjZqAnUPpPY4Bfg+87yo1IBW9HiY8iCFtg49Pn46MfZTebSHZh8PL6SOsgyt9LQP8Nj/sE6jm3aWpySmQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.64.7': + resolution: {integrity: sha512-Dm9CZ2SM3u2NgvBj4awW/gGqkOwzH8f6MSYIJE6+bKcKLI+T0+LhpD6vtetTfrIIx+rLXDf5RJqBP6v7V5O+wg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.64.7': + resolution: {integrity: sha512-oaxsvS2mL6+78741CT2HCORBvy1USg3Wqpt5qvZwy3aMXRMl4zxyDo8maeFRWJkF3dp0Wbi71fKH5Gr9YI3vYA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.64.7': + resolution: {integrity: sha512-UKZz1Y4mxUqkWh60uH7B1aZn6UcAimPk4ipRktpb3wfZq7JrzZYyAXTwiafkb3T+kd5P3Sf+vzcRdsDks4WtjQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.64.7': + resolution: {integrity: sha512-WWkSuC1J+mZJnsDh0mRaCAFSWfQdl6y3fDjZeWElI6zHo8CcOSG5lY3yurKJjEq+FrX0WD60sFrVVpbHqzMmrw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.64.7': + resolution: {integrity: sha512-u9bp2yXCI3/BUjUxY6uWknAY32pfvvDCPysyUTySotx0dj3ydlFHrRQYfn/b/kZgnfjyEwySMtI7Ga054uiysQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.64.7': + resolution: {integrity: sha512-EhX6N3XPMEnVU5cRfNgzPOKcSucsbu3nNNWuoAaEuKIqmddBKHN5b9d4rPe65T1I+FaOT176JKiZNsUBp3rqbA==} + cpu: [x64] + os: [win32] + + '@solid-primitives/event-listener@2.4.6': + resolution: {integrity: sha512-5I0YJcTVYIWoMmgBSROBZGcz+ymhew/pGTg2dHW74BUjFKsV8Li4bOZYl0YAGP4mHw5o4UBd9/BEesqBci3wxw==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/keyboard@1.3.7': + resolution: {integrity: sha512-558RPNYnXx4nGh537DSqAn4xMrC8iFipl/5+xzgzWoTNFst4RnUN3BOLmtDjJ0UGGoQXVMALYR3bNOHM0xnt1Q==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/resize-observer@2.2.0': + resolution: {integrity: sha512-9Fuu/EWBeGj+atGHRJp70HKhdfalmpjwxY8a32NZixdLNmfCJ45AfhLQNr6uOzETbbiMx4iCKlTrJ8KZCHC2Ww==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/rootless@1.5.4': + resolution: {integrity: sha512-TOIZa1VUfVJ+9nkCcRajw3U4t9vBOP1HxX1WHNTbXq32mXwlqTvUnC4CRIilohcryBkT9u2ZkhUDSHRTaGp55g==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/static-store@0.1.4': + resolution: {integrity: sha512-LgtVaVBtB7EbmS4+M0b8xY5Iq6pUWXBsIC4VgtrFKDGDdyCaDt88sHk0fUlx1Enxm/XZnZyLXJABRoa39RjJqA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/utils@6.4.1': + resolution: {integrity: sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg==} + peerDependencies: + solid-js: ^1.6.12 + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@stylistic/eslint-plugin@5.10.0': + resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} + peerDependencies: + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' + + '@tailwindcss/vite@4.3.3': + resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/devtools-bundler-core@0.1.3': + resolution: {integrity: sha512-F0tlxIyfFqXkZ1mJP1EjtkiSeJA+ztXY2AYOHf7r4goCIEAOp86N9PFJ/yv8vu1TnmxGS6vKsZCS3Kyls9xQQA==} + engines: {node: '>=18'} + + '@tanstack/devtools-client@0.0.8': + resolution: {integrity: sha512-cG3iZkGWCwN330bLBKa8+9r4Of2AXNoz2zUqcsy/4XsD3105ghVBx78cGyvJj9fSclNomPxoqAnDGXXhg1WLvA==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-bus@0.4.3': + resolution: {integrity: sha512-NeegBt5/n2E5q4DbrXHqECBq42+kDi6JBOp8/+RNqkIE+P4hJpbM36kGyjnGQFMWOoku31qhMyX9/48VuTTdmg==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-client@0.5.0': + resolution: {integrity: sha512-H+OH3zC6Vhu/K0NaVfQKknEKawc/+2PT+D3SB3Ox0V8SiMlTo0abbmH2rH0721R2aNYbjdMXA1oENOd8E2UVoA==} + engines: {node: '>=18'} + hasBin: true + + '@tanstack/devtools-ui@0.7.1': + resolution: {integrity: sha512-3xQ/ezZ2qVNszhjpCN2N3jn7uHc2J1PMgcyjHzH4XZBt9xAQyMMcPNoR2cd7rzReyxWoJpZUWiDBmOJiCtLj9A==} + engines: {node: '>=18'} + peerDependencies: + solid-js: '>=1.9.7' + + '@tanstack/devtools-vite@0.8.5': + resolution: {integrity: sha512-xaifCEmiwwzizlbp973oISXNsnmuU/BugLa66gryAZaPJJ/qo1kJd2DxY5X960eHwmyIS3SN0KYrL3/aRhucmA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@tanstack/devtools@0.15.0': + resolution: {integrity: sha512-KyS7Qi82Eq9yG8C+uae3ikAPb+D5rfzVhvOOjXVAHZ/64s6TWJlDWpREw1FrHIYZaI7paErpsExSSKlF5BKA/A==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + solid-js: '>=1.9.7' + + '@tanstack/eslint-config@0.4.0': + resolution: {integrity: sha512-V+Cd81W/f65dqKJKpytbwTGx9R+IwxKAHsG/uJ3nSLYEh36hlAr54lRpstUhggQB8nf/cP733cIw8DuD2dzQUg==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + + '@tanstack/history@1.162.5': + resolution: {integrity: sha512-w1+YvwmTkwt5n8yUqEH3IWZ9DsmorCZW/EQ3KYHnqEgIkOZpH54NvW6/Epq3Dzb8H0ifIeYX8PExvrNLIH5FIA==} + engines: {node: '>=20.19'} + + '@tanstack/react-devtools@0.10.13': + resolution: {integrity: sha512-r0cdB/14+st2Vxl/1Bodlyq4VC8pf9SykLQDYbGHDc1+V146WQo5B3KlYLkfyxD9Q/6+9NPRQHZyl8fvZVMdRA==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=16.8' + '@types/react-dom': '>=16.8' + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/react-router-devtools@1.167.2': + resolution: {integrity: sha512-VuoqB7OhDUrBa4WOFSKiAQ8xI8sJpeZdo9sYE1hQa89iMuVFUQu1iWE4iCiQhB+6TYOZDZ9e+iHKxYjop4ZVYw==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/react-router': ^1.170.36 + '@tanstack/router-core': ^1.171.30 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + peerDependenciesMeta: + '@tanstack/router-core': + optional: true + + '@tanstack/react-router@1.170.45': + resolution: {integrity: sha512-6tuiIAFb82zsk/YxveJRyGjbsm6kIfgUuoH7qCp2R2aSHveW6W0t/6j+Sn4m1iIlA2lLU4cvooqks08/YMOThw==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.11.2': + resolution: {integrity: sha512-oj5O5DmiMWE+ih+SioX2ayC1kbK7d3fpLx0B75fPJr9nY3zU+hoW010yr64qXsY/9fyiLVE2bn5mGNV3owm8RQ==} + 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 + + '@tanstack/router-core@1.171.38': + resolution: {integrity: sha512-XRZVxiO6BIexICmEo3XfPmpoyyLhbjgaMJsONz4gIf5oYys0UiaG1BJGRyj9x8QZPDZcBhvAMuTYN70wSxV0gA==} + engines: {node: '>=20.19'} + + '@tanstack/router-devtools-core@1.168.2': + resolution: {integrity: sha512-pCCdQ5M/NUeWdiAbPmjyfcNdls6G4vaVlIQbPf1IqZk1FLYFr00aQvi578yUuWFBNM2KNS2QcwM1uPucbtGf5Q==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/router-core': ^1.171.30 + csstype: ^3.0.10 + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.167.45': + resolution: {integrity: sha512-oCtNqtsMNcBJkPIOu1O7a68jnxt4TkVJj3jHrW3VgiZdBeaj/lBJQh50yvzmNRswRcyIkRg0g30g25bbLRrAGw==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.47': + resolution: {integrity: sha512-1qvMto2Id0Uulux4qd+WttCbOFloIEQ5x82m68T4knt6NmUDQC66P/u/5yzN0FWGSO0u2U2nbDSIQoR6g7HNmQ==} + engines: {node: '>=20.19'} + 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 + + '@tanstack/router-utils@1.162.4': + resolution: {integrity: sha512-3TSH3rwAQTqQOtTBF8IXMFXmGNJWnjwIm+l87uguyC/of0pSRKcU9GJrozaf0fY9it8dLQvUZJA2MOlfqbW1sA==} + engines: {node: '>=20.19'} + + '@tanstack/store@0.11.2': + resolution: {integrity: sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + + '@testing-library/dom@10.4.2': + resolution: {integrity: sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.3': + resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.20.5': + resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + + '@typescript-eslint/eslint-plugin@8.71.2': + resolution: {integrity: sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.71.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.71.2': + resolution: {integrity: sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.71.2': + resolution: {integrity: sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.71.2': + resolution: {integrity: sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.71.2': + resolution: {integrity: sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.71.2': + resolution: {integrity: sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.71.2': + resolution: {integrity: sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.71.2': + resolution: {integrity: sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.71.2': + resolution: {integrity: sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.71.2': + resolution: {integrity: sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + '@vitejs/plugin-react@5.2.0': + resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@vitest/mocker@5.0.5': + resolution: {integrity: sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==} + peerDependencies: + msw: ^2.4.9 || ^3.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/spy@5.0.5': + resolution: {integrity: sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansis@4.4.0: + resolution: {integrity: sha512-9k3v7xcHwgdO/DruxGIg4HtjvlAZlcnsX/mzqUb1t3NkYnl9kK2UJ+Gq0io+vQf7iT//BD/HB/NBkUR1LWxoeA==} + engines: {node: '>=14'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.29: + resolution: {integrity: sha512-YeesxhUmR4h5y5V8KF8iBCqJb8YFrBJ++XBTeGcWIaQOYXGN19JM0FoiOjAhwuDoln8owMgXcWBmgHtYSkky2w==} + engines: {node: '>=6.0.0'} + hasBin: true + + better-auth@1.7.7: + resolution: {integrity: sha512-Zhgxi/c5ylmfB/sX+nwnBbsFf/j6k0L8DvL6CJXZSrXgxc5pPMC/e7j812zNd4L4B4ELmdURCPw3X8nAOUdcjw==} + peerDependencies: + '@lynx-js/react': '*' + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + '@sveltejs/kit': ^2.0.0 + '@tanstack/react-start': ^1.0.0 + '@tanstack/solid-start': ^1.0.0 + drizzle-kit: '>=0.31.4 || >=1.0.0-beta.1' + drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@lynx-js/react': + optional: true + '@prisma/client': + optional: true + '@sveltejs/kit': + optional: true + '@tanstack/react-start': + optional: true + '@tanstack/solid-start': + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + + better-call@1.4.0: + resolution: {integrity: sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA==} + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + bidi-js@1.1.0: + resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + browserslist@4.29.3: + resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} + + caniuse-lite@1.0.30001816: + resolution: {integrity: sha512-+8Yx7BXCoqk6IF8yTicjfUR6D/ldJzdlsodlEOiOSPmhK844WIgk67/zIQaGAOxNkZyyvBlrRFnLvqKIoqbAFw==} + + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} + engines: {node: '>=18'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + comment-parser@1.4.9: + resolution: {integrity: sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==} + engines: {node: '>= 12.0.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssstyle@6.2.0: + resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} + engines: {node: '>=20'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + dayjs@1.11.23: + resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} + engines: {node: '>=0.3.1'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + electron-to-chromium@1.5.458: + resolution: {integrity: sha512-/hIH2gRSmxrf4/X4Nw/t/Wc8PVS22l8/3HnWjWTWsTOM5dsnMUPZF/+u+yr3LqweNuTBJE1rq6qAQzKkZVwMfA==} + + enhanced-resolve@5.26.0: + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} + engines: {node: '>=10.13.0'} + + entities@8.1.0: + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} + engines: {node: '>=20.19.0'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-import-context@0.1.9: + resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-import-x@4.17.1: + resolution: {integrity: sha512-4cdstYkKCyjumM2Q9NSI03K8D2a9F4Ssz33K2lv2hQa4KmR9jPLwk3uWGtNvclfqBrPGfGuMBwsGMbe6dMRbfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/utils': ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint-import-resolver-node: '*' + peerDependenciesMeta: + '@typescript-eslint/utils': + optional: true + eslint-import-resolver-node: + optional: true + + eslint-plugin-n@17.24.0: + resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.23.0' + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.13.0: + resolution: {integrity: sha512-UohDjD4zSzEwhf58M3O7vc/ycc8O1I7dNlhtD0WxE8Px8EHNJKtUwzBqrzeGhisRuv2ag4sAYswszCJRgKXYUg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@17.13.0: + resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==} + engines: {node: '>=18'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + goober@2.1.19: + resolution: {integrity: sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==} + peerDependencies: + csstype: ^3.0.10 + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.12: + resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + isbot@5.2.2: + resolution: {integrity: sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + jose@6.2.12: + resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsdom@28.1.0: + resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + kysely@0.29.6: + resolution: {integrity: sha512-hHaB8C/rfzDDtr/t8YZwxAuPJTT0zHyaPoVzcXwDYhYNAgH/4sIfVhi/XLLIY+bL/FqaIJnjATDbi8ObSELmxg==} + engines: {node: '>=22.0.0'} + + launch-editor@2.14.2: + resolution: {integrity: sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.545.0: + resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magic-string@1.4.3: + resolution: {integrity: sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanostores@1.5.5: + resolution: {integrity: sha512-FixtE239Gl6Gz0ZGCK0WzvNwObNCIwVT+L7zQcf1QgcA6AzBxblzqe9OT88qiucNkhsjSOqiogSaF5jVbJiqSA==} + engines: {node: ^20.0.0 || >=22.0.0} + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.59: + resolution: {integrity: sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==} + engines: {node: '>=18'} + + obug@3.0.0: + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + engines: {node: '>=12.20.0'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + oxc-parser@0.120.0: + resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} + engines: {node: ^20.19.0 || >=22.12.0} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss@8.5.30: + resolution: {integrity: sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.9.10: + resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + + react-dom@19.3.1: + resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==} + peerDependencies: + react: ^19.3.1 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react@19.3.1: + resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} + engines: {node: '>=0.10.0'} + + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + rollup@4.64.7: + resolution: {integrity: sha512-aCiJONoZp0lNGJ6sYqspLkh6UXaWEey/7Ko2TZyXQ4kgzAhDUxAgbt/oulXKmD+zFHXw1k+RF6ZZWOrjp7DZnQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rou3@0.9.2: + resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + seroval-plugins@1.6.9: + resolution: {integrity: sha512-kiDK/Tb8IWZ61WEU8pA2PDmAuw37gKph9Y77OZlD56mg0Y2g0Bg7gZ1HeHhs5d40w+nRoUq/wj08V26ilbez9g==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.6.9: + resolution: {integrity: sha512-UeEzHwAK+yl0YLbsvROV1t5yMYEwZZ0CfKYxDvUgS2fO9Vxdf8djqVIB+OuVpuawRNPqw68LE1Du/GMsW2ZPqA==} + engines: {node: '>=10'} + + set-cookie-parser@3.1.4: + resolution: {integrity: sha512-iEmdEew+Tn1BwMGMP8LzGBEPC7Mf1sySjglCeEPb/Vchm9bnGezePvacYcId+fGprQzIng+TBkzqfJ/qS7AsBg==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.12.0: + resolution: {integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==} + engines: {node: '>= 0.4'} + + solid-js@1.9.17: + resolution: {integrity: sha512-4GAmLOWPfNV8H9Jfn2LSFTpNEG6jiOKPiwd5lnLGQtehHaX6l3TOLzJqQ7mqwB/3k1Yy3zyumFVopmc/tm34ow==} + + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + + stable-hash-x@0.2.0: + resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} + engines: {node: '>=12.0.0'} + + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinybench@6.2.2: + resolution: {integrity: sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==} + engines: {node: '>=20.0.0'} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tldts-core@7.4.18: + resolution: {integrity: sha512-KI2PKvCmvQI/xD6ypPqkU0i7oxNXR/O58RLRaHR4svsgVUQ2JE/XC8BtMbG6PSehD27I8sZJRcC1ASnCy6lp3g==} + + tldts@7.4.18: + resolution: {integrity: sha512-somxA8xKAUzlAxl0TX/jMSYIbRNFIVGnI96pVGJPeMc5BsHmfVXn6mOJT6U++ELnl04eGfzpfcEuZe8WiASkXg==} + hasBin: true + + tough-cookie@6.0.2: + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} + peerDependencies: + typescript: '>=4.0.0' + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + deprecated: unmaintained + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.71.2: + resolution: {integrity: sha512-Rz4effbMy2g9wnvOJZG6SbVavX313Lap1vNEbk1RUochzickvzP8P4HB+bkNg5e50eVz1hQGjh9K1omI1xB4Bw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@7.30.1: + resolution: {integrity: sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==} + engines: {node: '>=20.18.1'} + + unplugin@3.4.0: + resolution: {integrity: sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==} + 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 + + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + + update-browserslist-db@1.3.4: + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.7.0: + resolution: {integrity: sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@7.3.7: + resolution: {integrity: sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.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 + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@5.0.5: + resolution: {integrity: sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.5 + '@vitest/browser-preview': 5.0.5 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.5 + '@vitest/coverage-v8': 5.0.5 + '@vitest/ui': 5.0.5 + happy-dom: '*' + jsdom: '*' + vite: ^6.4.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 + + vue-eslint-parser@10.4.1: + resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + ws@8.22.0: + resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} + +snapshots: + + '@acemir/cssom@0.9.32': {} + + '@asamuzakjp/css-color@5.1.11': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) + '@csstools/css-tokenizer': 4.0.2 + + '@asamuzakjp/dom-selector@6.8.1': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.1.0 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.3 + + '@asamuzakjp/generational-cache@1.0.1': {} + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.10 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.10 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.10 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.10': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/runtime@7.29.10': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.10': + 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.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5)': + dependencies: + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + '@opentelemetry/semantic-conventions': 1.43.0 + '@standard-schema/spec': 1.1.0 + better-call: 1.4.0(zod@4.6.7) + jose: 6.2.12 + kysely: 0.29.6 + nanostores: 1.5.5 + zod: 4.6.7 + + '@better-auth/drizzle-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/utils': 0.4.2 + + '@better-auth/kysely-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(kysely@0.29.6)': + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/utils': 0.4.2 + optionalDependencies: + kysely: 0.29.6 + + '@better-auth/memory-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/utils': 0.4.2 + + '@better-auth/mongo-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/utils': 0.4.2 + + '@better-auth/prisma-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/utils': 0.4.2 + + '@better-auth/telemetry@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)': + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + + '@better-auth/utils@0.4.2': + dependencies: + '@noble/hashes': 2.4.0 + + '@better-auth/utils@0.5.0': + dependencies: + '@noble/hashes': 2.4.0 + + '@better-fetch/fetch@1.3.2': {} + + '@bramus/specificity@2.4.3': + dependencies: + css-tree: 3.2.1 + + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + + '@csstools/color-helpers@6.1.2': {} + + '@csstools/css-calc@3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) + '@csstools/css-tokenizer': 4.0.2 + + '@csstools/css-color-parser@4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': + dependencies: + '@csstools/color-helpers': 6.1.2 + '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) + '@csstools/css-tokenizer': 4.0.2 + + '@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2)': + dependencies: + '@csstools/css-tokenizer': 4.0.2 + + '@csstools/css-syntax-patches-for-csstree@1.1.15(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.2': {} + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@10.13.0(jiti@2.7.0))': + dependencies: + eslint: 10.13.0(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.6 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.7.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.13.0(jiti@2.7.0))': + optionalDependencies: + eslint: 10.13.0(jiti@2.7.0) + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.3': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@exodus/bytes@1.16.0(@noble/hashes@2.4.0)': + optionalDependencies: + '@noble/hashes': 2.4.0 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@napi-rs/wasm-runtime@1.2.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.4 + optional: true + + '@neodrag/core@3.0.0-next.11': {} + + '@neodrag/solid@3.0.0-next.11(@neodrag/core@3.0.0-next.11)(solid-js@1.9.17)': + dependencies: + '@neodrag/core': 3.0.0-next.11 + solid-js: 1.9.17 + + '@noble/ciphers@2.4.0': {} + + '@noble/hashes@2.4.0': {} + + '@opentelemetry/semantic-conventions@1.43.0': {} + + '@oxc-parser/binding-android-arm-eabi@0.120.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.120.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + optional: true + + '@oxc-project/types@0.120.0': {} + + '@rolldown/pluginutils@1.0.0-rc.3': {} + + '@rollup/rollup-android-arm-eabi@4.64.7': + optional: true + + '@rollup/rollup-android-arm64@4.64.7': + optional: true + + '@rollup/rollup-darwin-arm64@4.64.7': + optional: true + + '@rollup/rollup-darwin-x64@4.64.7': + optional: true + + '@rollup/rollup-freebsd-arm64@4.64.7': + optional: true + + '@rollup/rollup-freebsd-x64@4.64.7': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.64.7': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.64.7': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.64.7': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.64.7': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.64.7': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.64.7': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.64.7': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.64.7': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.64.7': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.64.7': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.64.7': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.64.7': + optional: true + + '@rollup/rollup-linux-x64-musl@4.64.7': + optional: true + + '@rollup/rollup-openbsd-x64@4.64.7': + optional: true + + '@rollup/rollup-openharmony-arm64@4.64.7': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.64.7': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.64.7': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.64.7': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.64.7': + optional: true + + '@solid-primitives/event-listener@2.4.6(solid-js@1.9.17)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.17) + solid-js: 1.9.17 + + '@solid-primitives/keyboard@1.3.7(solid-js@1.9.17)': + dependencies: + '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.17) + '@solid-primitives/rootless': 1.5.4(solid-js@1.9.17) + '@solid-primitives/utils': 6.4.1(solid-js@1.9.17) + solid-js: 1.9.17 + + '@solid-primitives/resize-observer@2.2.0(solid-js@1.9.17)': + dependencies: + '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.17) + '@solid-primitives/rootless': 1.5.4(solid-js@1.9.17) + '@solid-primitives/static-store': 0.1.4(solid-js@1.9.17) + '@solid-primitives/utils': 6.4.1(solid-js@1.9.17) + solid-js: 1.9.17 + + '@solid-primitives/rootless@1.5.4(solid-js@1.9.17)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.17) + solid-js: 1.9.17 + + '@solid-primitives/static-store@0.1.4(solid-js@1.9.17)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.17) + solid-js: 1.9.17 + + '@solid-primitives/utils@6.4.1(solid-js@1.9.17)': + dependencies: + solid-js: 1.9.17 + + '@standard-schema/spec@1.1.0': {} + + '@stylistic/eslint-plugin@5.10.0(eslint@10.13.0(jiti@2.7.0))': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.13.0(jiti@2.7.0)) + '@typescript-eslint/types': 8.71.2 + eslint: 10.13.0(jiti@2.7.0) + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + estraverse: 5.3.0 + picomatch: 4.0.7 + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.26.0 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.2 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + 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 + + '@tailwindcss/typography@0.5.20(tailwindcss@4.3.3)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.3.3 + + '@tailwindcss/vite@4.3.3(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + tailwindcss: 4.3.3 + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + + '@tanstack/devtools-bundler-core@0.1.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@tanstack/devtools-client': 0.0.8 + '@tanstack/devtools-event-bus': 0.4.3 + chalk: 5.6.2 + launch-editor: 2.14.2 + magic-string: 0.30.21 + oxc-parser: 0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + picomatch: 4.0.7 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + - bufferutil + - utf-8-validate + + '@tanstack/devtools-client@0.0.8': + dependencies: + '@tanstack/devtools-event-client': 0.5.0 + + '@tanstack/devtools-event-bus@0.4.3': + dependencies: + ws: 8.22.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tanstack/devtools-event-client@0.5.0': {} + + '@tanstack/devtools-ui@0.7.1(csstype@3.2.3)(solid-js@1.9.17)': + dependencies: + clsx: 2.1.1 + dayjs: 1.11.23 + goober: 2.1.19(csstype@3.2.3) + solid-js: 1.9.17 + transitivePeerDependencies: + - csstype + + '@tanstack/devtools-vite@0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@tanstack/devtools-bundler-core': 0.1.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@tanstack/devtools-client': 0.0.8 + '@tanstack/devtools-event-bus': 0.4.3 + chalk: 5.6.2 + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + - bufferutil + - utf-8-validate + + '@tanstack/devtools@0.15.0(csstype@3.2.3)(solid-js@1.9.17)': + dependencies: + '@neodrag/core': 3.0.0-next.11 + '@neodrag/solid': 3.0.0-next.11(@neodrag/core@3.0.0-next.11)(solid-js@1.9.17) + '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.17) + '@solid-primitives/keyboard': 1.3.7(solid-js@1.9.17) + '@solid-primitives/resize-observer': 2.2.0(solid-js@1.9.17) + '@tanstack/devtools-client': 0.0.8 + '@tanstack/devtools-event-bus': 0.4.3 + '@tanstack/devtools-ui': 0.7.1(csstype@3.2.3)(solid-js@1.9.17) + clsx: 2.1.1 + goober: 2.1.19(csstype@3.2.3) + solid-js: 1.9.17 + transitivePeerDependencies: + - bufferutil + - csstype + - utf-8-validate + + '@tanstack/eslint-config@0.4.0(@typescript-eslint/utils@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint/js': 10.0.1(eslint@10.13.0(jiti@2.7.0)) + '@stylistic/eslint-plugin': 5.10.0(eslint@10.13.0(jiti@2.7.0)) + eslint: 10.13.0(jiti@2.7.0) + eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.13.0(jiti@2.7.0)) + eslint-plugin-n: 17.24.0(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + globals: 17.13.0 + typescript-eslint: 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + vue-eslint-parser: 10.4.1(eslint@10.13.0(jiti@2.7.0)) + transitivePeerDependencies: + - '@typescript-eslint/utils' + - eslint-import-resolver-node + - supports-color + - typescript + + '@tanstack/history@1.162.5': {} + + '@tanstack/react-devtools@0.10.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(csstype@3.2.3)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(solid-js@1.9.17)': + dependencies: + '@tanstack/devtools': 0.15.0(csstype@3.2.3)(solid-js@1.9.17) + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + transitivePeerDependencies: + - bufferutil + - csstype + - solid-js + - utf-8-validate + + '@tanstack/react-router-devtools@1.167.2(@tanstack/react-router@1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(@tanstack/router-core@1.171.38)(csstype@3.2.3)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': + dependencies: + '@tanstack/react-router': 1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@tanstack/router-devtools-core': 1.168.2(@tanstack/router-core@1.171.38)(csstype@3.2.3) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + optionalDependencies: + '@tanstack/router-core': 1.171.38 + transitivePeerDependencies: + - csstype + + '@tanstack/react-router@1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': + dependencies: + '@tanstack/history': 1.162.5 + '@tanstack/react-store': 0.11.2(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@tanstack/router-core': 1.171.38 + isbot: 5.2.2 + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + + '@tanstack/react-store@0.11.2(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': + dependencies: + '@tanstack/store': 0.11.2 + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + use-sync-external-store: 1.7.0(react@19.3.1) + + '@tanstack/router-core@1.171.38': + dependencies: + '@tanstack/history': 1.162.5 + cookie-es: 3.1.1 + seroval: 1.6.9 + seroval-plugins: 1.6.9(seroval@1.6.9) + + '@tanstack/router-devtools-core@1.168.2(@tanstack/router-core@1.171.38)(csstype@3.2.3)': + dependencies: + '@tanstack/router-core': 1.171.38 + clsx: 2.1.1 + goober: 2.1.19(csstype@3.2.3) + optionalDependencies: + csstype: 3.2.3 + + '@tanstack/router-generator@1.167.45': + 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.10 + zod: 4.6.7 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.47(@tanstack/react-router@1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(esbuild@0.28.2)(rollup@4.64.7)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))': + 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.4.0(esbuild@0.28.2)(rollup@4.64.7)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + zod: 4.6.7 + optionalDependencies: + '@tanstack/react-router': 1.170.45(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.4': + dependencies: + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + ansis: 4.4.0 + babel-dead-code-elimination: 1.0.12 + diff: 9.0.0 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/store@0.11.2': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + + '@testing-library/dom@10.4.2': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.10 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': + dependencies: + '@babel/runtime': 7.29.10 + '@testing-library/dom': 10.4.2 + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.20.5': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/type-utils': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.2 + eslint: 10.13.0(jiti@2.7.0) + ignore: 7.0.12 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.2 + debug: 4.4.3 + eslint: 10.13.0(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.71.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.71.2(typescript@5.9.3) + '@typescript-eslint/types': 8.71.2 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.71.2': + dependencies: + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/visitor-keys': 8.71.2 + + '@typescript-eslint/tsconfig-utils@8.71.2(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 10.13.0(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.71.2': {} + + '@typescript-eslint/typescript-estree@8.71.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.71.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.71.2(typescript@5.9.3) + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/visitor-keys': 8.71.2 + debug: 4.4.3 + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.13.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + eslint: 10.13.0(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.71.2': + dependencies: + '@typescript-eslint/types': 8.71.2 + eslint-visitor-keys: 5.0.1 + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.2.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@vitejs/plugin-react@5.2.0(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-rc.3 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + + '@vitest/mocker@5.0.5(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.5 + estree-walker: 3.0.3 + magic-string: 1.4.3 + optionalDependencies: + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + + '@vitest/spy@5.0.5': {} + + acorn-jsx@5.3.2(acorn@8.19.0): + dependencies: + acorn: 8.19.0 + + acorn@8.19.0: {} + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + + ansis@4.4.0: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + assertion-error@2.0.1: {} + + babel-dead-code-elimination@1.0.12: + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/traverse': 7.29.10 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.29: {} + + better-auth@1.7.7(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(solid-js@1.9.17)(vitest@5.0.5(@types/node@22.20.5)(jsdom@28.1.0(@noble/hashes@2.4.0))(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0))): + dependencies: + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5) + '@better-auth/drizzle-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/kysely-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(kysely@0.29.6) + '@better-auth/memory-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/prisma-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/telemetry': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.7))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2) + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + '@noble/ciphers': 2.4.0 + '@noble/hashes': 2.4.0 + better-call: 1.4.0(zod@4.6.7) + defu: 6.1.7 + jose: 6.2.12 + kysely: 0.29.6 + nanostores: 1.5.5 + zod: 4.6.7 + optionalDependencies: + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + solid-js: 1.9.17 + vitest: 5.0.5(@types/node@22.20.5)(jsdom@28.1.0(@noble/hashes@2.4.0))(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + transitivePeerDependencies: + - '@opentelemetry/api' + + better-call@1.4.0(zod@4.6.7): + dependencies: + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.2 + rou3: 0.9.2 + set-cookie-parser: 3.1.4 + optionalDependencies: + zod: 4.6.7 + + bidi-js@1.1.0: + dependencies: + require-from-string: 2.0.2 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.29.3: + dependencies: + baseline-browser-mapping: 2.11.29 + caniuse-lite: 1.0.30001816 + electron-to-chromium: 1.5.458 + node-releases: 2.0.59 + update-browserslist-db: 1.3.4(browserslist@4.29.3) + + cacheable@2.5.0: + dependencies: + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + + caniuse-lite@1.0.30001816: {} + + chai@6.3.0: {} + + chalk@5.6.2: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + + clsx@2.1.1: {} + + comment-parser@1.4.9: {} + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.2 + + cssesc@3.0.0: {} + + cssstyle@6.2.0: + dependencies: + '@asamuzakjp/css-color': 5.1.11 + '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) + css-tree: 3.2.1 + lru-cache: 11.5.3 + + csstype@3.2.3: {} + + data-urls@7.0.0(@noble/hashes@2.4.0): + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1(@noble/hashes@2.4.0) + transitivePeerDependencies: + - '@noble/hashes' + + dayjs@1.11.23: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-is@0.1.4: {} + + defu@6.1.7: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + diff@9.0.0: {} + + dom-accessibility-api@0.5.16: {} + + electron-to-chromium@1.5.458: {} + + enhanced-resolve@5.26.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@8.1.0: {} + + es-module-lexer@2.3.2: {} + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-compat-utils@0.5.1(eslint@10.13.0(jiti@2.7.0)): + dependencies: + eslint: 10.13.0(jiti@2.7.0) + semver: 7.8.5 + + eslint-import-context@0.1.9(unrs-resolver@1.12.2): + dependencies: + get-tsconfig: 4.14.3 + stable-hash-x: 0.2.0 + optionalDependencies: + unrs-resolver: 1.12.2 + + eslint-plugin-es-x@7.8.0(eslint@10.13.0(jiti@2.7.0)): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.13.0(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + eslint: 10.13.0(jiti@2.7.0) + eslint-compat-utils: 0.5.1(eslint@10.13.0(jiti@2.7.0)) + + eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.13.0(jiti@2.7.0)): + dependencies: + '@typescript-eslint/types': 8.71.2 + comment-parser: 1.4.9 + debug: 4.4.3 + eslint: 10.13.0(jiti@2.7.0) + eslint-import-context: 0.1.9(unrs-resolver@1.12.2) + is-glob: 4.0.3 + minimatch: 10.2.6 + semver: 7.8.5 + stable-hash-x: 0.2.0 + unrs-resolver: 1.12.2 + optionalDependencies: + '@typescript-eslint/utils': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-n@17.24.0(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.13.0(jiti@2.7.0)) + enhanced-resolve: 5.26.0 + eslint: 10.13.0(jiti@2.7.0) + eslint-plugin-es-x: 7.8.0(eslint@10.13.0(jiti@2.7.0)) + get-tsconfig: 4.14.3 + globals: 15.15.0 + globrex: 0.1.2 + ignore: 5.3.2 + semver: 7.8.5 + ts-declaration-location: 1.0.7(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.13.0(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.13.0(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.7.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.3 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 11.1.5 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.6 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) + eslint-visitor-keys: 4.2.1 + + espree@11.2.0: + dependencies: + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.4.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@11.1.5: + dependencies: + flat-cache: 6.1.23 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@6.1.23: + dependencies: + cacheable: 2.5.0 + flatted: 3.4.4 + hookified: 1.15.1 + + flatted@3.4.4: {} + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + get-tsconfig@4.14.3: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@15.15.0: {} + + globals@17.13.0: {} + + globrex@0.1.2: {} + + goober@2.1.19(csstype@3.2.3): + dependencies: + csstype: 3.2.3 + + graceful-fs@4.2.11: {} + + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + + hookified@1.15.1: {} + + hookified@2.2.0: {} + + html-encoding-sniffer@6.0.0(@noble/hashes@2.4.0): + dependencies: + '@exodus/bytes': 1.16.0(@noble/hashes@2.4.0) + transitivePeerDependencies: + - '@noble/hashes' + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + ignore@5.3.2: {} + + ignore@7.0.12: {} + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-potential-custom-element-name@1.0.1: {} + + isbot@5.2.2: {} + + isexe@2.0.0: {} + + jiti@2.7.0: {} + + jose@6.2.12: {} + + js-tokens@4.0.0: {} + + jsdom@28.1.0(@noble/hashes@2.4.0): + dependencies: + '@acemir/cssom': 0.9.32 + '@asamuzakjp/dom-selector': 6.8.1 + '@bramus/specificity': 2.4.3 + '@exodus/bytes': 1.16.0(@noble/hashes@2.4.0) + cssstyle: 6.2.0 + data-urls: 7.0.0(@noble/hashes@2.4.0) + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0(@noble/hashes@2.4.0) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.2 + undici: 7.30.1 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1(@noble/hashes@2.4.0) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + - supports-color + + jsesc@3.1.0: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + + kysely@0.29.6: {} + + launch-editor@2.14.2: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.12.0 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + 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 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lru-cache@11.5.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.545.0(react@19.3.1): + dependencies: + react: 19.3.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magic-string@1.4.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + mdn-data@2.27.1: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + ms@2.1.3: {} + + nanoid@3.3.20: {} + + nanostores@1.5.5: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.59: {} + + obug@3.0.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + oxc-parser@0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + dependencies: + '@oxc-project/types': 0.120.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.120.0 + '@oxc-parser/binding-android-arm64': 0.120.0 + '@oxc-parser/binding-darwin-arm64': 0.120.0 + '@oxc-parser/binding-darwin-x64': 0.120.0 + '@oxc-parser/binding-freebsd-x64': 0.120.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 + '@oxc-parser/binding-linux-arm64-musl': 0.120.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-musl': 0.120.0 + '@oxc-parser/binding-openharmony-arm64': 0.120.0 + '@oxc-parser/binding-wasm32-wasi': 0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 + '@oxc-parser/binding-win32-x64-msvc': 0.120.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parse5@8.0.1: + dependencies: + entities: 8.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.30: + dependencies: + nanoid: 3.3.20 + picocolors: 1.1.1 + source-map-js: 1.2.2 + + prelude-ls@1.2.1: {} + + prettier@3.9.10: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + punycode@2.3.1: {} + + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + react-dom@19.3.1(react@19.3.1): + dependencies: + react: 19.3.1 + scheduler: 0.28.0 + + react-is@17.0.2: {} + + react-refresh@0.18.0: {} + + react@19.3.1: {} + + readdirp@5.1.1: {} + + require-from-string@2.0.2: {} + + resolve-pkg-maps@1.0.0: {} + + rollup@4.64.7: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.64.7 + '@rollup/rollup-android-arm64': 4.64.7 + '@rollup/rollup-darwin-arm64': 4.64.7 + '@rollup/rollup-darwin-x64': 4.64.7 + '@rollup/rollup-freebsd-arm64': 4.64.7 + '@rollup/rollup-freebsd-x64': 4.64.7 + '@rollup/rollup-linux-arm-gnueabihf': 4.64.7 + '@rollup/rollup-linux-arm-musleabihf': 4.64.7 + '@rollup/rollup-linux-arm64-gnu': 4.64.7 + '@rollup/rollup-linux-arm64-musl': 4.64.7 + '@rollup/rollup-linux-loong64-gnu': 4.64.7 + '@rollup/rollup-linux-loong64-musl': 4.64.7 + '@rollup/rollup-linux-ppc64-gnu': 4.64.7 + '@rollup/rollup-linux-ppc64-musl': 4.64.7 + '@rollup/rollup-linux-riscv64-gnu': 4.64.7 + '@rollup/rollup-linux-riscv64-musl': 4.64.7 + '@rollup/rollup-linux-s390x-gnu': 4.64.7 + '@rollup/rollup-linux-x64-gnu': 4.64.7 + '@rollup/rollup-linux-x64-musl': 4.64.7 + '@rollup/rollup-openbsd-x64': 4.64.7 + '@rollup/rollup-openharmony-arm64': 4.64.7 + '@rollup/rollup-win32-arm64-msvc': 4.64.7 + '@rollup/rollup-win32-ia32-msvc': 4.64.7 + '@rollup/rollup-win32-x64-gnu': 4.64.7 + '@rollup/rollup-win32-x64-msvc': 4.64.7 + fsevents: 2.3.3 + + rou3@0.9.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.28.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + seroval-plugins@1.6.9(seroval@1.6.9): + dependencies: + seroval: 1.6.9 + + seroval@1.6.9: {} + + set-cookie-parser@3.1.4: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.12.0: {} + + solid-js@1.9.17: + dependencies: + csstype: 3.2.3 + seroval: 1.6.9 + seroval-plugins: 1.6.9(seroval@1.6.9) + + source-map-js@1.2.2: {} + + stable-hash-x@0.2.0: {} + + std-env@4.3.0: {} + + symbol-tree@3.2.4: {} + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + tinybench@6.2.2: {} + + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tldts-core@7.4.18: {} + + tldts@7.4.18: + dependencies: + tldts-core: 7.4.18 + + tough-cookie@6.0.2: + dependencies: + tldts: 7.4.18 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-declaration-location@1.0.7(typescript@5.9.3): + dependencies: + picomatch: 4.0.7 + typescript: 5.9.3 + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@2.8.1: + optional: true + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.2(eslint@10.13.0(jiti@2.7.0))(typescript@5.9.3) + eslint: 10.13.0(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + undici@7.30.1: {} + + unplugin@3.4.0(esbuild@0.28.2)(rollup@4.64.7)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + esbuild: 0.28.2 + rollup: 4.64.7 + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + + unrs-resolver@1.12.2: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + update-browserslist-db@1.3.4(browserslist@4.29.3): + dependencies: + browserslist: 4.29.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.7.0(react@19.3.1): + dependencies: + react: 19.3.1 + + util-deprecate@1.0.2: {} + + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0): + dependencies: + esbuild: 0.28.2 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + postcss: 8.5.30 + rollup: 4.64.7 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.5 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + + vitest@5.0.5(@types/node@22.20.5)(jsdom@28.1.0(@noble/hashes@2.4.0))(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.5(vite@7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0)) + chai: 6.3.0 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.3 + obug: 3.0.0 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.2.2 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + vite: 7.3.7(@types/node@22.20.5)(jiti@2.7.0)(lightningcss@1.32.0) + why-is-node-running: 3.2.1 + optionalDependencies: + '@types/node': 22.20.5 + jsdom: 28.1.0(@noble/hashes@2.4.0) + transitivePeerDependencies: + - msw + + vue-eslint-parser@10.4.1(eslint@10.13.0(jiti@2.7.0)): + dependencies: + debug: 4.4.3 + eslint: 10.13.0(jiti@2.7.0) + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1(@noble/hashes@2.4.0): + dependencies: + '@exodus/bytes': 1.16.0(@noble/hashes@2.4.0) + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@3.2.1: {} + + word-wrap@1.2.5: {} + + ws@8.22.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod@4.6.7: {} From aebdb7c7382bcdf9355fab8582e49c7ad42c0108 Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Fri, 16 Oct 2026 22:31:22 -0400 Subject: [PATCH 2/7] feat(fake-backend): add configuration script and hypothetical scenario presets --- .gitignore | 1 + docs/frontend-architecture.md | 27 ++ scripts/configure-fake-backend.mjs | 545 ++++++++++++++++++++++++ scripts/fake-backend-scenarios.mjs | 643 ++++++++++++++++++++++++++++ scripts/fake-backend.mjs | 660 +++++++++++------------------ web-interface/package.json | 1 + 6 files changed, 1457 insertions(+), 420 deletions(-) create mode 100644 scripts/configure-fake-backend.mjs create mode 100644 scripts/fake-backend-scenarios.mjs diff --git a/.gitignore b/.gitignore index 40ae98d..f456dba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__/ *.py[cod] data/ .direnv/ +.fake-backend-config.json diff --git a/docs/frontend-architecture.md b/docs/frontend-architecture.md index 41e2c50..f27ed6a 100644 --- a/docs/frontend-architecture.md +++ b/docs/frontend-architecture.md @@ -96,3 +96,30 @@ npm --prefix web-interface run dev -- --host 127.0.0.1 --port 3001 --strictPort ``` Open http://127.0.0.1:3001 in your browser. Pre-seeded users include `ada@utmist.gitlab.io` (admin), `alan@utmist.gitlab.io`, and `grace@utmist.gitlab.io`. + +### Configuring hypothetical scenarios and fake information + +A companion configure tool is provided at `scripts/configure-fake-backend.mjs` to set up hypothetical cluster topologies, error conditions, and user setups: + +```bash +# Interactive menu (choose scenarios, add fake nodes, switch active user) +npm --prefix web-interface run configure-fake-backend + +# Switch to a preset scenario directly +node scripts/configure-fake-backend.mjs --scenario high-load +node scripts/configure-fake-backend.mjs --scenario unhealthy +node scripts/configure-fake-backend.mjs --scenario empty-slate +node scripts/configure-fake-backend.mjs --scenario large-cluster +node scripts/configure-fake-backend.mjs --scenario default + +# Inspect current cluster topology & status +node scripts/configure-fake-backend.mjs --status + +# Add custom fake machines, users, or teams +node scripts/configure-fake-backend.mjs --add-machine name=dgx-custom,ip=10.99.1.5,cpu=64,ram=256Gi,gpu=8 +node scripts/configure-fake-backend.mjs --add-user name="Marie Curie",email=curie@rad.edu,role=admin +node scripts/configure-fake-backend.mjs --set-user curie@rad.edu +``` + +If the fake backend is running, `configure-fake-backend.mjs` updates the live server immediately without requiring a restart, while also saving to `.fake-backend-config.json` for persistence. + diff --git a/scripts/configure-fake-backend.mjs b/scripts/configure-fake-backend.mjs new file mode 100644 index 0000000..f5a8027 --- /dev/null +++ b/scripts/configure-fake-backend.mjs @@ -0,0 +1,545 @@ +#!/usr/bin/env node +/** + * Mist Fake Backend Configuration CLI + * + * Configures hypothetical scenarios, fake users, machines, and hardware pools + * for local testing of the web interface. + * + * Usage: + * node scripts/configure-fake-backend.mjs # Interactive mode + * node scripts/configure-fake-backend.mjs --scenario high-load # Switch scenario + * node scripts/configure-fake-backend.mjs --status # Show current topology + * node scripts/configure-fake-backend.mjs --list-scenarios # List all presets + * node scripts/configure-fake-backend.mjs --reset # Reset to default + */ + +import fs from 'node:fs' +import path from 'node:path' +import http from 'node:http' +import readline from 'node:readline/promises' +import { stdin as input, stdout as output } from 'node:process' +import { + getScenario, + SCENARIO_DESCRIPTIONS, + SCENARIO_FACTORIES, +} from './fake-backend-scenarios.mjs' + +const CONFIG_FILE = path.resolve(process.cwd(), '.fake-backend-config.json') + +// Parse CLI flags +const argv = process.argv.slice(2) +function getFlag(name) { + const idx = argv.indexOf(name) + return idx !== -1 && argv[idx + 1] ? argv[idx + 1] : null +} +function hasFlag(name) { + return argv.includes(name) +} + +const PORT = Number( + getFlag('--port') || process.env.PORT || process.env.MIST_FAKE_PORT || 3000, +) +const HOST = process.env.HOST || '127.0.0.1' + +// --- Server Communication Helpers --- + +function requestJSON(method, endpointPath, body = null) { + return new Promise((resolve, reject) => { + const data = body ? JSON.stringify(body) : null + const req = http.request( + { + hostname: HOST, + port: PORT, + path: endpointPath, + method, + headers: { + 'Content-Type': 'application/json', + ...(data ? { 'Content-Length': Buffer.byteLength(data) } : {}), + }, + timeout: 1500, + }, + (res) => { + let resData = '' + res.on('data', (chunk) => (resData += chunk)) + res.on('end', () => { + try { + resolve({ + status: res.statusCode, + data: resData ? JSON.parse(resData) : null, + }) + } catch (e) { + resolve({ status: res.statusCode, raw: resData }) + } + }) + }, + ) + req.on('timeout', () => { + req.destroy() + reject(new Error('Connection timed out')) + }) + req.on('error', (err) => reject(err)) + if (data) req.write(data) + req.end() + }) +} + +async function checkLiveServer() { + try { + const res = await requestJSON('GET', '/_fake/state') + if (res.status === 200 && res.data) { + return res.data + } + } catch {} + return null +} + +function loadLocalConfigFile() { + if (fs.existsSync(CONFIG_FILE)) { + try { + return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8')) + } catch {} + } + return null +} + +function saveLocalConfigFile(state) { + fs.writeFileSync(CONFIG_FILE, JSON.stringify(state, null, 2), 'utf-8') +} + +async function applyState(state) { + // 1. Save to local config file + saveLocalConfigFile(state) + + // 2. If live server running, update it dynamically + const live = await checkLiveServer() + if (live) { + try { + await requestJSON('POST', '/_fake/state', state) + console.log(`\n✓ Live fake backend at http://${HOST}:${PORT} updated instantly!`) + console.log(`✓ Saved configuration to .fake-backend-config.json`) + return + } catch (err) { + console.warn(`\n(Live update failed: ${err.message})`) + } + } + console.log(`\n✓ Saved configuration to .fake-backend-config.json`) + console.log(` (The fake backend is offline. Changes will take effect when it starts.)`) +} + +function parseKeyValue(inputStr) { + const result = {} + const pairs = inputStr.split(',') + for (const pair of pairs) { + const [k, v] = pair.split('=').map((s) => s.trim()) + if (k && v !== undefined) { + result[k] = v.replace(/^["']|["']$/g, '') + } + } + return result +} + +function printStatus(state) { + console.log('\n======================================================') + console.log(`Scenario: ${state.activeScenario || state.name || 'custom'}`) + const activeUser = + state.users?.[state.currentUserIndex] || state.currentUser || null + console.log( + `Active User: ${activeUser ? `${activeUser.name} <${activeUser.email}> [${activeUser.role}]` : 'None'}`, + ) + console.log(`Total Users: ${state.users?.length ?? 0}`) + console.log('------------------------------------------------------') + console.log('Machines & Nodes:') + for (const m of state.machines || []) { + const status = m.ready ? (m.schedulable ? 'Ready' : 'Placement Paused') : 'Offline' + console.log(` • ${m.name.padEnd(22)} ${m.address.padEnd(14)} [${status.padEnd(16)}] ${m.cpu_allocatable} CPU · ${m.memory_allocatable} RAM`) + } + console.log('Hardware Accelerator Pools:') + for (const p of state.pools || []) { + const avail = p.available === null ? 'Unknown' : p.available + const msg = p.message ? ` (${p.message})` : '' + console.log(` • ${p.accelerator.toUpperCase().padEnd(12)} Node: ${p.node.padEnd(20)} Total: ${p.total} Allocated: ${p.allocated} Available: ${avail}${msg}`) + } + console.log('Teams:') + for (const t of state.teams || []) { + console.log(` • ${t.name.padEnd(26)} Members: ${t.members?.length ?? 0} Policy: ${t.policy?.storage_gib ?? 0} GiB Datasets / ${t.policy?.model_storage_gib ?? 0} GiB Models`) + } + console.log('Jobs in History:') + const states = {} + for (const j of state.jobs || []) { + states[j.job_state] = (states[j.job_state] || 0) + 1 + } + console.log(` Total: ${state.jobs?.length ?? 0} (${Object.entries(states).map(([k, v]) => `${k}: ${v}`).join(', ') || '0'})`) + console.log('======================================================\n') +} + +// --- CLI Commands --- + +async function main() { + if (hasFlag('--help') || hasFlag('-h')) { + console.log(` +Mist Fake Backend Configurator + +Commands & Options: + --scenario Load a preset scenario (default, high-load, unhealthy, empty-slate, large-cluster) + --list-scenarios List all available built-in scenarios + --status Display current cluster topology, machines, users, and jobs + --set-user Set which user is active in the web session + --add-user Add a user: name="...",email="...",role=admin|user + --add-machine Add a machine: name="...",ip="...",cpu=32,ram=128Gi,gpu=4,tt=0 + --add-team Add a team: name="...",storage=300,models=600 + --config Load configuration from a JSON file + --export Export active configuration to a JSON file + --reset Reset back to the default scenario + --port Target port (default: 3000) + +Run with no flags for the interactive menu. +`) + return + } + + if (hasFlag('--list-scenarios')) { + console.log('\nAvailable Hypothetical Scenarios:') + for (const [name, desc] of Object.entries(SCENARIO_DESCRIPTIONS)) { + console.log(` • ${name.padEnd(16)} : ${desc}`) + } + console.log('') + return + } + + // Current state resolution + let currentState = (await checkLiveServer()) || loadLocalConfigFile() || getScenario('default') + + if (hasFlag('--status')) { + const live = await checkLiveServer() + console.log(live ? `[Backend Server: ONLINE at http://${HOST}:${PORT}]` : `[Backend Server: OFFLINE (reading config file)]`) + printStatus(currentState) + return + } + + if (hasFlag('--scenario')) { + const name = getFlag('--scenario') + try { + const scenario = getScenario(name) + await applyState(scenario) + printStatus(scenario) + } catch (err) { + console.error(`Error: ${err.message}`) + process.exit(1) + } + return + } + + if (hasFlag('--reset')) { + const defaultScenario = getScenario('default') + await applyState(defaultScenario) + console.log('Reset to default scenario successfully.') + return + } + + if (hasFlag('--config')) { + const filePath = getFlag('--config') + if (!fs.existsSync(filePath)) { + console.error(`File not found: ${filePath}`) + process.exit(1) + } + const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')) + await applyState(data) + console.log(`Loaded configuration from ${filePath}.`) + return + } + + if (hasFlag('--export')) { + const outPath = getFlag('--export') + fs.writeFileSync(outPath, JSON.stringify(currentState, null, 2), 'utf-8') + console.log(`Exported configuration to ${outPath}.`) + return + } + + if (hasFlag('--set-user')) { + const target = getFlag('--set-user') + const idx = (currentState.users || []).findIndex( + (u) => u.id === target || u.email.toLowerCase() === target.toLowerCase(), + ) + if (idx === -1) { + console.error(`User '${target}' not found. Available: ${currentState.users.map((u) => u.email).join(', ')}`) + process.exit(1) + } + currentState.currentUserIndex = idx + await applyState(currentState) + console.log(`Active user set to: ${currentState.users[idx].name} (${currentState.users[idx].email})`) + return + } + + if (hasFlag('--add-user')) { + const kv = parseKeyValue(getFlag('--add-user')) + if (!kv.name || !kv.email) { + console.error('Error: name and email are required (e.g. name="Marie Curie",email="curie@rad.edu",role=admin)') + process.exit(1) + } + const newUser = { + id: 'usr_' + Date.now().toString(36), + name: kv.name, + email: kv.email, + role: kv.role || 'user', + banned: false, + } + currentState.users = currentState.users || [] + currentState.users.push(newUser) + await applyState(currentState) + console.log(`Added user: ${newUser.name} <${newUser.email}> (${newUser.role})`) + return + } + + if (hasFlag('--add-machine')) { + const kv = parseKeyValue(getFlag('--add-machine')) + if (!kv.name) { + console.error('Error: machine name is required (e.g. name=dgx-a100,ip=10.0.1.50,cpu=64,ram=256Gi,gpu=8)') + process.exit(1) + } + const newMachine = { + name: kv.name, + address: kv.ip || kv.address || '10.0.1.' + Math.floor(Math.random() * 200 + 20), + ready: kv.ready !== 'false', + schedulable: kv.schedulable !== 'false', + cpu_allocatable: kv.cpu || '32', + memory_allocatable: kv.ram || '128Gi', + } + currentState.machines = currentState.machines || [] + currentState.machines.push(newMachine) + + // Add GPU or TT pools if requested + currentState.pools = currentState.pools || [] + if (Number(kv.gpu) > 0) { + currentState.pools.push({ + accelerator: 'nvidia', + node: newMachine.name, + unit: 'GPU', + total: Number(kv.gpu), + allocated: 0, + available: Number(kv.gpu), + ready: true, + }) + } + if (Number(kv.tt) > 0) { + currentState.pools.push({ + accelerator: 'tenstorrent', + node: newMachine.name, + unit: 'board', + total: Number(kv.tt), + allocated: 0, + available: Number(kv.tt), + ready: true, + chips_per_device: 2, + }) + } + await applyState(currentState) + console.log(`Added machine: ${newMachine.name} with ${kv.gpu || 0} GPUs and ${kv.tt || 0} Tenstorrent boards.`) + return + } + + if (hasFlag('--add-team')) { + const kv = parseKeyValue(getFlag('--add-team')) + if (!kv.name) { + console.error('Error: team name is required (e.g. name="Quantum ML",storage=500,models=1000)') + process.exit(1) + } + const activeUser = currentState.users?.[currentState.currentUserIndex] + const newTeam = { + id: 'team-' + kv.name.toLowerCase().replace(/\W+/g, '-'), + name: kv.name, + disabled: false, + policy: { + cpu: kv.cpu || '16', + memory: kv.memory || '64Gi', + nvidia: Number(kv.gpu ?? 2), + tenstorrent: Number(kv.tt ?? 2), + concurrent: 4, + queued: 20, + storage_gib: Number(kv.storage || 300), + model_storage_gib: Number(kv.models || 600), + runtime_seconds: 86400, + registries: ['docker.io', 'ghcr.io'], + }, + members: activeUser + ? [{ id: activeUser.id, name: activeUser.name, email: activeUser.email, common_writer: true }] + : [], + grants: [], + } + currentState.teams = currentState.teams || [] + currentState.teams.push(newTeam) + await applyState(currentState) + console.log(`Added team: ${newTeam.name}`) + return + } + + // --- Interactive Terminal Menu --- + const rl = readline.createInterface({ input, output }) + try { + while (true) { + const live = await checkLiveServer() + console.clear() + console.log('======================================================') + console.log(' MIST FAKE BACKEND CONFIGURATOR') + console.log('======================================================') + console.log(`Backend Server: ${live ? `ONLINE (http://${HOST}:${PORT})` : 'OFFLINE (saving to config file)'}`) + console.log(`Active Scenario: ${currentState.activeScenario || currentState.name || 'custom'}`) + const u = currentState.users?.[currentState.currentUserIndex] + console.log(`Active User: ${u ? `${u.name} <${u.email}> (${u.role})` : 'None'}`) + console.log(`Machines: ${currentState.machines?.map((m) => m.name).join(', ') || 'None'}`) + console.log(`Teams: ${currentState.teams?.map((t) => t.name).join(', ') || 'None'}`) + console.log('------------------------------------------------------') + console.log('Select an action:') + console.log(' 1) Switch to a hypothetical scenario preset') + console.log(' 2) Switch active logged-in user') + console.log(' 3) Add a fake machine / node') + console.log(' 4) Add a fake user') + console.log(' 5) Add a fake team') + console.log(' 6) View full cluster status') + console.log(' 7) Reset to default scenario') + console.log(' 8) Exit') + console.log('------------------------------------------------------') + + const choice = (await rl.question('Enter choice [1-8]: ')).trim() + + if (choice === '1') { + console.log('\nAvailable Hypothetical Scenarios:') + const keys = Object.keys(SCENARIO_DESCRIPTIONS) + keys.forEach((key, i) => { + console.log(` ${i + 1}) ${key.padEnd(16)} : ${SCENARIO_DESCRIPTIONS[key]}`) + }) + const sel = (await rl.question('\nSelect scenario [1-5]: ')).trim() + const idx = parseInt(sel, 10) - 1 + if (keys[idx]) { + currentState = getScenario(keys[idx]) + await applyState(currentState) + await rl.question('\nPress Enter to continue...') + } + } else if (choice === '2') { + console.log('\nAvailable Users:') + currentState.users.forEach((user, i) => { + const mark = i === currentState.currentUserIndex ? ' (ACTIVE)' : '' + console.log(` ${i + 1}) ${user.name} <${user.email}> [${user.role}]${mark}`) + }) + const sel = (await rl.question('\nSelect user number: ')).trim() + const idx = parseInt(sel, 10) - 1 + if (currentState.users[idx]) { + currentState.currentUserIndex = idx + await applyState(currentState) + } + } else if (choice === '3') { + console.log('\n--- Add Fake Machine ---') + const name = (await rl.question('Machine name (e.g. dgx-h100-01): ')).trim() + if (!name) continue + const ip = (await rl.question('IP Address (default 10.0.1.25): ')).trim() || '10.0.1.25' + const cpu = (await rl.question('Allocatable CPU cores (default 32): ')).trim() || '32' + const ram = (await rl.question('Allocatable RAM (default 128Gi): ')).trim() || '128Gi' + const gpu = parseInt((await rl.question('NVIDIA GPUs count (default 0): ')).trim() || '0', 10) + const tt = parseInt((await rl.question('Tenstorrent boards count (default 0): ')).trim() || '0', 10) + + const machine = { + name, + address: ip, + ready: true, + schedulable: true, + cpu_allocatable: cpu, + memory_allocatable: ram, + } + currentState.machines = currentState.machines || [] + currentState.machines.push(machine) + + currentState.pools = currentState.pools || [] + if (gpu > 0) { + currentState.pools.push({ + accelerator: 'nvidia', + node: name, + unit: 'GPU', + total: gpu, + allocated: 0, + available: gpu, + ready: true, + }) + } + if (tt > 0) { + currentState.pools.push({ + accelerator: 'tenstorrent', + node: name, + unit: 'board', + total: tt, + allocated: 0, + available: tt, + ready: true, + chips_per_device: 2, + }) + } + await applyState(currentState) + await rl.question('\nMachine added! Press Enter to continue...') + } else if (choice === '4') { + console.log('\n--- Add Fake User ---') + const name = (await rl.question('Full name (e.g. Marie Curie): ')).trim() + if (!name) continue + const email = (await rl.question('Email (e.g. marie@utmist.gitlab.io): ')).trim() + if (!email) continue + const role = (await rl.question('Role (admin / user, default user): ')).trim() || 'user' + currentState.users = currentState.users || [] + currentState.users.push({ + id: 'usr_' + Date.now().toString(36), + name, + email, + role, + banned: false, + }) + await applyState(currentState) + await rl.question('\nUser added! Press Enter to continue...') + } else if (choice === '5') { + console.log('\n--- Add Fake Team ---') + const name = (await rl.question('Team name (e.g. Quantum Computing Lab): ')).trim() + if (!name) continue + const storage = parseInt((await rl.question('Dataset storage in GiB (default 300): ')).trim() || '300', 10) + const models = parseInt((await rl.question('Model storage in GiB (default 600): ')).trim() || '600', 10) + const activeUser = currentState.users?.[currentState.currentUserIndex] + + const newTeam = { + id: 'team-' + name.toLowerCase().replace(/\W+/g, '-'), + name, + disabled: false, + policy: { + cpu: '16', + memory: '64Gi', + nvidia: 2, + tenstorrent: 2, + concurrent: 4, + queued: 20, + storage_gib: storage, + model_storage_gib: models, + runtime_seconds: 86400, + registries: ['docker.io', 'ghcr.io'], + }, + members: activeUser + ? [{ id: activeUser.id, name: activeUser.name, email: activeUser.email, common_writer: true }] + : [], + grants: [], + } + currentState.teams = currentState.teams || [] + currentState.teams.push(newTeam) + await applyState(currentState) + await rl.question('\nTeam added! Press Enter to continue...') + } else if (choice === '6') { + printStatus(currentState) + await rl.question('\nPress Enter to return to menu...') + } else if (choice === '7') { + currentState = getScenario('default') + await applyState(currentState) + await rl.question('\nReset to default scenario! Press Enter to continue...') + } else if (choice === '8') { + break + } + } + } finally { + rl.close() + } +} + +main().catch((err) => { + console.error('Fatal error:', err) + process.exit(1) +}) diff --git a/scripts/fake-backend-scenarios.mjs b/scripts/fake-backend-scenarios.mjs new file mode 100644 index 0000000..b9a0636 --- /dev/null +++ b/scripts/fake-backend-scenarios.mjs @@ -0,0 +1,643 @@ +/** + * Mist Fake Backend - Built-in Hypothetical Scenarios + * + * Provides modular scenario generators for testing various cluster topologies, + * load conditions, failure modes, and organizational structures. + */ + +export const SCENARIO_DESCRIPTIONS = { + default: 'Standard balanced cluster with 1 GPU node, 1 Tenstorrent node, 2 teams, and 3 users.', + 'high-load': 'Resource-exhausted cluster: 100% GPU/TT saturation, long waiting queue, and 95% full storage.', + unhealthy: 'Degraded cluster with offline nodes, failed jobs, driver error messages, and storage errors.', + 'empty-slate': 'Clean empty cluster with 0 jobs, 0 datasets, and 0 teams for onboarding / first-run testing.', + 'large-cluster': 'Multi-node enterprise setup with 5 machines (H100s, Wormholes), 5 teams, and 30+ jobs.', +} + +export function createDefaultScenario() { + const now = Date.now() + return { + name: 'default', + description: SCENARIO_DESCRIPTIONS.default, + currentUserIndex: 0, + storageBudgetGiB: 2000, + users: [ + { + id: 'usr_ada', + name: 'Ada Lovelace', + email: 'ada@utmist.gitlab.io', + role: 'admin', + banned: false, + }, + { + id: 'usr_alan', + name: 'Alan Turing', + email: 'alan@utmist.gitlab.io', + role: 'user', + banned: false, + }, + { + id: 'usr_grace', + name: 'Grace Hopper', + email: 'grace@utmist.gitlab.io', + role: 'user', + banned: false, + }, + ], + machines: [ + { + name: 'mist-gpu-node-01', + address: '10.0.1.10', + ready: true, + schedulable: true, + cpu_allocatable: '32', + memory_allocatable: '128Gi', + }, + { + name: 'mist-tt-worker-01', + address: '10.0.1.11', + ready: true, + schedulable: true, + cpu_allocatable: '16', + memory_allocatable: '64Gi', + }, + ], + pools: [ + { + accelerator: 'nvidia', + node: 'mist-gpu-node-01', + unit: 'GPU', + total: 4, + allocated: 1, + available: 3, + ready: true, + }, + { + accelerator: 'tenstorrent', + node: 'mist-tt-worker-01', + unit: 'board', + total: 4, + allocated: 0, + available: 4, + ready: true, + chips_per_device: 2, + }, + ], + storage: { + enabled: true, + capacity_bytes: 1099511627776, // 1 TiB + available_bytes: 879609302220, // 800 GiB + model_capacity_bytes: 2199023255552, // 2 TiB + model_available_bytes: 1759218604441, // 1.6 TiB + dataset_allocation_gib: 500, + model_allocation_gib: 1000, + upload_limit_bytes: 5368709120, // 5 GiB + }, + teams: [ + { + id: 'team-vision', + name: 'Computer Vision', + disabled: false, + policy: { + cpu: '16', + memory: '64Gi', + nvidia: 2, + tenstorrent: 4, + concurrent: 4, + queued: 20, + storage_gib: 300, + model_storage_gib: 600, + runtime_seconds: 86400, + registries: ['docker.io', 'ghcr.io', 'quay.io'], + }, + members: [ + { + id: 'usr_ada', + name: 'Ada Lovelace', + email: 'ada@utmist.gitlab.io', + common_writer: true, + }, + { + id: 'usr_alan', + name: 'Alan Turing', + email: 'alan@utmist.gitlab.io', + common_writer: false, + }, + ], + grants: [ + { + id: 'grant-nlp-share', + target_team: 'team-nlp', + scope: 'common', + }, + ], + }, + { + id: 'team-nlp', + name: 'Natural Language Processing', + disabled: false, + policy: { + cpu: '8', + memory: '32Gi', + nvidia: 1, + tenstorrent: 2, + concurrent: 2, + queued: 10, + storage_gib: 200, + model_storage_gib: 400, + runtime_seconds: 43200, + registries: ['docker.io', 'ghcr.io'], + }, + members: [ + { + id: 'usr_ada', + name: 'Ada Lovelace', + email: 'ada@utmist.gitlab.io', + common_writer: true, + }, + { + id: 'usr_grace', + name: 'Grace Hopper', + email: 'grace@utmist.gitlab.io', + common_writer: false, + }, + ], + grants: [], + }, + ], + jobs: [ + { + id: 'job-seed-001', + name: 'ResNet50 Vision Benchmark', + created: new Date(now - 3600000).toISOString(), + job_state: 'Success', + accelerator: 'nvidia', + device_count: 2, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '8', + memory: '32Gi', + node: 'mist-gpu-node-01', + exit_code: 0, + message: 'Job completed successfully in 1420s', + checkpoint_directory: '/outputs/checkpoints/job-seed-001', + output_directory: '/outputs/runs/job-seed-001', + timeout_seconds: 7200, + team_id: 'team-vision', + creator_id: 'usr_ada', + creator_name: 'Ada Lovelace', + storage_scope: 'common', + can_cancel: false, + }, + { + id: 'job-seed-002', + name: 'BERT Fine-tuning Test', + created: new Date(now - 1800000).toISOString(), + job_state: 'InProgress', + accelerator: 'nvidia', + device_count: 1, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '4', + memory: '16Gi', + node: 'mist-gpu-node-01', + checkpoint_directory: '/outputs/checkpoints/job-seed-002', + output_directory: '/outputs/runs/job-seed-002', + timeout_seconds: 3600, + team_id: 'team-nlp', + creator_id: 'usr_grace', + creator_name: 'Grace Hopper', + storage_scope: 'usr_grace', + can_cancel: true, + }, + { + id: 'job-seed-003', + name: 'Tenstorrent Wormhole Matrix Multiplication', + created: new Date(now - 600000).toISOString(), + job_state: 'Scheduled', + accelerator: 'tenstorrent', + device_count: 2, + image: 'ghcr.io/utmist/tt-metal-runtime:latest', + cpu: '4', + memory: '16Gi', + message: 'Queued; waiting for 2 Tenstorrent n300 board allocations', + checkpoint_directory: '/outputs/checkpoints/job-seed-003', + output_directory: '/outputs/runs/job-seed-003', + timeout_seconds: 1800, + team_id: 'team-vision', + creator_id: 'usr_alan', + creator_name: 'Alan Turing', + storage_scope: 'usr_alan', + can_cancel: true, + }, + ], + datasets: [ + { + id: 'ds-cifar100', + name: 'CIFAR-100 Benchmark Dataset', + filename: 'cifar100.zip', + size: 169000000, + files: 100, + sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + created: new Date(now - 86400000).toISOString(), + state: 'ready', + team_id: 'team-vision', + team_name: 'Computer Vision', + scope: 'common', + member_name: 'Ada Lovelace', + shared: false, + }, + { + id: 'ds-wikitext', + name: 'Wikitext-103 Subset', + filename: 'wikitext.tar.gz', + size: 512000000, + files: 1, + sha256: 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', + created: new Date(now - 43200000).toISOString(), + state: 'ready', + team_id: 'team-nlp', + team_name: 'Natural Language Processing', + scope: 'common', + member_name: 'Grace Hopper', + shared: true, + }, + ], + } +} + +export function createHighLoadScenario() { + const now = Date.now() + const base = createDefaultScenario() + base.name = 'high-load' + base.description = SCENARIO_DESCRIPTIONS['high-load'] + + // Saturated hardware: 0 available + base.pools = [ + { + accelerator: 'nvidia', + node: 'mist-gpu-node-01', + unit: 'GPU', + total: 4, + allocated: 4, + available: 0, + ready: true, + }, + { + accelerator: 'tenstorrent', + node: 'mist-tt-worker-01', + unit: 'board', + total: 4, + allocated: 4, + available: 0, + ready: true, + chips_per_device: 2, + }, + ] + + // Storage 95% consumed + base.storage = { + enabled: true, + capacity_bytes: 1073741824000, // 1 TiB + available_bytes: 32212254720, // 30 GiB left (near capacity!) + model_capacity_bytes: 2147483648000, + model_available_bytes: 53687091200, // 50 GiB left + dataset_allocation_gib: 980, + model_allocation_gib: 1950, + upload_limit_bytes: 2147483648, + } + + // 4 running jobs taking all accelerators + 8 queued jobs + base.jobs = [ + { + id: 'job-hl-run-01', + name: 'LLM Multi-GPU Pretraining Node 1', + created: new Date(now - 7200000).toISOString(), + job_state: 'InProgress', + accelerator: 'nvidia', + device_count: 2, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '16', + memory: '64Gi', + node: 'mist-gpu-node-01', + checkpoint_directory: '/outputs/checkpoints/job-hl-run-01', + output_directory: '/outputs/runs/job-hl-run-01', + team_id: 'team-vision', + creator_name: 'Ada Lovelace', + can_cancel: true, + }, + { + id: 'job-hl-run-02', + name: 'Diffusion Video Upscaler', + created: new Date(now - 3600000).toISOString(), + job_state: 'InProgress', + accelerator: 'nvidia', + device_count: 2, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '16', + memory: '64Gi', + node: 'mist-gpu-node-01', + checkpoint_directory: '/outputs/checkpoints/job-hl-run-02', + output_directory: '/outputs/runs/job-hl-run-02', + team_id: 'team-nlp', + creator_name: 'Grace Hopper', + can_cancel: true, + }, + { + id: 'job-hl-run-03', + name: 'TT Wormhole Full-Board Stress Test', + created: new Date(now - 5400000).toISOString(), + job_state: 'InProgress', + accelerator: 'tenstorrent', + device_count: 4, + image: 'ghcr.io/utmist/tt-metal-runtime:latest', + cpu: '16', + memory: '64Gi', + node: 'mist-tt-worker-01', + checkpoint_directory: '/outputs/checkpoints/job-hl-run-03', + output_directory: '/outputs/runs/job-hl-run-03', + team_id: 'team-vision', + creator_name: 'Alan Turing', + can_cancel: true, + }, + // Queued jobs + ...Array.from({ length: 8 }, (_, i) => ({ + id: `job-hl-wait-0${i + 1}`, + name: `Queued Experiment #${i + 1}`, + created: new Date(now - (8 - i) * 300000).toISOString(), + job_state: 'Scheduled', + accelerator: i % 2 === 0 ? 'nvidia' : 'tenstorrent', + device_count: (i % 2) + 1, + image: i % 2 === 0 ? 'ghcr.io/utmist/pytorch-cuda:latest' : 'ghcr.io/utmist/tt-metal-runtime:latest', + cpu: '4', + memory: '16Gi', + message: `Waiting for resources (Position in queue: ${i + 1})`, + checkpoint_directory: `/outputs/checkpoints/wait-${i + 1}`, + output_directory: `/outputs/runs/wait-${i + 1}`, + team_id: i % 2 === 0 ? 'team-vision' : 'team-nlp', + creator_name: i % 3 === 0 ? 'Ada Lovelace' : i % 3 === 1 ? 'Alan Turing' : 'Grace Hopper', + can_cancel: true, + })), + ] + + return base +} + +export function createUnhealthyScenario() { + const now = Date.now() + const base = createDefaultScenario() + base.name = 'unhealthy' + base.description = SCENARIO_DESCRIPTIONS.unhealthy + + base.machines = [ + { + name: 'mist-gpu-node-01', + address: '10.0.1.10', + ready: false, + schedulable: false, + cpu_allocatable: '0', + memory_allocatable: '0Gi', + }, + { + name: 'mist-tt-worker-01', + address: '10.0.1.11', + ready: true, + schedulable: false, // Placement paused! + cpu_allocatable: '16', + memory_allocatable: '64Gi', + }, + ] + + base.pools = [ + { + accelerator: 'nvidia', + node: 'mist-gpu-node-01', + unit: 'GPU', + total: 4, + allocated: 0, + available: null, + ready: false, + message: 'Node offline; NVIDIA driver communication lost (NVML error 999)', + }, + { + accelerator: 'tenstorrent', + node: 'mist-tt-worker-01', + unit: 'board', + total: 4, + allocated: 0, + available: 0, + ready: false, + chips_per_device: 2, + message: 'Node cordon active: scheduling paused for driver kernel patch', + }, + ] + + base.jobs = [ + { + id: 'job-fail-001', + name: 'Vision Model OOM Failure', + created: new Date(now - 7200000).toISOString(), + job_state: 'Failure', + accelerator: 'nvidia', + device_count: 2, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '8', + memory: '16Gi', + node: 'mist-gpu-node-01', + exit_code: 137, + message: 'Command terminated by SIGKILL (Out of Memory - cgroup limit exceeded)', + checkpoint_directory: '/outputs/checkpoints/job-fail-001', + output_directory: '/outputs/runs/job-fail-001', + team_id: 'team-vision', + creator_name: 'Ada Lovelace', + can_cancel: false, + }, + { + id: 'job-fail-002', + name: 'Faulty Python Script Crash', + created: new Date(now - 3600000).toISOString(), + job_state: 'Failure', + accelerator: 'cpu', + device_count: 0, + image: 'docker.io/library/python:3.11-slim', + cpu: '2', + memory: '4Gi', + node: 'mist-tt-worker-01', + exit_code: 1, + message: 'RuntimeError: ModuleNotFoundError: No module named torch_geometric', + checkpoint_directory: '/outputs/checkpoints/job-fail-002', + output_directory: '/outputs/runs/job-fail-002', + team_id: 'team-nlp', + creator_name: 'Grace Hopper', + can_cancel: false, + }, + { + id: 'job-stuck-003', + name: 'Stuck Queued Job', + created: new Date(now - 1800000).toISOString(), + job_state: 'Scheduled', + accelerator: 'nvidia', + device_count: 1, + image: 'ghcr.io/utmist/pytorch-cuda:latest', + cpu: '4', + memory: '16Gi', + message: '0/2 nodes are available: 1 node is offline, 1 node has untolerated taint', + checkpoint_directory: '/outputs/checkpoints/job-stuck-003', + output_directory: '/outputs/runs/job-stuck-003', + team_id: 'team-vision', + creator_name: 'Alan Turing', + can_cancel: true, + }, + ] + + return base +} + +export function createEmptySlateScenario() { + return { + name: 'empty-slate', + description: SCENARIO_DESCRIPTIONS['empty-slate'], + currentUserIndex: 0, + storageBudgetGiB: 1000, + users: [ + { + id: 'usr_newcomer', + name: 'New Researcher', + email: 'researcher@utmist.gitlab.io', + role: 'admin', + banned: false, + }, + ], + machines: [ + { + name: 'lab-cluster-master', + address: '192.168.1.10', + ready: true, + schedulable: true, + cpu_allocatable: '16', + memory_allocatable: '64Gi', + }, + ], + pools: [ + { + accelerator: 'nvidia', + node: 'lab-cluster-master', + unit: 'GPU', + total: 2, + allocated: 0, + available: 2, + ready: true, + }, + ], + storage: { + enabled: true, + capacity_bytes: 536870912000, // 500 GiB + available_bytes: 536870912000, + model_capacity_bytes: 1073741824000, + model_available_bytes: 1073741824000, + dataset_allocation_gib: 0, + model_allocation_gib: 0, + upload_limit_bytes: 5368709120, + }, + teams: [], + jobs: [], + datasets: [], + } +} + +export function createLargeClusterScenario() { + const now = Date.now() + const base = createDefaultScenario() + base.name = 'large-cluster' + base.description = SCENARIO_DESCRIPTIONS['large-cluster'] + base.storageBudgetGiB = 10000 + + base.users = [ + { id: 'usr_ada', name: 'Ada Lovelace', email: 'ada@utmist.gitlab.io', role: 'admin', banned: false }, + { id: 'usr_alan', name: 'Alan Turing', email: 'alan@utmist.gitlab.io', role: 'user', banned: false }, + { id: 'usr_grace', name: 'Grace Hopper', email: 'grace@utmist.gitlab.io', role: 'user', banned: false }, + { id: 'usr_curie', name: 'Marie Curie', email: 'marie@utmist.gitlab.io', role: 'admin', banned: false }, + { id: 'usr_feynman', name: 'Richard Feynman', email: 'feynman@utmist.gitlab.io', role: 'user', banned: false }, + { id: 'usr_shannon', name: 'Claude Shannon', email: 'shannon@utmist.gitlab.io', role: 'user', banned: false }, + { id: 'usr_neumann', name: 'John von Neumann', email: 'neumann@utmist.gitlab.io', role: 'user', banned: true }, + ] + + base.machines = [ + { name: 'dgx-h100-node-01', address: '10.10.1.1', ready: true, schedulable: true, cpu_allocatable: '64', memory_allocatable: '512Gi' }, + { name: 'dgx-h100-node-02', address: '10.10.1.2', ready: true, schedulable: true, cpu_allocatable: '64', memory_allocatable: '512Gi' }, + { name: 'tt-wormhole-box-01', address: '10.10.1.3', ready: true, schedulable: true, cpu_allocatable: '32', memory_allocatable: '128Gi' }, + { name: 'tt-wormhole-box-02', address: '10.10.1.4', ready: true, schedulable: true, cpu_allocatable: '32', memory_allocatable: '128Gi' }, + { name: 'control-plane-k3s', address: '10.10.1.100', ready: true, schedulable: true, cpu_allocatable: '16', memory_allocatable: '64Gi' }, + ] + + base.pools = [ + { accelerator: 'nvidia', node: 'dgx-h100-node-01', unit: 'GPU', total: 8, allocated: 4, available: 4, ready: true }, + { accelerator: 'nvidia', node: 'dgx-h100-node-02', unit: 'GPU', total: 8, allocated: 6, available: 2, ready: true }, + { accelerator: 'tenstorrent', node: 'tt-wormhole-box-01', unit: 'board', total: 4, allocated: 2, available: 2, ready: true, chips_per_device: 2 }, + { accelerator: 'tenstorrent', node: 'tt-wormhole-box-02', unit: 'board', total: 4, allocated: 1, available: 3, ready: true, chips_per_device: 2 }, + ] + + base.teams = [ + { + id: 'team-cv', + name: 'Computer Vision Lab', + disabled: false, + policy: { cpu: '32', memory: '128Gi', nvidia: 8, tenstorrent: 4, concurrent: 8, queued: 40, storage_gib: 1000, model_storage_gib: 2000, runtime_seconds: 86400, registries: ['docker.io', 'ghcr.io'] }, + members: [{ id: 'usr_ada', name: 'Ada Lovelace', email: 'ada@utmist.gitlab.io', common_writer: true }, { id: 'usr_curie', name: 'Marie Curie', email: 'marie@utmist.gitlab.io', common_writer: true }], + grants: [], + }, + { + id: 'team-llm', + name: 'Foundation Models & NLP', + disabled: false, + policy: { cpu: '64', memory: '256Gi', nvidia: 8, tenstorrent: 2, concurrent: 6, queued: 30, storage_gib: 2000, model_storage_gib: 4000, runtime_seconds: 86400, registries: ['docker.io', 'ghcr.io'] }, + members: [{ id: 'usr_grace', name: 'Grace Hopper', email: 'grace@utmist.gitlab.io', common_writer: true }, { id: 'usr_shannon', name: 'Claude Shannon', email: 'shannon@utmist.gitlab.io', common_writer: false }], + grants: [], + }, + { + id: 'team-robotics', + name: 'Autonomous Robotics', + disabled: false, + policy: { cpu: '16', memory: '64Gi', nvidia: 4, tenstorrent: 4, concurrent: 4, queued: 20, storage_gib: 800, model_storage_gib: 1200, runtime_seconds: 43200, registries: ['docker.io'] }, + members: [{ id: 'usr_alan', name: 'Alan Turing', email: 'alan@utmist.gitlab.io', common_writer: true }, { id: 'usr_feynman', name: 'Richard Feynman', email: 'feynman@utmist.gitlab.io', common_writer: false }], + grants: [], + }, + ] + + // Generate 25 jobs across all states + const jobStates = ['Success', 'InProgress', 'Scheduled', 'Failure', 'Cancelled'] + base.jobs = Array.from({ length: 25 }, (_, i) => ({ + id: `job-enterprise-${String(i + 1).padStart(3, '0')}`, + name: `Model Exploration #${i + 1}`, + created: new Date(now - (25 - i) * 1200000).toISOString(), + job_state: jobStates[i % jobStates.length], + accelerator: i % 3 === 0 ? 'nvidia' : i % 3 === 1 ? 'tenstorrent' : 'cpu', + device_count: i % 3 === 2 ? 0 : (i % 4) + 1, + image: i % 3 === 0 ? 'ghcr.io/utmist/pytorch-cuda:latest' : i % 3 === 1 ? 'ghcr.io/utmist/tt-metal-runtime:latest' : 'docker.io/library/python:3.11-slim', + cpu: String((i % 4 + 1) * 4), + memory: `${(i % 4 + 1) * 16}Gi`, + node: i % 2 === 0 ? 'dgx-h100-node-01' : 'tt-wormhole-box-01', + exit_code: jobStates[i % jobStates.length] === 'Failure' ? 1 : jobStates[i % jobStates.length] === 'Success' ? 0 : undefined, + message: jobStates[i % jobStates.length] === 'Failure' ? 'Killed by cluster controller' : undefined, + checkpoint_directory: `/outputs/checkpoints/job-enterprise-${i + 1}`, + output_directory: `/outputs/runs/job-enterprise-${i + 1}`, + team_id: i % 2 === 0 ? 'team-cv' : 'team-llm', + creator_name: base.users[i % base.users.length].name, + can_cancel: jobStates[i % jobStates.length] === 'InProgress' || jobStates[i % jobStates.length] === 'Scheduled', + })) + + return base +} + +export const SCENARIO_FACTORIES = { + default: createDefaultScenario, + 'high-load': createHighLoadScenario, + unhealthy: createUnhealthyScenario, + 'empty-slate': createEmptySlateScenario, + 'large-cluster': createLargeClusterScenario, +} + +export function getScenario(name) { + const factory = SCENARIO_FACTORIES[name] + if (!factory) { + throw new Error(`Unknown scenario '${name}'. Available: ${Object.keys(SCENARIO_FACTORIES).join(', ')}`) + } + return factory() +} diff --git a/scripts/fake-backend.mjs b/scripts/fake-backend.mjs index 90c8c29..00d33f5 100644 --- a/scripts/fake-backend.mjs +++ b/scripts/fake-backend.mjs @@ -8,282 +8,181 @@ * * Usage: * node scripts/fake-backend.mjs + * node scripts/fake-backend.mjs --scenario high-load + * node scripts/fake-backend.mjs --config ./custom-config.json * PORT=3000 node scripts/fake-backend.mjs * npm --prefix web-interface run fake-backend */ import http from 'node:http' +import fs from 'node:fs' +import path from 'node:path' import { URL } from 'node:url' import crypto from 'node:crypto' +import { + getScenario, + SCENARIO_DESCRIPTIONS, + SCENARIO_FACTORIES, +} from './fake-backend-scenarios.mjs' + +// Parse command line arguments +const args = process.argv.slice(2) +function getArg(flag) { + const idx = args.indexOf(flag) + return idx !== -1 && args[idx + 1] ? args[idx + 1] : null +} -const PORT = Number(process.env.PORT || process.env.MIST_FAKE_PORT || 3000) +const PORT = Number( + getArg('--port') || process.env.PORT || process.env.MIST_FAKE_PORT || 3000, +) const HOST = process.env.HOST || '127.0.0.1' +const DEFAULT_CONFIG_FILE = path.resolve(process.cwd(), '.fake-backend-config.json') -// --- In-Memory State & Seed Data --- - -const state = { - users: [ - { - id: 'usr_ada', - name: 'Ada Lovelace', - email: 'ada@utmist.gitlab.io', - role: 'admin', - banned: false, - }, - { - id: 'usr_alan', - name: 'Alan Turing', - email: 'alan@utmist.gitlab.io', - role: 'user', - banned: false, - }, - { - id: 'usr_grace', - name: 'Grace Hopper', - email: 'grace@utmist.gitlab.io', - role: 'user', - banned: false, - }, - ], - currentUserIndex: 0, // Ada Lovelace by default - teams: [ - { - id: 'team-vision', - name: 'Computer Vision', - disabled: false, - policy: { - cpu: '16', - memory: '64Gi', - nvidia: 2, - tenstorrent: 4, - concurrent: 4, - queued: 20, - storage_gib: 300, - model_storage_gib: 600, - runtime_seconds: 86400, - registries: ['docker.io', 'ghcr.io', 'quay.io'], - }, - members: [ - { - id: 'usr_ada', - name: 'Ada Lovelace', - email: 'ada@utmist.gitlab.io', - common_writer: true, - }, - { - id: 'usr_alan', - name: 'Alan Turing', - email: 'alan@utmist.gitlab.io', - common_writer: false, - }, - ], - grants: [ - { - id: 'grant-nlp-share', - target_team: 'team-nlp', - scope: 'common', - }, - ], - }, - { - id: 'team-nlp', - name: 'Natural Language Processing', - disabled: false, - policy: { - cpu: '8', - memory: '32Gi', - nvidia: 1, - tenstorrent: 2, - concurrent: 2, - queued: 10, - storage_gib: 200, - model_storage_gib: 400, - runtime_seconds: 43200, - registries: ['docker.io', 'ghcr.io'], - }, - members: [ - { - id: 'usr_ada', - name: 'Ada Lovelace', - email: 'ada@utmist.gitlab.io', - common_writer: true, - }, - { - id: 'usr_grace', - name: 'Grace Hopper', - email: 'grace@utmist.gitlab.io', - common_writer: false, - }, - ], - grants: [], - }, - ], +// --- Initial State Management --- + +let state = { + activeScenario: 'default', + users: [], + currentUserIndex: 0, + machines: [], + pools: [], storageBudgetGiB: 2000, - jobs: [ - { - id: 'job-seed-001', - name: 'ResNet50 Vision Benchmark', - created: new Date(Date.now() - 3600000).toISOString(), - job_state: 'Success', - accelerator: 'nvidia', - device_count: 2, - image: 'ghcr.io/utmist/pytorch-cuda:latest', - cpu: '8', - memory: '32Gi', - node: 'mist-gpu-node-01', - exit_code: 0, - message: 'Job completed successfully in 1420s', - checkpoint_directory: '/outputs/checkpoints/job-seed-001', - output_directory: '/outputs/runs/job-seed-001', - timeout_seconds: 7200, - team_id: 'team-vision', - creator_id: 'usr_ada', - creator_name: 'Ada Lovelace', - storage_scope: 'common', - can_cancel: false, - }, - { - id: 'job-seed-002', - name: 'BERT Fine-tuning Test', - created: new Date(Date.now() - 1800000).toISOString(), - job_state: 'InProgress', - accelerator: 'nvidia', - device_count: 1, - image: 'ghcr.io/utmist/pytorch-cuda:latest', - cpu: '4', - memory: '16Gi', - node: 'mist-gpu-node-01', - checkpoint_directory: '/outputs/checkpoints/job-seed-002', - output_directory: '/outputs/runs/job-seed-002', - timeout_seconds: 3600, - team_id: 'team-nlp', - creator_id: 'usr_grace', - creator_name: 'Grace Hopper', - storage_scope: 'usr_grace', - can_cancel: true, - }, - { - id: 'job-seed-003', - name: 'Tenstorrent Wormhole Matrix Multiplication', - created: new Date(Date.now() - 600000).toISOString(), - job_state: 'Scheduled', - accelerator: 'tenstorrent', - device_count: 2, - image: 'ghcr.io/utmist/tt-metal-runtime:latest', - cpu: '4', - memory: '16Gi', - message: 'Queued; waiting for 2 Tenstorrent n300 board allocations', - checkpoint_directory: '/outputs/checkpoints/job-seed-003', - output_directory: '/outputs/runs/job-seed-003', - timeout_seconds: 1800, - team_id: 'team-vision', - creator_id: 'usr_alan', - creator_name: 'Alan Turing', - storage_scope: 'usr_alan', - can_cancel: true, - }, - ], - datasets: [ - { - id: 'ds-cifar100', - name: 'CIFAR-100 Benchmark Dataset', - filename: 'cifar100.zip', - size: 169000000, - files: 100, - sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - created: new Date(Date.now() - 86400000).toISOString(), - state: 'ready', - team_id: 'team-vision', - team_name: 'Computer Vision', - scope: 'common', - member_name: 'Ada Lovelace', - shared: false, - }, - { - id: 'ds-wikitext', - name: 'Wikitext-103 Subset', - filename: 'wikitext.tar.gz', - size: 512000000, - files: 1, - sha256: 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', - created: new Date(Date.now() - 43200000).toISOString(), - state: 'ready', - team_id: 'team-nlp', - team_name: 'Natural Language Processing', - scope: 'common', - member_name: 'Grace Hopper', - shared: true, - }, - ], - jobLogs: new Map([ - [ - 'job-seed-001', - `[2026-10-16 20:00:01] [INFO] Initializing PyTorch runtime on NVIDIA node 'mist-gpu-node-01' -[2026-10-16 20:00:03] [INFO] Allocated 2x NVIDIA RTX A6000 GPUs (CUDA devices 0, 1) -[2026-10-16 20:00:05] [INFO] Loading CIFAR-100 dataset from /inputs/cifar100 -[2026-10-16 20:00:12] [INFO] Epoch 1/10: train_loss=2.31, val_acc=68.4% -[2026-10-16 20:10:45] [INFO] Epoch 10/10: train_loss=0.42, val_acc=84.2% -[2026-10-16 20:23:41] [INFO] Checkpoint saved to /outputs/runs/job-seed-001/checkpoint_final.pt -[2026-10-16 20:23:42] [INFO] Training complete. Process exited with code 0.`, - ], - [ - 'job-seed-002', - `[2026-10-16 20:30:00] [INFO] BERT fine-tuning initialized. -[2026-10-16 20:30:02] [INFO] Using GPU 0 (Allocated 1x GPU) -[2026-10-16 20:30:15] [INFO] Warmup steps complete. Training batch 450/2000... -[2026-10-16 20:30:30] [INFO] Current loss: 1.284`, - ], - [ - 'job-seed-003', - `[2026-10-16 20:50:00] [INFO] Job queued in team 'team-vision'. Awaiting Tenstorrent admission controller.`, + storage: {}, + teams: [], + jobs: [], + datasets: [], + images: { + self_service: true, + teams_enabled: true, + registries: ['docker.io', 'ghcr.io', 'quay.io', 'localhost:5000'], + images: [ + { reference: 'docker.io/library/python:3.11-slim', accelerators: ['cpu'] }, + { reference: 'docker.io/library/pytorch-cpu:2.2.0', accelerators: ['cpu'] }, + { reference: 'ghcr.io/utmist/pytorch-cuda:latest', accelerators: ['nvidia'] }, + { reference: 'ghcr.io/utmist/tt-metal-runtime:latest', accelerators: ['tenstorrent'] }, ], - ]), - jobFiles: new Map([ - [ - 'job-seed-001', - [ - { - path: 'checkpoint_final.pt', - size: 104857600, - modified: new Date(Date.now() - 3600000).toISOString(), - }, - { - path: 'training_metrics.json', - size: 4096, - modified: new Date(Date.now() - 3600000).toISOString(), - }, - { - path: 'run_summary.txt', - size: 512, - modified: new Date(Date.now() - 3600000).toISOString(), - }, - ], + profiles: [ + { + accelerator: 'cpu', + default_image: 'docker.io/library/python:3.11-slim', + device_unit: 'core', + max_devices: 16, + note: 'Standard CPU runtime', + }, + { + accelerator: 'nvidia', + default_image: 'ghcr.io/utmist/pytorch-cuda:latest', + device_unit: 'GPU', + max_devices: 4, + note: 'NVIDIA CUDA 12.4 + PyTorch runtime', + }, + { + accelerator: 'tenstorrent', + default_image: 'ghcr.io/utmist/tt-metal-runtime:latest', + device_unit: 'board', + max_devices: 4, + note: 'Tenstorrent Wormhole n300 PCIe runtime', + }, ], - ]), + }, + jobLogs: new Map(), + jobFiles: new Map(), +} + +export function applyConfiguration(config) { + if (config.users) state.users = JSON.parse(JSON.stringify(config.users)) + if (config.currentUserIndex !== undefined) state.currentUserIndex = config.currentUserIndex + if (config.activeUserId) { + const idx = state.users.findIndex( + (u) => u.id === config.activeUserId || u.email.toLowerCase() === config.activeUserId.toLowerCase(), + ) + if (idx >= 0) state.currentUserIndex = idx + } + if (config.machines) state.machines = JSON.parse(JSON.stringify(config.machines)) + if (config.pools) state.pools = JSON.parse(JSON.stringify(config.pools)) + if (config.storage) state.storage = JSON.parse(JSON.stringify(config.storage)) + if (config.storageBudgetGiB !== undefined) state.storageBudgetGiB = config.storageBudgetGiB + if (config.teams) state.teams = JSON.parse(JSON.stringify(config.teams)) + if (config.jobs) { + state.jobs = JSON.parse(JSON.stringify(config.jobs)) + for (const job of state.jobs) { + if (!state.jobLogs.has(job.id)) { + state.jobLogs.set( + job.id, + `[${job.created}] Job ${job.name} initialized in state ${job.job_state}\n`, + ) + } + } + } + if (config.datasets) state.datasets = JSON.parse(JSON.stringify(config.datasets)) + if (config.images) state.images = JSON.parse(JSON.stringify(config.images)) + if (config.name) state.activeScenario = config.name +} + +// Determine initial scenario / config +function initializeState() { + const cliScenario = getArg('--scenario') || process.env.MIST_FAKE_SCENARIO + const cliConfig = getArg('--config') || process.env.MIST_FAKE_CONFIG + + if (cliScenario) { + try { + console.log(`[Config] Loading requested scenario: '${cliScenario}'`) + applyConfiguration(getScenario(cliScenario)) + return + } catch (err) { + console.warn(`[Config] Warning: ${err.message}. Falling back to default.`) + } + } + + if (cliConfig && fs.existsSync(cliConfig)) { + try { + console.log(`[Config] Loading configuration from: ${cliConfig}`) + const raw = fs.readFileSync(cliConfig, 'utf-8') + applyConfiguration(JSON.parse(raw)) + return + } catch (err) { + console.warn(`[Config] Failed to load config from ${cliConfig}: ${err.message}`) + } + } + + if (fs.existsSync(DEFAULT_CONFIG_FILE)) { + try { + console.log(`[Config] Found local ${DEFAULT_CONFIG_FILE}, loading...`) + const raw = fs.readFileSync(DEFAULT_CONFIG_FILE, 'utf-8') + applyConfiguration(JSON.parse(raw)) + return + } catch (err) { + console.warn(`[Config] Failed to parse ${DEFAULT_CONFIG_FILE}: ${err.message}`) + } + } + + // Fallback to default scenario + applyConfiguration(getScenario('default')) } -// Automatically progress queued jobs to running and completed +initializeState() + +// Automatically progress queued jobs over time setInterval(() => { for (const job of state.jobs) { if (job.job_state === 'Scheduled') { - job.job_state = 'InProgress' - job.node = - job.accelerator === 'tenstorrent' - ? 'mist-tt-worker-01' - : 'mist-gpu-node-01' - job.message = 'Pod running on ' + job.node - const logs = - state.jobLogs.get(job.id) || - `[${new Date().toISOString()}] Job submitted.\n` - state.jobLogs.set( - job.id, - logs + - `[${new Date().toISOString()}] Admitted to cluster. Initializing pod on ${job.node}...\n`, + // Find suitable node if available + const pool = state.pools.find( + (p) => p.accelerator === job.accelerator && p.ready && (p.available === null || p.available > 0), ) + if (pool) { + job.job_state = 'InProgress' + job.node = pool.node + job.message = `Pod running on ${job.node}` + const logs = state.jobLogs.get(job.id) || `[${new Date().toISOString()}] Job submitted.\n` + state.jobLogs.set( + job.id, + logs + `[${new Date().toISOString()}] Admitted to cluster. Initializing pod on ${job.node}...\n`, + ) + } } else if (job.job_state === 'InProgress') { const elapsed = Date.now() - new Date(job.created).getTime() - if (elapsed > 20000) { - // Complete job after 20s + if (elapsed > 20000 && !job.id.startsWith('job-hl-run')) { job.job_state = 'Success' job.exit_code = 0 job.message = 'Job completed successfully' @@ -291,21 +190,12 @@ setInterval(() => { const logs = state.jobLogs.get(job.id) || '' state.jobLogs.set( job.id, - logs + - `[${new Date().toISOString()}] Execution finished with code 0.\nOutputs written to /outputs.\n`, + logs + `[${new Date().toISOString()}] Execution finished with code 0.\nOutputs saved.\n`, ) if (!state.jobFiles.has(job.id)) { state.jobFiles.set(job.id, [ - { - path: 'output.log', - size: 2048, - modified: new Date().toISOString(), - }, - { - path: 'model_weights.bin', - size: 52428800, - modified: new Date().toISOString(), - }, + { path: 'output.log', size: 2048, modified: new Date().toISOString() }, + { path: 'model_weights.bin', size: 52428800, modified: new Date().toISOString() }, ]) } } @@ -361,8 +251,7 @@ const server = http.createServer(async (req, res) => { res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE, OPTIONS', - 'Access-Control-Allow-Headers': - 'Content-Type, X-Mist-Team, Authorization', + 'Access-Control-Allow-Headers': 'Content-Type, X-Mist-Team, Authorization', 'Access-Control-Max-Age': '86400', }) res.end() @@ -374,7 +263,7 @@ const server = http.createServer(async (req, res) => { const query = Object.fromEntries(parsedUrl.searchParams.entries()) const activeTeam = req.headers['x-mist-team'] || query.team_id || '' - // Normalize path by stripping optional '/api' prefix (Vite proxy rewrites /api, direct hits do not) + // Normalize path by stripping optional '/api' prefix if (pathname.startsWith('/api/')) { pathname = pathname.slice(4) } else if (pathname === '/api') { @@ -384,11 +273,77 @@ const server = http.createServer(async (req, res) => { const currentUser = state.users[state.currentUserIndex] || null try { + // ---------------------------------------------------- + // FAKE BACKEND DYNAMIC CONFIGURATION & SCENARIO CONTROL + // ---------------------------------------------------- + + if (pathname === '/_fake/state' && req.method === 'GET') { + sendJSON(res, 200, { + activeScenario: state.activeScenario, + currentUserIndex: state.currentUserIndex, + currentUser, + users: state.users, + machines: state.machines, + pools: state.pools, + storage: state.storage, + storageBudgetGiB: state.storageBudgetGiB, + teams: state.teams, + jobs: state.jobs, + datasets: state.datasets, + }) + return + } + + if ((pathname === '/_fake/state' || pathname === '/_fake/configure') && req.method === 'POST') { + const body = await parseJSONBody(req) + applyConfiguration(body) + sendJSON(res, 200, { + success: true, + message: 'Configuration applied successfully', + activeScenario: state.activeScenario, + }) + return + } + + if (pathname === '/_fake/scenarios' && req.method === 'GET') { + sendJSON(res, 200, { + scenarios: Object.entries(SCENARIO_DESCRIPTIONS).map(([name, description]) => ({ + name, + description, + active: state.activeScenario === name, + })), + }) + return + } + + const scenarioMatch = pathname.match(/^\/_fake\/scenario\/([^/]+)$/) + if (scenarioMatch && req.method === 'POST') { + const name = scenarioMatch[1] + try { + const scenarioData = getScenario(name) + applyConfiguration(scenarioData) + sendJSON(res, 200, { + success: true, + scenario: name, + message: `Switched to hypothetical scenario: '${name}'`, + }) + } catch (err) { + sendError(res, 400, err.message) + } + return + } + + if (pathname === '/_fake/reset' && req.method === 'POST') { + const defaultData = getScenario('default') + applyConfiguration(defaultData) + sendJSON(res, 200, { success: true, message: 'Reset to default scenario' }) + return + } + // ---------------------------------------------------- // AUTH & SESSION ENDPOINTS // ---------------------------------------------------- - // Session check used by AccountGate if (pathname === '/session' && req.method === 'GET') { sendJSON(res, 200, { enabled: true, @@ -397,7 +352,6 @@ const server = http.createServer(async (req, res) => { return } - // Better Auth: Session retrieval if (pathname === '/auth/get-session' && req.method === 'GET') { if (!currentUser) { sendJSON(res, 200, null) @@ -414,7 +368,6 @@ const server = http.createServer(async (req, res) => { return } - // Better Auth: Email Sign-in if (pathname === '/auth/sign-in/email' && req.method === 'POST') { const body = await parseJSONBody(req) const user = state.users.find( @@ -444,24 +397,20 @@ const server = http.createServer(async (req, res) => { return } - // Better Auth: Sign-out if (pathname === '/auth/sign-out' && req.method === 'POST') { state.currentUserIndex = -1 // signed out sendJSON(res, 200, { success: true }) return } - // Better Auth: Change Password if (pathname === '/auth/change-password' && req.method === 'POST') { await parseJSONBody(req) sendJSON(res, 200, { success: true }) return } - // Better Auth Admin: List Users if ( - (pathname === '/auth/admin/list-users' || - pathname === '/auth/admin/listUsers') && + (pathname === '/auth/admin/list-users' || pathname === '/auth/admin/listUsers') && (req.method === 'GET' || req.method === 'POST') ) { sendJSON(res, 200, { @@ -471,7 +420,6 @@ const server = http.createServer(async (req, res) => { return } - // Better Auth Admin: Create User if (pathname === '/auth/admin/create-user' && req.method === 'POST') { const body = await parseJSONBody(req) if (!body.name || !body.email) { @@ -490,24 +438,18 @@ const server = http.createServer(async (req, res) => { return } - // Better Auth Admin: Set User Password if (pathname === '/auth/admin/set-user-password' && req.method === 'POST') { await parseJSONBody(req) sendJSON(res, 200, { success: true }) return } - // Better Auth Admin: Revoke User Sessions - if ( - pathname === '/auth/admin/revoke-user-sessions' && - req.method === 'POST' - ) { + if (pathname === '/auth/admin/revoke-user-sessions' && req.method === 'POST') { await parseJSONBody(req) sendJSON(res, 200, { success: true }) return } - // Better Auth Admin: Ban / Unban User if (pathname === '/auth/admin/ban-user' && req.method === 'POST') { const body = await parseJSONBody(req) const target = state.users.find((u) => u.id === body.userId) @@ -515,6 +457,7 @@ const server = http.createServer(async (req, res) => { sendJSON(res, 200, { success: true }) return } + if (pathname === '/auth/admin/unban-user' && req.method === 'POST') { const body = await parseJSONBody(req) const target = state.users.find((u) => u.id === body.userId) @@ -530,127 +473,14 @@ const server = http.createServer(async (req, res) => { if (pathname === '/hardware' && req.method === 'GET') { sendJSON(res, 200, { observed_at: new Date().toISOString(), - machines: [ - { - name: 'mist-gpu-node-01', - address: '10.0.1.10', - ready: true, - schedulable: true, - cpu_allocatable: '32', - memory_allocatable: '128Gi', - }, - { - name: 'mist-tt-worker-01', - address: '10.0.1.11', - ready: true, - schedulable: true, - cpu_allocatable: '16', - memory_allocatable: '64Gi', - }, - ], - pools: [ - { - accelerator: 'nvidia', - node: 'mist-gpu-node-01', - unit: 'GPU', - total: 4, - allocated: state.jobs - .filter( - (j) => - j.accelerator === 'nvidia' && j.job_state === 'InProgress', - ) - .reduce((sum, j) => sum + (j.device_count || 1), 0), - available: Math.max( - 0, - 4 - - state.jobs - .filter( - (j) => - j.accelerator === 'nvidia' && - j.job_state === 'InProgress', - ) - .reduce((sum, j) => sum + (j.device_count || 1), 0), - ), - ready: true, - }, - { - accelerator: 'tenstorrent', - node: 'mist-tt-worker-01', - unit: 'board', - total: 4, - allocated: state.jobs - .filter( - (j) => - j.accelerator === 'tenstorrent' && - j.job_state === 'InProgress', - ) - .reduce((sum, j) => sum + (j.device_count || 1), 0), - available: Math.max( - 0, - 4 - - state.jobs - .filter( - (j) => - j.accelerator === 'tenstorrent' && - j.job_state === 'InProgress', - ) - .reduce((sum, j) => sum + (j.device_count || 1), 0), - ), - ready: true, - chips_per_device: 2, - }, - ], + machines: state.machines, + pools: state.pools, }) return } if (pathname === '/images' && req.method === 'GET') { - sendJSON(res, 200, { - self_service: true, - teams_enabled: true, - registries: ['docker.io', 'ghcr.io', 'quay.io', 'localhost:5000'], - images: [ - { - reference: 'docker.io/library/python:3.11-slim', - accelerators: ['cpu'], - }, - { - reference: 'docker.io/library/pytorch-cpu:2.2.0', - accelerators: ['cpu'], - }, - { - reference: 'ghcr.io/utmist/pytorch-cuda:latest', - accelerators: ['nvidia'], - }, - { - reference: 'ghcr.io/utmist/tt-metal-runtime:latest', - accelerators: ['tenstorrent'], - }, - ], - profiles: [ - { - accelerator: 'cpu', - default_image: 'docker.io/library/python:3.11-slim', - device_unit: 'core', - max_devices: 16, - note: 'Standard CPU runtime', - }, - { - accelerator: 'nvidia', - default_image: 'ghcr.io/utmist/pytorch-cuda:latest', - device_unit: 'GPU', - max_devices: 4, - note: 'NVIDIA CUDA 12.4 + PyTorch runtime', - }, - { - accelerator: 'tenstorrent', - default_image: 'ghcr.io/utmist/tt-metal-runtime:latest', - device_unit: 'board', - max_devices: 4, - note: 'Tenstorrent Wormhole n300 PCIe runtime', - }, - ], - }) + sendJSON(res, 200, state.images) return } @@ -877,16 +707,7 @@ const server = http.createServer(async (req, res) => { // ---------------------------------------------------- if (pathname === '/storage' && req.method === 'GET') { - sendJSON(res, 200, { - enabled: true, - capacity_bytes: 1099511627776, // 1 TiB - available_bytes: 879609302220, // 800 GiB - model_capacity_bytes: 2199023255552, // 2 TiB - model_available_bytes: 1759218604441, // 1.6 TiB - dataset_allocation_gib: 500, - model_allocation_gib: 1000, - upload_limit_bytes: 5368709120, // 5 GiB - }) + sendJSON(res, 200, state.storage) return } @@ -951,12 +772,11 @@ const server = http.createServer(async (req, res) => { } sendJSON(res, 200, { datasets: filtered, - upload_limit_bytes: 5368709120, + upload_limit_bytes: state.storage.upload_limit_bytes || 5368709120, }) return } - // Dataset upload (streaming / raw binary or multipart) if (pathname === '/datasets' && req.method === 'POST') { const filename = query.filename || 'dataset.bin' const name = query.name || filename @@ -1029,13 +849,11 @@ const server = http.createServer(async (req, res) => { return } - // Health check if (pathname === '/healthz' && req.method === 'GET') { sendJSON(res, 200, { status: 'ok', executor: 'fake-backend' }) return } - // 404 Fallback sendError(res, 404, `Endpoint not found: ${req.method} ${pathname}`) } catch (err) { console.error('[Fake Backend Error]', err) @@ -1046,10 +864,12 @@ const server = http.createServer(async (req, res) => { server.listen(PORT, HOST, () => { console.log(`======================================================`) console.log(`Mist Fake Backend running at http://${HOST}:${PORT}`) - console.log(`Ready for web-interface testing!`) - console.log(`- Simulated User: ${state.users[state.currentUserIndex]?.name} (${state.users[state.currentUserIndex]?.role})`) - console.log(`- Teams: ${state.teams.map((t) => t.name).join(', ')}`) - console.log(`- Jobs: ${state.jobs.length} pre-populated`) + console.log(`Active Scenario: '${state.activeScenario}'`) + console.log(`Simulated User: ${state.users[state.currentUserIndex]?.name} (${state.users[state.currentUserIndex]?.role})`) + console.log(`Machines: ${state.machines.map((m) => m.name).join(', ')}`) + console.log(`Teams: ${state.teams.map((t) => t.name).join(', ')}`) + console.log(`Jobs: ${state.jobs.length} in history`) + console.log(`Run 'node scripts/configure-fake-backend.mjs' to modify`) console.log(`======================================================`) }) diff --git a/web-interface/package.json b/web-interface/package.json index 5200d90..0783ac3 100644 --- a/web-interface/package.json +++ b/web-interface/package.json @@ -15,6 +15,7 @@ "test": "vitest run", "lint": "eslint", "fake-backend": "node ../scripts/fake-backend.mjs", + "configure-fake-backend": "node ../scripts/configure-fake-backend.mjs", "format": "prettier --check .", "check": "prettier --write . && eslint --fix" }, From 6f22c8de1172f0932a7f3c53dab4fda124c36bce Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Fri, 16 Oct 2026 22:51:10 -0400 Subject: [PATCH 3/7] feat: favicons --- web-interface/public/favicon.ico | Bin 3870 -> 152126 bytes web-interface/public/logo192.png | Bin 5347 -> 13841 bytes web-interface/public/logo512.png | Bin 9664 -> 41025 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/web-interface/public/favicon.ico b/web-interface/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..bd4829290a12df81098f85c3a9027defd44ee06b 100644 GIT binary patch literal 152126 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 literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/web-interface/public/logo192.png b/web-interface/public/logo192.png index fc44b0a3796c0e0a64c3d858ca038bd4570465d9..51ffbf36b4f75a4b32b3f2e619396a3f2a6430c4 100644 GIT binary patch literal 13841 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 F{{iqwvS|PS literal 5347 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

GlY;rU&kOIi~nsbIDAvX(qxtWO8t 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN From 5d3e0702eba961cb30e2ceb46bef5494439f13f4 Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Fri, 16 Oct 2026 22:54:11 -0400 Subject: [PATCH 4/7] fix(fake-backend): resolve config file relative to project root --- scripts/configure-fake-backend.mjs | 4 +++- scripts/fake-backend.mjs | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/configure-fake-backend.mjs b/scripts/configure-fake-backend.mjs index f5a8027..4796596 100644 --- a/scripts/configure-fake-backend.mjs +++ b/scripts/configure-fake-backend.mjs @@ -18,13 +18,15 @@ import path from 'node:path' import http from 'node:http' import readline from 'node:readline/promises' import { stdin as input, stdout as output } from 'node:process' +import { fileURLToPath } from 'node:url' import { getScenario, SCENARIO_DESCRIPTIONS, SCENARIO_FACTORIES, } from './fake-backend-scenarios.mjs' -const CONFIG_FILE = path.resolve(process.cwd(), '.fake-backend-config.json') +const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') +const CONFIG_FILE = path.resolve(ROOT_DIR, '.fake-backend-config.json') // Parse CLI flags const argv = process.argv.slice(2) diff --git a/scripts/fake-backend.mjs b/scripts/fake-backend.mjs index 00d33f5..d267623 100644 --- a/scripts/fake-backend.mjs +++ b/scripts/fake-backend.mjs @@ -17,7 +17,7 @@ import http from 'node:http' import fs from 'node:fs' import path from 'node:path' -import { URL } from 'node:url' +import { URL, fileURLToPath } from 'node:url' import crypto from 'node:crypto' import { getScenario, @@ -36,7 +36,8 @@ const PORT = Number( getArg('--port') || process.env.PORT || process.env.MIST_FAKE_PORT || 3000, ) const HOST = process.env.HOST || '127.0.0.1' -const DEFAULT_CONFIG_FILE = path.resolve(process.cwd(), '.fake-backend-config.json') +const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') +const DEFAULT_CONFIG_FILE = path.resolve(ROOT_DIR, '.fake-backend-config.json') // --- Initial State Management --- From 573a32b2400dfcab2909fd3901ad97346deae9d2 Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Fri, 16 Oct 2026 22:59:03 -0400 Subject: [PATCH 5/7] feat: svg for favicon --- web-interface/public/favicon.svg | 150 +++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 web-interface/public/favicon.svg diff --git a/web-interface/public/favicon.svg b/web-interface/public/favicon.svg new file mode 100644 index 0000000..a526ef2 --- /dev/null +++ b/web-interface/public/favicon.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + M + + From 3ab09cc17e6b09823a6d1817c0cc8fb5bf0491d0 Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Fri, 16 Oct 2026 23:04:21 -0400 Subject: [PATCH 6/7] feat: readme --- README.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9d0f2f8..468d0e6 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,17 @@ UTMIST's research compute platform. Submit Python scripts or tagged container images, choose CPU, NVIDIA GPUs or Tenstorrent boards, upload datasets, and collect persistent results through the same website or CLI. -**Private website:** http://100.73.139.66:8088, accessible through authorized +**Private website:** [http://100.73.139.66:8088](http://100.73.139.66:8088), accessible through authorized Tailscale connectivity. A Mist account and team membership are also required. ## Current installation -| Machine | Role | LAN address | Tailscale address | -|---|---|---|---| -| `utmist-z1opa08` | k3s server, API/auth, website, CPU and two RTX A4000 GPUs | `10.0.0.175` | `100.73.139.66` | -| `utmist-tt` | k3s worker, four n300 boards/eight chips, shared storage | `10.0.0.112` | `100.95.175.37` | + +| Machine | Role | LAN address | Tailscale address | +| ---------------- | --------------------------------------------------------- | ------------ | ----------------- | +| `utmist-z1opa08` | k3s server, API/auth, website, CPU and two RTX A4000 GPUs | `10.0.0.175` | `100.73.139.66` | +| `utmist-tt` | k3s worker, four n300 boards/eight chips, shared storage | `10.0.0.112` | `100.95.175.37` | + The cluster and NFS use the existing LAN. Tailscale provides private portal and administrator SSH access. Kubernetes/containerd run workload containers. @@ -60,3 +62,21 @@ chaining, browser image archives/builds, private pull-credential management and distributed multi-machine training are outside this release. Automatic backups, HTTPS and monitoring/cleanup products were explicitly excluded. This is a private HTTP installation with one control plane, one auth instance and one storage host. + +## Frontend Testing + +how to use fake backend for testing frontend. + +(temporary, functionality, to be removed in future commit.) + +```bash +# Terminal 1 +npm --prefix web-interface run dev -- --host 127.0.0.1 --port 3001 --strictPort + +# Terminal 2 +npm --prefix web-interface run fake-backend +``` + + + +  \ No newline at end of file From 474c787605b5a92545e143dbde920479d5a465db Mon Sep 17 00:00:00 2001 From: andrei-akopian Date: Tue, 20 Oct 2026 09:45:49 -0400 Subject: [PATCH 7/7] docs: agents.md and instructions --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 +++ AGENTS.md | 9 +++++++++ README.md | 6 ++++++ 4 files changed, 18 insertions(+) delete mode 100644 .DS_Store create mode 100644 AGENTS.md diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 6df9d253a2aae9e0f803c35c0ecf0c9a0108d468..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK-AcnS6i(c98AIrW!Y%{e4&09nhc~6p7qFriDzl|Si?tbR=Pt&e*ZM*}iO=IX zNeT{oE#l6BM`ap#soA(j!K1~yE3$6lMy+NkCjK!D(;Zk*iU^+mR=O z;dRP7Z8q*|%82J1BNFul_tuRQd>>GP}$oBnF59Vt^RfZU*eR zAX?i^1FfDIAO6T?p*=uD!Mp+$5YRU+0bt-h($+x}m#9OYYq2nhqo7}x Q1JXr66G9y^@Cyul0r>VwYybcN diff --git a/.gitignore b/.gitignore index f456dba..f130b04 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ __pycache__/ data/ .direnv/ .fake-backend-config.json + +# OS files +.DS_Store \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6baa25e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +This is a project designing the compute platform for UTMIST (University of Toronto Machine Intelligence Student Team). The target cluster is two computers, one with tenstorrent gpu and one with nvidia gpus. + +## Documentation + +Agents are allowed to edit the README and top level markdown files as well as docs/agents, but shouldn't touch docs/humans as those are by humans for humans (though agents may notify humans if they believe human docs are severely outdated). + +## Temporary Decisions + +Make the frontend lightweight and move logic to backend. We aren't short on bandwidth, so it is fine to send a new qpi request and refresh UI every once in a while. \ No newline at end of file diff --git a/README.md b/README.md index 468d0e6..ad6502a 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,12 @@ distributed multi-machine training are outside this release. Automatic backups, HTTPS and monitoring/cleanup products were explicitly excluded. This is a private HTTP installation with one control plane, one auth instance and one storage host. +### Documentation + +Humans are responsible for editing the human facing docs. This is because the human facing docs will be read by humans, and it is rude to give them untested instructions to follow. + +Documentation should include explanations of design decisions for later platform maintainers. + ## Frontend Testing how to use fake backend for testing frontend.