From ec8f8578458fac315f828de9160a1d283ec27e1a Mon Sep 17 00:00:00 2001 From: ginnoir Date: Thu, 11 Jun 2026 17:06:52 -0500 Subject: [PATCH] feat: bootstrap walking skeleton --- .claude/launch.json | 11 + .gitignore | 22 + .nvmrc | 1 + AGENTS.md | 87 + CLAUDE.md | 10 + LICENSE.md | 7 + README.md | 43 + biome.json | 26 + docs/adr/0001-tech-stack.md | 33 + docs/architecture.md | 58 + docs/plans/2026-06-11-m0-bootstrap.md | 163 + index.html | 14 + package.json | 51 + pnpm-lock.yaml | 4767 +++++++++++++++++++++++ public/pwa-192.png | Bin 0 -> 4076 bytes public/pwa-512.png | Bin 0 -> 12268 bytes src/content/.gitkeep | 2 + src/content/__tests__/schema.test.ts | 47 + src/content/definitions.ts | 17 + src/content/index.ts | 7 + src/content/schema.ts | 66 + src/engine/__tests__/game.test.ts | 76 + src/engine/__tests__/num.test.ts | 49 + src/engine/__tests__/save.test.ts | 108 + src/engine/__tests__/tickLoop.test.ts | 76 + src/engine/game.ts | 55 + src/engine/num.ts | 57 + src/engine/save.ts | 98 + src/engine/tickLoop.ts | 73 + src/index.css | 12 + src/main.tsx | 15 + src/state/.gitkeep | 1 + src/state/__tests__/persistence.test.ts | 56 + src/state/__tests__/viewModel.test.ts | 64 + src/state/persistence.ts | 117 + src/state/runtime.ts | 120 + src/state/store.ts | 26 + src/state/viewModel.ts | 55 + src/ui/ActionPanel.tsx | 38 + src/ui/App.tsx | 27 + src/ui/EventLog.tsx | 24 + src/ui/ResourceBar.tsx | 27 + tsconfig.json | 28 + vite.config.ts | 43 + 44 files changed, 6677 insertions(+) create mode 100644 .claude/launch.json create mode 100644 .gitignore create mode 100644 .nvmrc create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 biome.json create mode 100644 docs/adr/0001-tech-stack.md create mode 100644 docs/architecture.md create mode 100644 docs/plans/2026-06-11-m0-bootstrap.md create mode 100644 index.html create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 public/pwa-192.png create mode 100644 public/pwa-512.png create mode 100644 src/content/.gitkeep create mode 100644 src/content/__tests__/schema.test.ts create mode 100644 src/content/definitions.ts create mode 100644 src/content/index.ts create mode 100644 src/content/schema.ts create mode 100644 src/engine/__tests__/game.test.ts create mode 100644 src/engine/__tests__/num.test.ts create mode 100644 src/engine/__tests__/save.test.ts create mode 100644 src/engine/__tests__/tickLoop.test.ts create mode 100644 src/engine/game.ts create mode 100644 src/engine/num.ts create mode 100644 src/engine/save.ts create mode 100644 src/engine/tickLoop.ts create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/state/.gitkeep create mode 100644 src/state/__tests__/persistence.test.ts create mode 100644 src/state/__tests__/viewModel.test.ts create mode 100644 src/state/persistence.ts create mode 100644 src/state/runtime.ts create mode 100644 src/state/store.ts create mode 100644 src/state/viewModel.ts create mode 100644 src/ui/ActionPanel.tsx create mode 100644 src/ui/App.tsx create mode 100644 src/ui/EventLog.tsx create mode 100644 src/ui/ResourceBar.tsx create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..fe06ee3 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "dev", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["dev", "--port", "5180", "--strictPort"], + "port": 5180 + } + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..962cb5d --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Dependencies +node_modules/ +.pnpm-store/ + +# Build output +dist/ +dev-dist/ +*.tsbuildinfo + +# Test / coverage +coverage/ + +# Editor / OS +.DS_Store +*.local +.claude/settings.local.json +.vite/ + +# Logs +*.log +npm-debug.log* +pnpm-debug.log* diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6d5a84b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# Idlegame Agent Guide + +Idlegame is a proprietary idle/incremental text-fantasy RPG inspired by Your +Chronicle. It is a responsive web + PWA project with a pure TypeScript game +engine and a React/Zustand view layer. + +## User And Context + +- Address the user as **ginnoir** or **senpai**. Never call him Matt. +- The Obsidian vault is the cross-project source of truth for design, story, + decisions, and session memory. +- Vault MCP path: `claude/Context.md` first, then `Idlegame/_Claude.md`. +- Local vault path for tools without MCP: `C:\Users\MattC\Documents\Obsidian Vault\`. +- Credentials used by AI tools live in the vault at `claude/Credentials.md`. + Never hardcode tokens or secrets in this repo. +- Repo docs are code-truth: setup, architecture, ADRs, and plans. Vault notes + hold design intent and durable operational context. + +## Commands + +- Install: `pnpm install` +- Dev server: `pnpm dev` +- Typecheck: `pnpm typecheck` +- Lint/format check: `pnpm lint` +- Format: `pnpm format` +- Unit tests: `pnpm test` +- Coverage: `pnpm test:coverage` +- Production build: `pnpm build` + +The required pre-PR verification chain is: + +```powershell +pnpm typecheck +pnpm lint +pnpm test:coverage +pnpm build +``` + +## Architecture Rules + +- `src/engine/` is pure TypeScript. It must not import React, Zustand, browser + APIs, storage APIs, or wall-clock scheduling. +- `src/content/` contains data-driven resource/action/story definitions and + Zod validation. +- `src/state/` bridges engine snapshots into Zustand and owns environment + coupling such as requestAnimationFrame, IndexedDB, localStorage fallback, and + lifecycle hooks. +- `src/ui/` is the React shell. It renders store snapshots and calls runtime + commands; gameplay rules stay out of components. +- Saves are versioned and validated at the boundary. Invalid or tampered saves + must fail explicitly. +- Numbers remain human-readable; use the `src/engine/num.ts` boundary before + introducing any Decimal-style library. + +## Engineering Norms + +- TypeScript strict, immutable-by-default, explicit errors at boundaries. +- Keep functions small and modules focused. Prefer many small files over large + catch-all modules. +- Tests are required for new engine behavior. Maintain at least 80% coverage on + `src/engine/`. +- Use permissive dependencies only. No GPL dependencies; this game is + proprietary and may be sold. +- Commit messages use Conventional Commits, with no AI attribution. + +## Tool Routing + +- Claude Code: architecture, engine-system design, story/design writing, review, + vault stewardship, homelab/infra work. +- Codex: scoped implementation tickets, debugging, coverage work, mechanical + refactors. +- Antigravity: UI implementation, responsive polish, browser/playtest + verification, visual checks. +- Cursor: small ginnoir-driven edits and exploratory pair-programming. + +Use one tool per issue or branch at a time. After M0, all code changes should +land through a PR unless ginnoir explicitly says otherwise. + +## Workflow + +- Default Git host: private Gitea at `gitea.ginnoir.com/ginnoir/idlegame`. +- Trunk is `main`; feature work happens on branches with PRs. +- Gitea Issues are implementation work items. Plane epics are roadmap grouping. +- Internal playtest URL: `idlegame.ginnoir.com` via Caddy `internal_only`. + +When a session produces durable knowledge, write it back to +`Idlegame/_Claude.md` or a topic note in the vault before ending. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..010422d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,10 @@ +@AGENTS.md + +# Claude-Specific Notes + +- Use the Superpowers workflow for feature work: brainstorm when shaping + behavior, write/execute plans for multi-step work, and use TDD for engine + changes. +- Start each session by reading `claude/Context.md` and `Idlegame/_Claude.md` + from the Obsidian vault. +- Keep design/story decisions in the vault, not in repo-only notes. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e62264f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +# Proprietary License Notice + +Copyright (c) 2026 ginnoir. All rights reserved. + +This repository and its contents are proprietary. No license is granted to copy, +modify, distribute, sublicense, or use the code or assets except with explicit +written permission from the copyright holder. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1557330 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Idlegame + +Idle/incremental text-fantasy RPG, built as a responsive web app and PWA. + +## Stack + +- TypeScript strict +- Vite + React 19 +- Zustand for view state +- Pure TypeScript engine under `src/engine` +- Zod-validated content and saves +- Biome, Vitest, Tailwind v4, vite-plugin-pwa + +## Development + +```powershell +pnpm install +pnpm dev +``` + +Before pushing a branch: + +```powershell +pnpm typecheck +pnpm lint +pnpm test:coverage +pnpm build +``` + +## Current Scope + +M0 is a walking skeleton: one resource, one timed action, fixed-timestep engine, +versioned saves, offline credit, persistence, and a minimal React shell. M1 adds +the first playable vertical slice. + +## Documentation + +- Architecture: `docs/architecture.md` +- Tech stack ADR: `docs/adr/0001-tech-stack.md` +- M0 plan: `docs/plans/2026-06-11-m0-bootstrap.md` + +Design, story, decisions, credentials, and durable session memory live in the +Obsidian vault under `Idlegame/`, not in this repository. diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..e9b5a39 --- /dev/null +++ b/biome.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.16/schema.json", + "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "files": { "ignoreUnknown": true }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "linter": { + "enabled": true, + "rules": { "recommended": true } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "always", + "trailingCommas": "all" + } + }, + "assist": { + "enabled": true, + "actions": { "source": { "organizeImports": "on" } } + } +} diff --git a/docs/adr/0001-tech-stack.md b/docs/adr/0001-tech-stack.md new file mode 100644 index 0000000..46a5975 --- /dev/null +++ b/docs/adr/0001-tech-stack.md @@ -0,0 +1,33 @@ +# ADR 0001: Web Stack And Engine Boundary + +## Status + +Accepted, 2026-06-11. + +## Context + +Idlegame is a text-first idle/incremental RPG. The UI is mostly panels, progress +bars, action lists, resources, logs, and branching story choices. It must run +well on phones from day one and remain easy to wrap later for desktop or mobile +stores. + +## Decision + +Use TypeScript strict, Vite, React 19, Zustand, Tailwind v4, Vitest, Biome, and +vite-plugin-pwa. + +Keep gameplay rules in a pure TypeScript engine under `src/engine/`. React, +Zustand, browser storage, requestAnimationFrame, and lifecycle hooks belong +outside the engine. + +Use plain JavaScript numbers behind `src/engine/num.ts` for M0/M1. Human-readable +values are a design pillar; a Decimal-style dependency is deferred until a later +layer proves it is needed. + +## Consequences + +- Engine behavior is headless-testable with Vitest. +- UI and wrapper changes should not force gameplay rewrites. +- Offline progress uses the same fixed-timestep simulation path as online play. +- Save/load validation sits at explicit boundaries with versioned schemas. +- Dependencies must be permissive; no GPL packages are allowed. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..86fa5c5 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,58 @@ +# Architecture + +Idlegame is split into four layers. + +## Engine + +`src/engine/` contains deterministic gameplay logic: + +- `tickLoop.ts`: fixed-timestep accumulator. Large elapsed deltas, including + offline catch-up, drain through the same tick path as active play. +- `game.ts`: core game state, active action progress, and resource accrual. +- `save.ts`: versioned save schema, serialized export/import strings, and + offline elapsed calculation. +- `num.ts`: branded numeric boundary and human-readable formatting. + +The engine must stay pure. It does not import React, Zustand, browser APIs, +storage, requestAnimationFrame, or Date scheduling. + +## Content + +`src/content/` contains authored definitions validated by Zod. M0 includes one +resource and one timed action. M1 expands this into real resources, actions, +story nodes, automation unlocks, and prestige definitions. + +## State + +`src/state/` owns environment coupling: + +- persistence backend selection: IndexedDB via `idb-keyval`, with localStorage + fallback +- load-on-boot and autosave orchestration +- requestAnimationFrame loop and lifecycle hooks +- mapping engine state to view models +- Zustand store updates for React + +## UI + +`src/ui/` renders the view model and calls runtime commands. Components should +not implement gameplay rules. The M0 shell includes a resource readout, action +panel, progress bar, and event log. + +## Verification + +Run the full local gate before pushing: + +```powershell +pnpm typecheck +pnpm lint +pnpm test:coverage +pnpm build +``` + +Rendered checks for the walking skeleton: + +- app loads without framework overlay or console errors +- starting the timed action increments Gold after completion +- reload preserves saved state +- reopening after time away credits offline progress diff --git a/docs/plans/2026-06-11-m0-bootstrap.md b/docs/plans/2026-06-11-m0-bootstrap.md new file mode 100644 index 0000000..13e2f85 --- /dev/null +++ b/docs/plans/2026-06-11-m0-bootstrap.md @@ -0,0 +1,163 @@ +# Idlegame — M0 Bootstrap Plan (handoff artifact for superpowers) + +> **How to use this file:** the interview/brainstorm phase is complete; everything below is locked. Hand this plan to superpowers for execution (this copy in the working dir at `docs/plans/2026-06-11-m0-bootstrap.md` is canonical). Tasks are bite-sized with per-task verification so `executing-plans` can run them sequentially. Feature work after M0 flows through superpowers' brainstorm → plan → TDD cycle per feature. +> +> **Refined 2026-06-11 (same-day review pass, approved by ginnoir):** T3 split into T3.1–T3.4; T8 deploy auth + Caddy mechanics specified (romhacks-wiki precedent); Node policy `engines >=24` / CI on 24 LTS; pnpm 10 postinstall allowlist note; CI-required protection toggle moved from T6 to T7. No design decisions re-opened. Review-gate addition: per-task tool + model routing for M0 (§AI tool routing). + +## Context + +ginnoir is starting a commercial-grade idle/incremental game: a text fantasy RPG inspired by **Your Chronicle** (timed actions, resource generate/consume loops, story objectives), structured like a visual novel with branching choices, ending runs in a prestige system that avoids same-y repeats. Monetization, if ever: upfront purchase or cosmetics only — QoL and speed-up are never paywalled; any process completed once becomes automatable. + +**M0 scope**: capture the locked design in the vault, stand up the multi-tool workflow (Gitea + CI + internal playtest deploys + Plane + Obsidian), and scaffold a walking-skeleton codebase. Gameplay (M1 vertical slice) is seeded as Gitea issues and built in later sessions. + +Starting state: `C:\Users\MattC\Documents\idlegame` is empty except `docs/plans/`, not a git repo. Gitea runs at `gitea.ginnoir.com` (owner `ginnoir`, private default, HTTPS creds in GCM). No Gitea Actions runner exists yet. Plane is self-hosted on valhalla. Obsidian vault is the cross-project source of truth. Superpowers drives development workflow; all four AI tools (Claude Code, Codex, Antigravity, Cursor) get Obsidian MCP + local vault access. + +## Locked design pillars (interview, 2026-06-11) + +| Pillar | Decision | +|---|---| +| Platform | Responsive web + PWA; plays well on phones from day one. Steam (Tauri/Electron) and app-store (Capacitor) wraps are later milestones | +| Visuals | Text-first UI: panels, progress bars, action lists, event log. No art pipeline | +| Combat | Light/abstracted — timed actions with stat checks and risk/reward; no battle screen in v1 | +| First playable (M1) | Vertical slice: one short story arc → first prestige reset; branching choice; automation unlock; offline progress; save/load | +| Prestige | **Hybrid**: aggressive catch-up *within* a layer (Magic Research style — keep knowledge, blast through known content); genuinely new mechanics *between* layers (Antimatter Dimensions style) | +| Numbers | Human-readable throughout (≤ billions). No scientific-notation spectacle | +| Pacing | Semi-active (Your Chronicle-like): queue actions, choices every few minutes; idles/offline fine | +| Story authoring | Outline together in Obsidian; Claude drafts prose; ginnoir reviews/edits | +| Automation principle | Complete a process once → it becomes automatable (generous, progression-gated) | +| CI/CD | Gitea Actions runner on valhalla; typecheck/lint/test/build on push; auto-deploy dev build to internal-only playtest URL | +| Tracking | Plane = roadmap/epics. Gitea Issues = code-level tasks (commit/PR auto-linking) | +| Codename | `idlegame` (rename later is cheap in Gitea) | + +## Tech stack (researched recommendation) + +**TypeScript (strict) + Vite + React 19 + Zustand, with the game engine as a pure-TS module that never imports React.** + +- Genre precedent: every successful game in this genre is a web app — Antimatter Dimensions (Vue), Magic Research (React), Melvor Idle (web, wrapped for Steam/mobile). Text/UI-heavy idle games are DOM games; Godot/Unity fight you on UI and bloat web exports. +- React over Svelte/Vue: deepest fluency across all four AI tools — important when agents write most of the code. Perf is a non-issue with the core/view split: engine ticks on its own loop; UI subscribes to snapshots at ~10 fps via Zustand selectors. +- The engine/view split is the load-bearing decision: headless-testable game core (Vitest, no DOM); Tauri/Capacitor wraps or even a framework swap later touch only the view. +- **Considered & rejected**: Profectus (MIT, Vue, Modding-Tree lineage, ~34 stars — license fine but Vue-locked, shaped for tree-prestige games, community too small for a commercial bet); The Modding Tree (older JS, same shape); Godot (canvas UI friction for a text game). + +Dependencies (all permissive; the game stays proprietary — no GPL anywhere): + +| Dep | License | Role | +|---|---|---| +| react, zustand | MIT | view + state bridge | +| zod | MIT | schema validation for content definitions and saves | +| lz-string | MIT | compressed save export/import strings | +| idb-keyval | Apache-2.0 | IndexedDB persistence (localStorage fallback) | +| vite, vite-plugin-pwa, workbox | MIT | build + installable PWA | +| tailwindcss v4 | MIT | styling for the text-first UI | +| biome, vitest, (playwright later) | MIT/Apache-2.0 | lint+format, unit tests, E2E later | + +Toolchain: **pnpm**, **Node 24 LTS as the CI/runtime baseline** — `engines: { "node": ">=24" }` (a floor, not an exact pin: local boxes may run newer — Node 25.9 verified working 2026-06-11; a strict `24.x` pin makes every local `pnpm install` warn), `.nvmrc` = `24`, and CI jobs run a `node:24` image so the LTS pin is enforced where determinism matters. Numbers: plain JS numbers behind `src/engine/num.ts` (format + branded type); ADR documents the Decimal escape hatch if a late prestige layer ever needs it. + +## Workflow architecture + +- **Repo**: `gitea.ginnoir.com/ginnoir/idlegame`, private. Trunk `main` (protected, CI required), feature branches + PRs, conventional commits, self-merge allowed (solo + AI dev). +- **Issues**: Gitea labels (`type/feat|fix|content|infra`, `area/engine|ui|story|workflow`, `prio/1-3`), milestones `M0 Scaffold`, `M1 Vertical Slice`. Issue templates (feature/bug/content) + PR template in `.gitea/`. +- **Plane**: project "Idlegame" with epics (Vertical Slice, Workflow & Infra, Game Design). Epics link Gitea issues by URL; day-to-day work lives in Gitea. +- **CI** (`.gitea/workflows/ci.yml`): pnpm install → `typecheck` → `biome ci` → `vitest run --coverage` → `build` → upload `dist` artifact. +- **Deploy** (on push to `main`): rsync `dist/` over SSH (dedicated deploy key stored as repo Actions secrets) to valhalla's Caddy site dir `/config/caddy/site/idlegame/` → `idlegame.ginnoir.com`, **internal-only during development** (Caddy `import internal_only` — same pattern as other internal sites; no auth layer needed). Precedent: romhack-archive-site static deploy (`/config/caddy/site/romhacks-wiki`). +- **Runner**: `act_runner` container (docker mode, outbound polling only) added to homelabstack's `dev` stack, following that repo's conventions — read `Homelab/_Claude.md` + `Deploy.md` in the vault before touching homelabstack (applies to T7 **and** T8). + +## Cross-tool agent layer + +- **`AGENTS.md` is canonical** — Linux Foundation standard; Codex, Cursor, and Antigravity read it natively. Contents: project overview, commands, architecture map, conventions (commits, PR flow, engine/view boundary rules), the context-layer contract, and the tool-routing table below. +- **`CLAUDE.md`** = `@AGENTS.md` import + Claude-only extras (superpowers notes, vault skill pointers). +- **Context-layer contract** (stated in AGENTS.md): Obsidian vault (all tools have MCP + the local folder `C:\Users\MattC\Documents\Obsidian Vault\`; record both in AGENTS.md) = design docs, story, decisions, session memory. **API tokens tools authenticate with live in the vault at `claude/Credentials.md`** — never in the repo. Repo `docs/` = code-truth (README, ADRs, architecture, superpowers plans in `docs/plans/`). Gitea Issues = work items. Every tool writes session outcomes back to the vault. + +## AI tool routing (light) + +One tool per issue/branch at a time; everything lands via PR; AGENTS.md + vault are the shared brain regardless of tool. + +| Tool | Best at | Route these tasks | +|---|---|---| +| **Claude Code** (+ superpowers) | Architecture, multi-step planning, nuanced writing, big multi-file features, code review | Engine/systems design, prestige & balance design, story outlining + prose drafts, milestone planning, reviewing other tools' PRs, vault stewardship | +| **Codex** | Long autonomous runs on well-scoped tasks, gnarly debugging, test discipline | Well-scoped implementation tickets, bug hunts with repro steps, test-coverage passes, mechanical refactors | +| **Antigravity** | Frontend work with built-in browser verification, multimodal/screenshot feedback, large-context research | UI implementation + polish, responsive/mobile layout passes, visual verification of the playtest build, competitive research (other idle games) | +| **Cursor** | Interactive pair-programming, fast small edits while ginnoir drives; agent mode now frontier-class (Composer 2.5 ≈ Opus 4.8 on benchmarks, June 2026) | Quick fixes, small scoped tweaks, exploratory fiddling, anything ginnoir wants to steer by hand; credible alternate for Codex-lane scoped tickets | + +Rule of thumb: design and words → Claude; heads-down scoped code → Codex; anything you need to *see* → Antigravity; anything you're driving yourself → Cursor. + +### Per-task routing (M0) + +Applies the rules above to T1–T10. Claude Code legitimately dominates M0 — it's workflow/infra/writing-heavy by design; Codex and Antigravity take the tasks where they genuinely win. **Cursor has no M0 assignment** (its lane is ginnoir-driven interactive edits; M0 runs autonomously — Cursor's turn starts at M1). **Default execution mode is unchanged**: a single superpowers/Claude Code `executing-plans` run per the header; the table says who's *best* per task — route T2/T3.2/T3.3/T3.4 out to Codex/Antigravity only if ginnoir wants to shake down the multi-tool workflow during M0. For M1+ feature work the table's logic is the standing template. + +**Claude Code primary is Opus 4.8** — Fable 5 access ends **2026-06-22**, so nothing durable relies on it. ⭐F marks the tasks that benefit most from Fable 5 while it lasts (the highest-judgment writing and architecture calls); if M0 executes before the cutoff, running the whole thing on Fable 5 is fine. + +| Task | Tool | Model | Why | +|---|---|---|---| +| T1 vault notes | Claude Code | Opus 4.8 ⭐F | Vault stewardship; GDD/story prose quality compounds downstream | +| T2 scaffold | Codex | GPT-5.5-Codex (medium) | Well-scoped ticket, command-line verification, gotchas pre-spelled-out; alt: Cursor agent (Composer 2.5) | +| T3.1 num + tick loop | Claude Code | Opus 4.8 ⭐F | Load-bearing engine architecture: API shape + TDD, not heads-down typing | +| T3.2 resource + action | Codex | GPT-5.5-Codex (medium) | Scoped code on shapes T3.1 fixed; fully test-gated | +| T3.3 saves + persistence | Codex | GPT-5.5-Codex (highest effort tier) | Save integrity = the data-loss surface; strongest test discipline | +| T3.4 Zustand + React shell | Antigravity | Gemini 3 Pro (high thinking) | Browser verification watches the progress bar, reload, offline credit | +| T4 AGENTS.md + docs | Claude Code | Opus 4.8 ⭐F | The conventions doc every other tool reads | +| T5 git + Gitea repo | Claude Code | Sonnet 4.6¹ | API glue with vault-held creds; mechanical | +| T6 repo config | Claude Code | Sonnet 4.6¹ | Labels/protection/templates via API | +| T7 Actions runner | Claude Code | Opus 4.8 | Touches the live homelab stack; vault conventions + care | +| T8 playtest deploy | Claude Code | Opus 4.8 | Secrets, Caddy, cross-repo infra; Antigravity optional for browser/PWA verify-assist | +| T9 Plane + M1 seeding | Claude Code | Opus 4.8 ⭐F | Issue prose = the contract for M1 sessions | +| T10 write-back | Claude Code | Sonnet 4.6¹ | Vault stewardship, mechanical | + +¹ Tier note: if M0 runs as one continuous `executing-plans` session (lowest-friction path), stay on one model throughout — the Sonnet 4.6 entries mean "this tier suffices when run standalone / cost-sensitive." + +Caveats: (a) all of T3 predates the repo (git init is T5), so M0 handoffs to Codex/Antigravity land directly in the working dir without the PR gate — hand off only at task boundaries with green tests; full one-tool-per-branch discipline starts at M1. (b) Model names current as of 2026-06-11 (Opus 4.8 / Sonnet 4.6 standing, Fable 5 only until 2026-06-22; GPT-5.5-Codex; Gemini 3 Pro; Cursor Composer 2.5, which benchmarks ≈ Opus 4.8 — making it Cursor's default for everything and Cursor agent a credible Codex-lane alternate at M1+) — substitute each tool's newest equivalent tier at execution time. + +## Obsidian vault additions + +Per `claude/Vault.md` convention: `Idlegame/_Claude.md` (what it is, working dir, repo URL, standing rules — monetization stance, automation principle, prestige philosophy — quick-nav, session log, `[[Context]]` backlink); `Idlegame/GDD.md` (pillars + core loops from the interview); `Idlegame/Story/Outline.md` (authoring-workflow skeleton); `Idlegame/Decisions.md` (design ADR-lite; code ADRs live in repo). Add the row to `claude/Context.md`'s project table. + +## Scaffold layout + +``` +idlegame/ + AGENTS.md CLAUDE.md README.md LICENSE.md (proprietary notice) + .gitea/workflows/{ci,deploy}.yml .gitea/issue_template/ PR template + .nvmrc package.json tsconfig.json (strict) biome.json vite.config.ts (PWA) + docs/adr/0001-tech-stack.md docs/architecture.md docs/plans/ (superpowers plans) + src/engine/ ← pure TS: fixed-timestep tick loop + offline catch-up, resources, + action queue, story graph, prestige hooks, versioned saves + (zod-validated, lz-string export), num.ts + src/content/ ← data-driven, zod-validated definitions (resources, actions, story nodes) + src/state/ ← zustand bridge: engine snapshots → view + src/ui/ ← React shell: action panel, resource bar, event log, settings + src/engine/__tests__/ ← engine core target: 80%+ coverage +``` + +The scaffold ships a **walking skeleton**, not gameplay: 1 resource, 1 timed action, tick loop with offline catch-up, save → reload → intact. Proves the architecture end to end; M1 makes it a game. + +## Tasks (superpowers-executable, in order) + +**T1 — Vault project setup.** `Idlegame/_Claude.md` and the `claude/Context.md` row already exist (created at planning handoff) — extend `_Claude.md`, don't recreate. Create `Idlegame/GDD.md`, `Idlegame/Story/Outline.md`, `Idlegame/Decisions.md` (content from this plan). *Verify: vault_read returns each note; Context.md table has the row.* + +**T2 — Scaffold the app.** Vite react-ts template → strict tsconfig, Biome, Vitest, Tailwind, vite-plugin-pwa, folder layout above (except `.gitea/workflows/deploy.yml` — deferred to T8 so deploy never runs red before its secrets exist), `.nvmrc` (`24`) + `engines: { "node": ">=24" }`. pnpm 10 blocks dependency postinstall scripts by default — allowlist build deps (esbuild etc.) via `pnpm.onlyBuiltDependencies` in `package.json` (or `pnpm approve-builds`), or the toolchain silently breaks. *Verify: `pnpm typecheck && pnpm lint && pnpm test && pnpm build` all green; `pnpm dev` serves; manifest in build output.* + +**T3 — Walking skeleton (TDD, four sub-tasks).** Tests first throughout; engine code never imports React. + +- **T3.1 — Engine core: `num.ts` + tick loop.** Branded number type + human-readable formatting; fixed-timestep accumulator loop (`advance(now)`) where offline catch-up is the same code path (large delta → many ticks, batch-capped). Configure Vitest coverage thresholds here (≥80% on `src/engine/`). *Verify: tests green; determinism test — N elapsed seconds always yields the same tick count.* +- **T3.2 — One resource, one timed action.** Zod-validated content definitions in `src/content/`: one resource, one timed action (duration → yields resource); action progress advances per tick, completion grants the yield. *Verify: accrual + completion unit tests green.* +- **T3.3 — Versioned saves + persistence.** Zod save schema with version field, serialize/deserialize, lz-string export/import string, idb-keyval persistence (localStorage fallback), autosave + load-on-boot, offline elapsed credited on load. *Verify: round-trip test; invalid/tampered save rejected cleanly; offline-credit unit test.* +- **T3.4 — Zustand bridge + React shell.** Engine snapshots → Zustand store (~10 fps); minimal UI: resource readout, start-action button with progress bar, event log stub. *Verify: engine files ≥80% coverage overall; manual — action completes, resource increments, reload preserves state, offline time credited.* + +**T4 — Agent layer + docs.** AGENTS.md (incl. routing table + context contract + vault local path), CLAUDE.md import, README, ADR-0001 (stack), `docs/architecture.md`; this plan already lives at `docs/plans/2026-06-11-m0-bootstrap.md` — keep it current. *Verify: files exist; AGENTS.md states commands that actually run.* + +**T5 — Git + Gitea repo.** `git init -b main` (git 2.46 may default to `master`), conventional commits, create private repo via Gitea API, push `main`. *Verify: repo visible at gitea.ginnoir.com/ginnoir/idlegame with full tree.* + +**T6 — Repo config.** Labels, milestones M0/M1, issue/PR templates live, protect `main` (no direct pushes, PRs required, self-merge ok). The **CI-required** status check is deliberately deferred to T7 — flipping it on before a runner exists would leave `main` unmergeable. *Verify: direct push to main rejected; templates render on new-issue page.* + +**T7 — Actions runner.** Read `Homelab/_Claude.md` + `Deploy.md` first. Add `act_runner` (docker mode) to homelabstack `dev` stack per that repo's conventions; register with token; enable Actions on the repo. After the first green run, flip **CI-required** on `main`'s protection (deferred from T6). *Verify: a test branch push runs CI to green on the runner (`main` rejects direct pushes by now); merging to main now requires green CI.* + +**T8 — Playtest deploy.** Read `Homelab/_Claude.md` + `Deploy.md` first (same rule as T7). Auth: generate a dedicated ed25519 deploy keypair; append the pubkey to the `ginnoir` user's `authorized_keys` on valhalla; store private key + `known_hosts` as repo Actions secrets via the Gitea API. Create `.gitea/workflows/deploy.yml` here (deferred from T2): on push to `main`, build → rsync `dist/` over SSH to `valhalla:/config/caddy/site/idlegame/` (mirrors the romhacks-wiki precedent). Caddy block in homelabstack: `idlegame.ginnoir.com` with `import internal_only`, `file_server`, SPA `try_files` fallback to `/index.html` (PWA routing); lands via the existing Caddyfile push workflow. *Verify: DNS resolves on LAN (add a host entry per homelab conventions if no wildcard covers it); URL loads the walking skeleton on desktop and phone (LAN); PWA installable.* + +**T9 — Plane + M1 seeding.** *(Caddy bypass done 2026-06-11, commit `9935041` in homelabstack — `/api/*` + `X-API-Key` now routes directly to `plane_api:8000`, bypassing Authentik; verified.)* Plane project "Idlegame" + 3 epics via API (key in vault `claude/Credentials.md`); ~10 M1 issues in Gitea (engine tick/offline, resource & action definitions, action queue UI, story graph + first branch, automation unlock, prestige reset, save migrations, mobile layout pass, opening-arc content, balance pass), labeled and milestoned, linked from epics. *Verify: Plane shows epics; Gitea milestone M1 lists the issues.* + +**T10 — Write-back.** Session outcomes → `Idlegame/_Claude.md` session log; mark M0 complete in Gitea. *Verify: vault note updated; M0 milestone closed.* + +No task needs to ask ginnoir for secrets or approval: the Gitea admin API token and the Plane API key live in the vault at `claude/Credentials.md` (local file: `C:\Users\MattC\Documents\Obsidian Vault\claude\Credentials.md`). Runner registration tokens are minted at deploy time via `POST /api/v1/admin/actions/runners/registration-token` (the GET route 404s on Gitea 1.26.2; CLI fallback: `docker exec gitea gitea actions generate-runner-token` on valhalla). The T8 deploy keypair is generated at execution time and stored via the Gitea secrets API — no secret passes through ginnoir's hands. Verified 2026-06-11: Gitea token has admin scope, Actions enabled, 0 runners registered. Plane API Caddy bypass is live (homelabstack `9935041`, 2026-06-11) — all tasks T1–T10 can run fully autonomously. + +## Out of scope (later milestones) + +M1 gameplay itself; Steam/Tauri and Capacitor wraps; Playwright E2E; release tagging + changelog; i18n; cloud saves; public playtest access. diff --git a/index.html b/index.html new file mode 100644 index 0000000..192dd62 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + Idlegame + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..0668f95 --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "idlegame", + "private": true, + "version": "0.0.0", + "type": "module", + "description": "Idle/incremental text-fantasy RPG (proprietary).", + "engines": { + "node": ">=24" + }, + "packageManager": "pnpm@10.33.3", + "scripts": { + "dev": "vite", + "build": "tsc --noEmit && vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit", + "lint": "biome ci .", + "format": "biome format --write .", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" + }, + "dependencies": { + "idb-keyval": "^6.2.5", + "lz-string": "^1.5.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "zod": "^4.4.3", + "zustand": "^5.0.14" + }, + "devDependencies": { + "@biomejs/biome": "^2.4.16", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^24.12.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "@vitest/coverage-v8": "^4.1.8", + "tailwindcss": "^4.3.0", + "typescript": "~6.0.2", + "vite": "^8.0.12", + "vite-plugin-pwa": "^1.3.0", + "vitest": "^4.1.8" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "@biomejs/biome", + "@tailwindcss/oxide", + "esbuild" + ] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..17e4632 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4767 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + idb-keyval: + specifier: ^6.2.5 + version: 6.2.5 + lz-string: + specifier: ^1.5.0 + version: 1.5.0 + react: + specifier: ^19.2.6 + version: 19.2.7 + react-dom: + specifier: ^19.2.6 + version: 19.2.7(react@19.2.7) + zod: + specifier: ^4.4.3 + version: 4.4.3 + zustand: + specifier: ^5.0.14 + version: 5.0.14(@types/react@19.2.17)(react@19.2.7) + devDependencies: + '@biomejs/biome': + specifier: ^2.4.16 + version: 2.4.16 + '@tailwindcss/vite': + specifier: ^4.3.0 + version: 4.3.0(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0)) + '@types/node': + specifier: ^24.12.3 + version: 24.13.2 + '@types/react': + specifier: ^19.2.14 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.2(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0)) + '@vitest/coverage-v8': + specifier: ^4.1.8 + version: 4.1.8(vitest@4.1.8) + tailwindcss: + specifier: ^4.3.0 + version: 4.3.0 + typescript: + specifier: ~6.0.2 + version: 6.0.3 + vite: + specifier: ^8.0.12 + version: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0) + vite-plugin-pwa: + specifier: ^1.3.0 + version: 1.3.0(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1) + vitest: + specifier: ^4.1.8 + version: 4.1.8(@types/node@24.13.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0)) + +packages: + + '@apideck/better-ajv-errors@0.3.7': + resolution: {integrity: sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + + '@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.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + 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-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + 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-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + 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/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.29.7': + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.29.7': + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.29.7': + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.29.7': + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.29.7': + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.29.7': + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.29.7': + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.29.7': + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.29.7': + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.29.7': + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.29.7': + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.7': + resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.29.7': + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.29.7': + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.29.7': + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.29.7': + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.29.7': + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.7': + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.29.7': + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.29.7': + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.29.7': + resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.29.7': + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.29.7': + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.29.7': + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.29.7': + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7': + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.7': + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + 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.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@biomejs/biome@2.4.16': + resolution: {integrity: sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.4.16': + resolution: {integrity: sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.4.16': + resolution: {integrity: sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.4.16': + resolution: {integrity: sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@2.4.16': + resolution: {integrity: sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@2.4.16': + resolution: {integrity: sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.4.16': + resolution: {integrity: sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@2.4.16': + resolution: {integrity: sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.4.16': + resolution: {integrity: sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@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==} + + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=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/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/plugin-babel@6.1.0': + resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@6.0.3': + resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-terser@1.0.0': + resolution: {integrity: sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.61.1': + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.61.1': + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.61.1': + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.61.1': + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.61.1': + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.61.1': + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.61.1': + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.61.1': + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.61.1': + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.61.1': + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.61.1': + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.61.1': + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.61.1': + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.61.1': + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.61.1': + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.61.1': + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.61.1': + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.61.1': + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.61.1': + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.61.1': + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.61.1': + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tailwindcss/node@4.3.0': + resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} + + '@tailwindcss/oxide-android-arm64@4.3.0': + resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.0': + resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} + 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.0': + resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.0': + resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.0': + resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} + engines: {node: '>=12'} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@vitejs/plugin-react@6.0.2': + resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + + '@vitest/coverage-v8@4.1.8': + resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} + peerDependencies: + '@vitest/browser': 4.1.8 + vitest: 4.1.8 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.8': + resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} + + '@vitest/mocker@4.1.8': + resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.8': + resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} + + '@vitest/runner@4.1.8': + resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} + + '@vitest/snapshot@4.1.8': + resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} + + '@vitest/spy@4.1.8': + resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} + + '@vitest/utils@4.1.8': + resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.36: + resolution: {integrity: sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.371: + resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==} + + enhanced-resolve@5.24.0: + resolution: {integrity: sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==} + engines: {node: '>=10.13.0'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + 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'} + + eta@4.6.0: + resolution: {integrity: sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==} + engines: {node: '>=20'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + 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-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + idb-keyval@6.2.5: + resolution: {integrity: sha512-eKQkTnS0relYsSOYomx8ozIbmdsQCKUdhyuIaQ2DZgKuaxtyQQMkyD/wlnQN32pO3yutN1b1L8uqwcDKaJd7/Q==} + + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + 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] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + 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] + libc: [musl] + + 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'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + 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==} + + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + obug@2.1.2: + resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + engines: {node: '>=12.20.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + 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.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + peerDependencies: + react: ^19.2.7 + + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.1: + resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} + hasBin: true + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@4.61.1: + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + engines: {node: '>=10'} + hasBin: true + + serialize-javascript@7.0.5: + resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + engines: {node: '>=20.0.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + 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'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + smob@1.6.2: + resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==} + engines: {node: '>=20.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@4.3.0: + resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + + terser@5.48.0: + resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} + engines: {node: '>=10'} + hasBin: true + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + vite-plugin-pwa@1.3.0: + resolution: {integrity: sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@vite-pwa/assets-generator': ^1.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + workbox-build: ^7.4.1 + workbox-window: ^7.4.1 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.8: + resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.8 + '@vitest/browser-preview': 4.1.8 + '@vitest/browser-webdriverio': 4.1.8 + '@vitest/coverage-istanbul': 4.1.8 + '@vitest/coverage-v8': 4.1.8 + '@vitest/ui': 4.1.8 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + workbox-background-sync@7.4.1: + resolution: {integrity: sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==} + + workbox-broadcast-update@7.4.1: + resolution: {integrity: sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==} + + workbox-build@7.4.1: + resolution: {integrity: sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==} + engines: {node: '>=20.0.0'} + + workbox-cacheable-response@7.4.1: + resolution: {integrity: sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==} + + workbox-core@7.4.1: + resolution: {integrity: sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==} + + workbox-expiration@7.4.1: + resolution: {integrity: sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==} + + workbox-google-analytics@7.4.1: + resolution: {integrity: sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==} + + workbox-navigation-preload@7.4.1: + resolution: {integrity: sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==} + + workbox-precaching@7.4.1: + resolution: {integrity: sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==} + + workbox-range-requests@7.4.1: + resolution: {integrity: sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==} + + workbox-recipes@7.4.1: + resolution: {integrity: sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==} + + workbox-routing@7.4.1: + resolution: {integrity: sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==} + + workbox-strategies@7.4.1: + resolution: {integrity: sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==} + + workbox-streams@7.4.1: + resolution: {integrity: sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==} + + workbox-sw@7.4.1: + resolution: {integrity: sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==} + + workbox-window@7.4.1: + resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zustand@5.0.14: + resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@apideck/better-ajv-errors@0.3.7(ajv@8.20.0)': + dependencies: + ajv: 8.20.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@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.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + 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.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helper-wrap-function@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-env@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/types': 7.29.7 + esutils: 2.0.3 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} + + '@biomejs/biome@2.4.16': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.4.16 + '@biomejs/cli-darwin-x64': 2.4.16 + '@biomejs/cli-linux-arm64': 2.4.16 + '@biomejs/cli-linux-arm64-musl': 2.4.16 + '@biomejs/cli-linux-x64': 2.4.16 + '@biomejs/cli-linux-x64-musl': 2.4.16 + '@biomejs/cli-win32-arm64': 2.4.16 + '@biomejs/cli-win32-x64': 2.4.16 + + '@biomejs/cli-darwin-arm64@2.4.16': + optional: true + + '@biomejs/cli-darwin-x64@2.4.16': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.4.16': + optional: true + + '@biomejs/cli-linux-arm64@2.4.16': + optional: true + + '@biomejs/cli-linux-x64-musl@2.4.16': + optional: true + + '@biomejs/cli-linux-x64@2.4.16': + optional: true + + '@biomejs/cli-win32-arm64@2.4.16': + optional: true + + '@biomejs/cli-win32-x64@2.4.16': + optional: true + + '@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 + + '@isaacs/cliui@9.0.0': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@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/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.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.2 + optional: true + + '@oxc-project/types@0.133.0': {} + + '@rolldown/binding-android-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-x64@1.0.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.3': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(rollup@4.61.1)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@rollup/pluginutils': 5.4.0(rollup@4.61.1) + optionalDependencies: + rollup: 4.61.1 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-node-resolve@16.0.3(rollup@4.61.1)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.61.1) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.12 + optionalDependencies: + rollup: 4.61.1 + + '@rollup/plugin-replace@6.0.3(rollup@4.61.1)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.61.1) + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.61.1 + + '@rollup/plugin-terser@1.0.0(rollup@4.61.1)': + dependencies: + serialize-javascript: 7.0.5 + smob: 1.6.2 + terser: 5.48.0 + optionalDependencies: + rollup: 4.61.1 + + '@rollup/pluginutils@5.4.0(rollup@4.61.1)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.61.1 + + '@rollup/rollup-android-arm-eabi@4.61.1': + optional: true + + '@rollup/rollup-android-arm64@4.61.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.61.1': + optional: true + + '@rollup/rollup-darwin-x64@4.61.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.61.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.61.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.61.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.61.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.61.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.61.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.61.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.61.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.61.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.61.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.61.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.61.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.61.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.61.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.61.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.61.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.61.1': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@tailwindcss/node@4.3.0': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.0 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.0 + + '@tailwindcss/oxide-android-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide@4.3.0': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-x64': 4.3.0 + '@tailwindcss/oxide-freebsd-x64': 4.3.0 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-x64-musl': 4.3.0 + '@tailwindcss/oxide-wasm32-wasi': 4.3.0 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 + + '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0))': + dependencies: + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + tailwindcss: 4.3.0 + vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0) + + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.30.21 + string.prototype.matchall: 4.0.12 + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/node@24.13.2': + dependencies: + undici-types: 7.18.2 + + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': + dependencies: + csstype: 3.2.3 + + '@types/resolve@1.20.2': {} + + '@types/trusted-types@2.0.7': {} + + '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0) + + '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.8 + ast-v8-to-istanbul: 1.0.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.2 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.8(@types/node@24.13.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0)) + + '@vitest/expect@4.1.8': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0))': + dependencies: + '@vitest/spy': 4.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0) + + '@vitest/pretty-format@4.1.8': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.8': + dependencies: + '@vitest/utils': 4.1.8 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.8': + dependencies: + '@vitest/pretty-format': 4.1.8 + '@vitest/utils': 4.1.8 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.8': {} + + '@vitest/utils@4.1.8': + dependencies: + '@vitest/pretty-format': 4.1.8 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + acorn@8.17.0: {} + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.4: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + async-function@1.0.0: {} + + async@3.2.6: {} + + at-least-node@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7): + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.36: {} + + brace-expansion@2.1.1: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.36 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.371 + node-releases: 2.0.47 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + buffer-from@1.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001799: {} + + chai@6.2.2: {} + + commander@2.20.3: {} + + common-tags@1.8.2: {} + + convert-source-map@2.0.0: {} + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-random-string@2.0.0: {} + + csstype@3.2.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.371: {} + + enhanced-resolve@5.24.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.1.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + eta@4.6.0: {} + + expect-type@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-uri@3.1.2: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.4 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-own-enumerable-property-symbols@3.0.2: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + html-escaper@2.0.2: {} + + idb-keyval@6.2.5: {} + + idb@7.1.1: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-obj@1.0.1: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-regexp@1.0.0: {} + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 + + jiti@2.7.0: {} + + js-tokens@10.0.0: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + + leven@3.1.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 + + lodash.debounce@4.0.8: {} + + lodash.sortby@4.7.0: {} + + lru-cache@11.5.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.4 + + math-intrinsics@1.1.0: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + + minipass@7.1.3: {} + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + node-releases@2.0.47: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + obug@2.1.2: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + package-json-from-dist@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.1 + minipass: 7.1.3 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + + punycode@2.3.1: {} + + react-dom@19.2.7(react@19.2.7): + dependencies: + react: 19.2.7 + scheduler: 0.27.0 + + react@19.2.7: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.1: + dependencies: + jsesc: 3.1.0 + + require-from-string@2.0.2: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rolldown@1.0.3: + dependencies: + '@oxc-project/types': 0.133.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 + + rollup@4.61.1: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.61.1 + '@rollup/rollup-android-arm64': 4.61.1 + '@rollup/rollup-darwin-arm64': 4.61.1 + '@rollup/rollup-darwin-x64': 4.61.1 + '@rollup/rollup-freebsd-arm64': 4.61.1 + '@rollup/rollup-freebsd-x64': 4.61.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1 + '@rollup/rollup-linux-arm64-gnu': 4.61.1 + '@rollup/rollup-linux-arm64-musl': 4.61.1 + '@rollup/rollup-linux-loong64-gnu': 4.61.1 + '@rollup/rollup-linux-loong64-musl': 4.61.1 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1 + '@rollup/rollup-linux-ppc64-musl': 4.61.1 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1 + '@rollup/rollup-linux-riscv64-musl': 4.61.1 + '@rollup/rollup-linux-s390x-gnu': 4.61.1 + '@rollup/rollup-linux-x64-gnu': 4.61.1 + '@rollup/rollup-linux-x64-musl': 4.61.1 + '@rollup/rollup-openbsd-x64': 4.61.1 + '@rollup/rollup-openharmony-arm64': 4.61.1 + '@rollup/rollup-win32-arm64-msvc': 4.61.1 + '@rollup/rollup-win32-ia32-msvc': 4.61.1 + '@rollup/rollup-win32-x64-gnu': 4.61.1 + '@rollup/rollup-win32-x64-msvc': 4.61.1 + fsevents: 2.3.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.4: {} + + serialize-javascript@7.0.5: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + smob@1.6.2: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + stackback@0.0.2: {} + + std-env@4.1.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-comments@2.0.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@4.3.0: {} + + tapable@2.3.3: {} + + temp-dir@2.0.0: {} + + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + + terser@5.48.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.17.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + tinybench@2.9.0: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@3.1.0: {} + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + tslib@2.8.1: + optional: true + + type-fest@0.16.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@6.0.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@7.18.2: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + universalify@2.0.1: {} + + upath@1.2.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + vite-plugin-pwa@1.3.0(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.17 + vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0) + workbox-build: 7.4.1 + workbox-window: 7.4.1 + transitivePeerDependencies: + - supports-color + + vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.2 + fsevents: 2.3.3 + jiti: 2.7.0 + terser: 5.48.0 + + vitest@4.1.8(@types/node@24.13.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0)): + dependencies: + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.2 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)(terser@5.48.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.13.2 + '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) + transitivePeerDependencies: + - msw + + webidl-conversions@4.0.2: {} + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + workbox-background-sync@7.4.1: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.1 + + workbox-broadcast-update@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-build@7.4.1: + dependencies: + '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) + '@babel/core': 7.29.7 + '@babel/preset-env': 7.29.7(@babel/core@7.29.7) + '@babel/runtime': 7.29.7 + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(rollup@4.61.1) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.61.1) + '@rollup/plugin-replace': 6.0.3(rollup@4.61.1) + '@rollup/plugin-terser': 1.0.0(rollup@4.61.1) + '@trickfilm400/rollup-plugin-off-main-thread': 3.0.0-pre1 + ajv: 8.20.0 + common-tags: 1.8.2 + eta: 4.6.0 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 11.1.0 + pretty-bytes: 5.6.0 + rollup: 4.61.1 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 7.4.1 + workbox-broadcast-update: 7.4.1 + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-google-analytics: 7.4.1 + workbox-navigation-preload: 7.4.1 + workbox-precaching: 7.4.1 + workbox-range-requests: 7.4.1 + workbox-recipes: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + workbox-streams: 7.4.1 + workbox-sw: 7.4.1 + workbox-window: 7.4.1 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-core@7.4.1: {} + + workbox-expiration@7.4.1: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.1 + + workbox-google-analytics@7.4.1: + dependencies: + workbox-background-sync: 7.4.1 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-navigation-preload@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-precaching@7.4.1: + dependencies: + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-range-requests@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-recipes@7.4.1: + dependencies: + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-precaching: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-routing@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-strategies@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-streams@7.4.1: + dependencies: + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + + workbox-sw@7.4.1: {} + + workbox-window@7.4.1: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 7.4.1 + + yallist@3.1.1: {} + + zod@4.4.3: {} + + zustand@5.0.14(@types/react@19.2.17)(react@19.2.7): + optionalDependencies: + '@types/react': 19.2.17 + react: 19.2.7 diff --git a/public/pwa-192.png b/public/pwa-192.png new file mode 100644 index 0000000000000000000000000000000000000000..8806b425759d0a3998dc339d32dec0eb27afb689 GIT binary patch literal 4076 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 literal 0 HcmV?d00001 diff --git a/src/content/.gitkeep b/src/content/.gitkeep new file mode 100644 index 0000000..429ba61 --- /dev/null +++ b/src/content/.gitkeep @@ -0,0 +1,2 @@ +# Data-driven, zod-validated content definitions (resources, actions, story nodes). +# Populated in T3.2 (resource + action) and M1 (story graph). diff --git a/src/content/__tests__/schema.test.ts b/src/content/__tests__/schema.test.ts new file mode 100644 index 0000000..6d9a75f --- /dev/null +++ b/src/content/__tests__/schema.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from 'vitest'; +import { buildContent } from '../schema'; + +const validResources = [{ id: 'gold', name: 'Gold' }]; +const validActions = [ + { id: 'forage', name: 'Forage', durationMs: 3000, yields: { resourceId: 'gold', amount: 1 } }, +]; + +describe('buildContent()', () => { + it('validates definitions and indexes them by id', () => { + const content = buildContent({ resources: validResources, actions: validActions }); + expect(content.resourcesById.gold.name).toBe('Gold'); + expect(content.actionsById.forage.durationMs).toBe(3000); + }); + + it('applies the startAmount default of 0', () => { + const content = buildContent({ resources: validResources, actions: validActions }); + expect(content.resourcesById.gold.startAmount).toBe(0); + }); + + it('rejects an action that yields an unknown resource', () => { + const actions = [ + { + id: 'forage', + name: 'Forage', + durationMs: 3000, + yields: { resourceId: 'ghost', amount: 1 }, + }, + ]; + expect(() => buildContent({ resources: validResources, actions })).toThrow(/unknown resource/i); + }); + + it('rejects duplicate resource ids', () => { + const resources = [ + { id: 'gold', name: 'Gold' }, + { id: 'gold', name: 'Gold Again' }, + ]; + expect(() => buildContent({ resources, actions: validActions })).toThrow(/duplicate/i); + }); + + it('rejects a structurally invalid definition', () => { + const actions = [ + { id: 'forage', name: 'Forage', durationMs: -1, yields: { resourceId: 'gold', amount: 1 } }, + ]; + expect(() => buildContent({ resources: validResources, actions })).toThrow(); + }); +}); diff --git a/src/content/definitions.ts b/src/content/definitions.ts new file mode 100644 index 0000000..343827d --- /dev/null +++ b/src/content/definitions.ts @@ -0,0 +1,17 @@ +/** + * Walking-skeleton content: one resource, one timed action. + * + * M1 expands this into the real resource/action set and the story graph. Kept as + * plain data so it stays diffable and authorable without touching engine code. + */ + +export const resourceDefs = [{ id: 'gold', name: 'Gold', startAmount: 0 }]; + +export const actionDefs = [ + { + id: 'forage', + name: 'Forage for coin', + durationMs: 3000, + yields: { resourceId: 'gold', amount: 1 }, + }, +]; diff --git a/src/content/index.ts b/src/content/index.ts new file mode 100644 index 0000000..3d77f73 --- /dev/null +++ b/src/content/index.ts @@ -0,0 +1,7 @@ +import { actionDefs, resourceDefs } from './definitions'; +import { buildContent } from './schema'; + +/** The validated, indexed content the engine and view consume. */ +export const content = buildContent({ resources: resourceDefs, actions: actionDefs }); + +export type { ActionDef, Content, ResourceDef } from './schema'; diff --git a/src/content/schema.ts b/src/content/schema.ts new file mode 100644 index 0000000..b5e023c --- /dev/null +++ b/src/content/schema.ts @@ -0,0 +1,66 @@ +import { z } from 'zod'; + +/** + * Content schemas. + * + * Game content (resources, actions, later story nodes) is data, validated at + * load time with zod so a malformed definition fails loudly instead of corrupting + * runtime state. The engine consumes the validated `Content` object and never + * reaches for raw definitions. + */ + +export const resourceDefSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + startAmount: z.number().nonnegative().default(0), +}); + +export const actionDefSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + durationMs: z.number().positive(), + yields: z.object({ + resourceId: z.string().min(1), + amount: z.number().positive(), + }), +}); + +export type ResourceDef = z.infer; +export type ActionDef = z.infer; + +export interface Content { + resources: ResourceDef[]; + actions: ActionDef[]; + resourcesById: Record; + actionsById: Record; +} + +function indexById(items: T[], kind: string): Record { + const byId: Record = {}; + for (const item of items) { + if (byId[item.id]) { + throw new Error(`Duplicate ${kind} id "${item.id}"`); + } + byId[item.id] = item; + } + return byId; +} + +/** Validate raw definitions and build the indexed, referentially-checked Content. */ +export function buildContent(input: { resources: unknown[]; actions: unknown[] }): Content { + const resources = input.resources.map((r) => resourceDefSchema.parse(r)); + const actions = input.actions.map((a) => actionDefSchema.parse(a)); + + const resourcesById = indexById(resources, 'resource'); + const actionsById = indexById(actions, 'action'); + + for (const action of actions) { + if (!resourcesById[action.yields.resourceId]) { + throw new Error( + `Action "${action.id}" yields unknown resource "${action.yields.resourceId}"`, + ); + } + } + + return { resources, actions, resourcesById, actionsById }; +} diff --git a/src/engine/__tests__/game.test.ts b/src/engine/__tests__/game.test.ts new file mode 100644 index 0000000..58c6d5d --- /dev/null +++ b/src/engine/__tests__/game.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'vitest'; +import { buildContent } from '../../content/schema'; +import { createGameState, startAction, tickGame } from '../game'; + +function testContent() { + return buildContent({ + resources: [{ id: 'gold', name: 'Gold', startAmount: 5 }], + actions: [ + { id: 'forage', name: 'Forage', durationMs: 300, yields: { resourceId: 'gold', amount: 2 } }, + ], + }); +} + +describe('createGameState()', () => { + it('seeds resource amounts from their start amounts with no active action', () => { + const state = createGameState(testContent()); + expect(state.resources.gold).toBe(5); + expect(state.activeActionId).toBeNull(); + expect(state.actionElapsedMs).toBe(0); + }); +}); + +describe('startAction()', () => { + it('activates the action and resets its progress', () => { + const content = testContent(); + const state = createGameState(content); + state.actionElapsedMs = 999; + startAction(state, content, 'forage'); + expect(state.activeActionId).toBe('forage'); + expect(state.actionElapsedMs).toBe(0); + }); + + it('throws on an unknown action id', () => { + const content = testContent(); + const state = createGameState(content); + expect(() => startAction(state, content, 'nope')).toThrow(/unknown action/i); + }); +}); + +describe('tickGame()', () => { + it('does nothing when no action is active', () => { + const content = testContent(); + const state = createGameState(content); + tickGame(state, content, 100); + expect(state.resources.gold).toBe(5); + }); + + it('advances action progress without yielding before completion', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + tickGame(state, content, 100); // 100 of 300ms + expect(state.resources.gold).toBe(5); + expect(state.actionElapsedMs).toBe(100); + }); + + it('grants the yield on completion and repeats, carrying the remainder', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + tickGame(state, content, 100); + tickGame(state, content, 100); + tickGame(state, content, 100); // 300ms -> one completion, +2 gold + expect(state.resources.gold).toBe(7); + expect(state.actionElapsedMs).toBe(0); + }); + + it('handles multiple completions within a single large tick (offline catch-up)', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + tickGame(state, content, 1000); // 3 completions (900ms) + 100ms remainder + expect(state.resources.gold).toBe(11); // 5 + 3*2 + expect(state.actionElapsedMs).toBe(100); + }); +}); diff --git a/src/engine/__tests__/num.test.ts b/src/engine/__tests__/num.test.ts new file mode 100644 index 0000000..018ecb4 --- /dev/null +++ b/src/engine/__tests__/num.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from 'vitest'; +import { formatNum, num } from '../num'; + +describe('num()', () => { + it('returns the same numeric value, branded', () => { + expect(num(42)).toBe(42); + expect(num(0)).toBe(0); + expect(num(-7.5)).toBe(-7.5); + }); + + it('rejects non-finite values', () => { + expect(() => num(Number.NaN)).toThrow(RangeError); + expect(() => num(Number.POSITIVE_INFINITY)).toThrow(RangeError); + expect(() => num(Number.NEGATIVE_INFINITY)).toThrow(RangeError); + }); +}); + +describe('formatNum()', () => { + it('formats values under 1000 without a suffix', () => { + expect(formatNum(0)).toBe('0'); + expect(formatNum(42)).toBe('42'); + expect(formatNum(999)).toBe('999'); + }); + + it('trims fractional values to at most two decimals', () => { + expect(formatNum(12.5)).toBe('12.5'); + expect(formatNum(12.3456)).toBe('12.35'); + expect(formatNum(7.0)).toBe('7'); + }); + + it('uses K / M / B suffixes for larger magnitudes', () => { + expect(formatNum(1000)).toBe('1K'); + expect(formatNum(1234)).toBe('1.23K'); + expect(formatNum(12_000)).toBe('12K'); + expect(formatNum(1_500_000)).toBe('1.5M'); + expect(formatNum(2_000_000_000)).toBe('2B'); + }); + + it('never uses scientific notation, even past a billion', () => { + const s = formatNum(1_500_000_000_000); + expect(s).not.toMatch(/e/i); + expect(s).toBe('1,500B'); + }); + + it('preserves sign for negative values', () => { + expect(formatNum(-42)).toBe('-42'); + expect(formatNum(-1500)).toBe('-1.5K'); + }); +}); diff --git a/src/engine/__tests__/save.test.ts b/src/engine/__tests__/save.test.ts new file mode 100644 index 0000000..3725092 --- /dev/null +++ b/src/engine/__tests__/save.test.ts @@ -0,0 +1,108 @@ +import { describe, expect, it } from 'vitest'; +import { buildContent } from '../../content/schema'; +import { createGameState, startAction } from '../game'; +import { + applyOfflineProgress, + createSave, + deserializeSave, + fromExportString, + SAVE_VERSION, + serializeSave, + toExportString, +} from '../save'; + +function testContent() { + return buildContent({ + resources: [{ id: 'gold', name: 'Gold', startAmount: 0 }], + actions: [ + { id: 'forage', name: 'Forage', durationMs: 3000, yields: { resourceId: 'gold', amount: 1 } }, + ], + }); +} + +function sampleState() { + const content = testContent(); + const state = createGameState(content); + state.resources.gold = 12; + startAction(state, content, 'forage'); + state.actionElapsedMs = 500; + return state; +} + +describe('createSave()', () => { + it('stamps the current version and timestamp around a snapshot of state', () => { + const save = createSave(sampleState(), 1700); + expect(save.version).toBe(SAVE_VERSION); + expect(save.savedAt).toBe(1700); + expect(save.state.resources.gold).toBe(12); + expect(save.state.activeActionId).toBe('forage'); + }); + + it('snapshots state so later mutation does not affect the save', () => { + const state = sampleState(); + const save = createSave(state, 1700); + state.resources.gold = 999; + expect(save.state.resources.gold).toBe(12); + }); +}); + +describe('serialize / deserialize round-trip', () => { + it('survives JSON serialization unchanged', () => { + const save = createSave(sampleState(), 1700); + const restored = deserializeSave(serializeSave(save)); + expect(restored).toEqual(save); + }); + + it('survives the compressed export string unchanged', () => { + const save = createSave(sampleState(), 1700); + const restored = fromExportString(toExportString(save)); + expect(restored).toEqual(save); + }); +}); + +describe('invalid / tampered saves', () => { + it('rejects a non-JSON export string cleanly', () => { + expect(() => fromExportString('@@@not-a-valid-payload@@@')).toThrow(); + }); + + it('rejects JSON that does not match the save schema', () => { + expect(() => deserializeSave('{"version":1,"savedAt":1,"state":{}}')).toThrow(); + }); + + it('rejects an unsupported save version', () => { + const future = JSON.stringify({ + version: 999, + savedAt: 1, + state: { resources: {}, activeActionId: null, actionElapsedMs: 0 }, + }); + expect(() => deserializeSave(future)).toThrow(/version/i); + }); +}); + +describe('applyOfflineProgress()', () => { + it('credits whole ticks of elapsed time to the active action', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); // 3000ms per gold + const credited = applyOfflineProgress(state, content, 1000, 1000 + 9000); // 9s + expect(credited).toBe(9000); + expect(state.resources.gold).toBe(3); // 9000 / 3000 + }); + + it('credits nothing when the clock did not advance', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + expect(applyOfflineProgress(state, content, 5000, 4000)).toBe(0); + expect(state.resources.gold).toBe(0); + }); + + it('clamps credited time to the offline cap', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + const credited = applyOfflineProgress(state, content, 0, 10_000_000, 6000); + expect(credited).toBe(6000); + expect(state.resources.gold).toBe(2); // 6000 / 3000 + }); +}); diff --git a/src/engine/__tests__/tickLoop.test.ts b/src/engine/__tests__/tickLoop.test.ts new file mode 100644 index 0000000..f08a20d --- /dev/null +++ b/src/engine/__tests__/tickLoop.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'vitest'; +import { advance, createTickLoop } from '../tickLoop'; + +describe('createTickLoop()', () => { + it('defaults to the standard tick rate and an empty accumulator', () => { + const loop = createTickLoop(); + expect(loop.tickMs).toBe(100); + expect(loop.tickCount).toBe(0); + expect(loop.accumulatorMs).toBe(0); + }); + + it('accepts a custom tick length and start timestamp', () => { + const loop = createTickLoop({ tickMs: 250, startNow: 1000 }); + expect(loop.tickMs).toBe(250); + expect(loop.lastNow).toBe(1000); + }); +}); + +describe('advance()', () => { + it('establishes a baseline without running ticks on the first call when no startNow given', () => { + const loop = createTickLoop({ tickMs: 100 }); + expect(advance(loop, 5000)).toBe(0); + expect(loop.lastNow).toBe(5000); + expect(advance(loop, 5100)).toBe(1); + }); + + it('runs one tick per whole tickMs elapsed and keeps the remainder', () => { + const loop = createTickLoop({ tickMs: 100, startNow: 0 }); + expect(advance(loop, 350)).toBe(3); + expect(loop.tickCount).toBe(3); + expect(loop.accumulatorMs).toBe(50); + }); + + it('invokes onTick once per tick with an increasing tick index', () => { + const loop = createTickLoop({ tickMs: 100, startNow: 0 }); + const seen: number[] = []; + advance(loop, 300, (i) => seen.push(i)); + expect(seen).toEqual([1, 2, 3]); + }); + + it('ignores backward clock movement', () => { + const loop = createTickLoop({ tickMs: 100, startNow: 1000 }); + expect(advance(loop, 500)).toBe(0); + expect(loop.lastNow).toBe(500); + expect(loop.tickCount).toBe(0); + }); + + it('yields the same total tick count for the same elapsed time regardless of chunking', () => { + const tickMs = 100; + const elapsedMs = 10_000; + const expected = 100; + + const whole = createTickLoop({ tickMs, startNow: 0 }); + expect(advance(whole, elapsedMs)).toBe(expected); + + const chunked = createTickLoop({ tickMs, startNow: 0 }); + for (let t = 37; t < elapsedMs; t += 37) { + advance(chunked, t); + } + advance(chunked, elapsedMs); + expect(chunked.tickCount).toBe(expected); + }); + + it('caps ticks per advance and defers the remainder so none are lost (offline catch-up path)', () => { + const loop = createTickLoop({ tickMs: 100, startNow: 0 }); + // 10s of elapsed = 100 ticks owed, but cap at 10 per call. + expect(advance(loop, 10_000, undefined, 10)).toBe(10); + expect(loop.tickCount).toBe(10); + + let guard = 0; + while (advance(loop, 10_000, undefined, 10) > 0 && guard < 1000) { + guard += 1; + } + expect(loop.tickCount).toBe(100); + }); +}); diff --git a/src/engine/game.ts b/src/engine/game.ts new file mode 100644 index 0000000..e6eeaa6 --- /dev/null +++ b/src/engine/game.ts @@ -0,0 +1,55 @@ +import type { Content } from '../content/schema'; + +/** + * Core game state and per-tick simulation. + * + * Pure TS — no React, no DOM, no wall clock. The tick loop (tickLoop.ts) drives + * `tickGame` once per tick; `now`/scheduling lives entirely outside this module. + */ + +export interface GameState { + /** resourceId -> current amount. */ + resources: Record; + /** The action currently running, or null. */ + activeActionId: string | null; + /** Progress of the active action, in milliseconds. */ + actionElapsedMs: number; +} + +export function createGameState(content: Content): GameState { + const resources: Record = {}; + for (const resource of content.resources) { + resources[resource.id] = resource.startAmount; + } + return { resources, activeActionId: null, actionElapsedMs: 0 }; +} + +/** Begin running an action, resetting its progress. Throws on an unknown id. */ +export function startAction(state: GameState, content: Content, actionId: string): void { + if (!content.actionsById[actionId]) { + throw new Error(`Unknown action "${actionId}"`); + } + state.activeActionId = actionId; + state.actionElapsedMs = 0; +} + +/** + * Advance the active action by `tickMs`. Each time it reaches its duration it + * grants its yield and repeats, carrying the remainder — so one large tick (the + * offline catch-up path) can complete an action many times. + */ +export function tickGame(state: GameState, content: Content, tickMs: number): void { + if (!state.activeActionId) { + return; + } + const action = content.actionsById[state.activeActionId]; + if (!action) { + return; + } + + state.actionElapsedMs += tickMs; + while (state.actionElapsedMs >= action.durationMs) { + state.actionElapsedMs -= action.durationMs; + state.resources[action.yields.resourceId] += action.yields.amount; + } +} diff --git a/src/engine/num.ts b/src/engine/num.ts new file mode 100644 index 0000000..507a34c --- /dev/null +++ b/src/engine/num.ts @@ -0,0 +1,57 @@ +/** + * Numbers, engine-side. + * + * The design keeps values human-readable (≤ billions, K/M/B suffixes, never + * scientific notation — see GDD D-0009). Plain JS numbers suffice; the `Num` + * brand documents "this number is a game quantity" without runtime cost. If a + * late prestige layer ever needs arbitrary precision, swap the brand for a + * Decimal wrapper here — call sites that use `num()`/`formatNum()` stay put. + */ + +declare const numBrand: unique symbol; +export type Num = number & { readonly [numBrand]: true }; + +/** Brand a finite number as a game quantity. Throws on NaN/Infinity. */ +export function num(value: number): Num { + if (!Number.isFinite(value)) { + throw new RangeError(`num() requires a finite value, got ${value}`); + } + return value as Num; +} + +const SUFFIX_TIERS = [ + { limit: 1e9, div: 1e9, suffix: 'B' }, + { limit: 1e6, div: 1e6, suffix: 'M' }, + { limit: 1e3, div: 1e3, suffix: 'K' }, +] as const; + +/** Round to ≤2 decimals, trim trailing zeros, add thousands separators. */ +function formatScaled(value: number): string { + const rounded = Math.round(value * 100) / 100; + const raw = Number.isInteger(rounded) + ? String(rounded) + : rounded.toFixed(2).replace(/0+$/, '').replace(/\.$/, ''); + + const [intPart, fracPart] = raw.split('.'); + const withSeparators = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ','); + return fracPart ? `${withSeparators}.${fracPart}` : withSeparators; +} + +/** Human-readable rendering: `1.23K`, `1.5M`, `2B`, `1,500B`. No `e` notation. */ +export function formatNum(value: Num | number): string { + const n = value as number; + if (!Number.isFinite(n)) { + return '0'; + } + + const sign = n < 0 ? '-' : ''; + const abs = Math.abs(n); + + for (const tier of SUFFIX_TIERS) { + if (abs >= tier.limit) { + return `${sign}${formatScaled(abs / tier.div)}${tier.suffix}`; + } + } + + return `${sign}${formatScaled(abs)}`; +} diff --git a/src/engine/save.ts b/src/engine/save.ts new file mode 100644 index 0000000..cf9a46a --- /dev/null +++ b/src/engine/save.ts @@ -0,0 +1,98 @@ +import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from 'lz-string'; +import { z } from 'zod'; +import type { Content } from '../content/schema'; +import type { GameState } from './game'; +import { tickGame } from './game'; +import { TICK_MS } from './tickLoop'; + +/** + * Save format, serialization, and offline crediting. + * + * Pure TS — no IndexedDB, no DOM. The IO adapter (idb-keyval + localStorage + * fallback) lives in src/state/persistence.ts; this module only turns game state + * into a validated, versioned, compressed string and back. Saves carry a + * `version` so future migrations have a hook; for now a version mismatch is + * rejected cleanly rather than silently coerced. + */ + +export const SAVE_VERSION = 1; + +/** Default cap on credited offline time: 24 hours. */ +export const DEFAULT_MAX_OFFLINE_MS = 24 * 60 * 60 * 1000; + +export const gameStateSchema = z.object({ + resources: z.record(z.string(), z.number()), + activeActionId: z.string().nullable(), + actionElapsedMs: z.number().nonnegative(), +}); + +export const saveSchema = z.object({ + version: z.number().int().nonnegative(), + savedAt: z.number().nonnegative(), + state: gameStateSchema, +}); + +export type SaveData = z.infer; + +/** Snapshot the current state into a versioned, timestamped save. */ +export function createSave(state: GameState, now: number): SaveData { + return { + version: SAVE_VERSION, + savedAt: now, + state: { + resources: { ...state.resources }, + activeActionId: state.activeActionId, + actionElapsedMs: state.actionElapsedMs, + }, + }; +} + +export function serializeSave(save: SaveData): string { + return JSON.stringify(save); +} + +/** Parse + validate a save from JSON. Throws on malformed or unsupported saves. */ +export function deserializeSave(json: string): SaveData { + const parsed: unknown = JSON.parse(json); + const save = saveSchema.parse(parsed); + if (save.version !== SAVE_VERSION) { + throw new Error( + `Unsupported save version ${save.version} (this build expects ${SAVE_VERSION})`, + ); + } + return save; +} + +export function toExportString(save: SaveData): string { + return compressToEncodedURIComponent(serializeSave(save)); +} + +/** Decompress + validate an export string. Throws cleanly on tampered input. */ +export function fromExportString(compressed: string): SaveData { + const json = decompressFromEncodedURIComponent(compressed); + if (json === null || json === '') { + throw new Error('Save string is corrupt or empty'); + } + return deserializeSave(json); +} + +/** + * Credit elapsed wall-clock time to the running simulation on load, using the + * same per-tick path the live loop uses. Elapsed is clamped to `maxOfflineMs` + * and floored to whole ticks. Returns the milliseconds actually credited. + */ +export function applyOfflineProgress( + state: GameState, + content: Content, + savedAt: number, + now: number, + maxOfflineMs: number = DEFAULT_MAX_OFFLINE_MS, +): number { + const elapsed = Math.max(0, now - savedAt); + const credited = Math.min(elapsed, maxOfflineMs); + const ticks = Math.floor(credited / TICK_MS); + for (let i = 0; i < ticks; i += 1) { + tickGame(state, content, TICK_MS); + } + return credited; +} diff --git a/src/engine/tickLoop.ts b/src/engine/tickLoop.ts new file mode 100644 index 0000000..d1ca0f9 --- /dev/null +++ b/src/engine/tickLoop.ts @@ -0,0 +1,73 @@ +/** + * Fixed-timestep tick loop with an accumulator. + * + * The engine advances in whole ticks of `tickMs`. `advance(loop, now)` folds the + * elapsed wall-clock time into an accumulator and drains it one tick at a time, + * keeping the sub-tick remainder so results are independent of how the caller + * chunks calls (determinism — see the test suite). + * + * Offline catch-up is the *same* code path: a large `now` delta simply owes many + * ticks. `maxTicks` caps how many run per call so a huge delta can't lock the + * thread; the remainder stays in the accumulator and drains on the next call, + * so no ticks are ever lost. Clamping the *credited* offline window lives in the + * save layer (T3.3), not here. + */ + +export const TICK_HZ = 10; +export const TICK_MS = 1000 / TICK_HZ; +export const DEFAULT_MAX_TICKS_PER_ADVANCE = 100_000; + +export interface TickLoop { + /** Milliseconds of simulated time per tick. */ + readonly tickMs: number; + /** Total ticks run over this loop's lifetime. */ + tickCount: number; + /** Sub-tick wall-clock remainder, in milliseconds. */ + accumulatorMs: number; + /** Last timestamp seen, or null until the first `advance` establishes a baseline. */ + lastNow: number | null; +} + +export function createTickLoop(opts?: { tickMs?: number; startNow?: number }): TickLoop { + const tickMs = opts?.tickMs ?? TICK_MS; + if (!(tickMs > 0)) { + throw new RangeError(`tickMs must be > 0, got ${tickMs}`); + } + return { + tickMs, + tickCount: 0, + accumulatorMs: 0, + lastNow: opts?.startNow ?? null, + }; +} + +/** + * Advance the loop to `now`, running owed ticks (capped at `maxTicks`). + * Returns the number of ticks run this call. + */ +export function advance( + loop: TickLoop, + now: number, + onTick?: (tickIndex: number) => void, + maxTicks: number = DEFAULT_MAX_TICKS_PER_ADVANCE, +): number { + if (loop.lastNow === null) { + loop.lastNow = now; + return 0; + } + + const elapsed = now - loop.lastNow; + loop.lastNow = now; + if (elapsed > 0) { + loop.accumulatorMs += elapsed; + } + + let ran = 0; + while (loop.accumulatorMs >= loop.tickMs && ran < maxTicks) { + loop.accumulatorMs -= loop.tickMs; + loop.tickCount += 1; + ran += 1; + onTick?.(loop.tickCount); + } + return ran; +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..33a8804 --- /dev/null +++ b/src/index.css @@ -0,0 +1,12 @@ +@import "tailwindcss"; + +:root { + color-scheme: dark; +} + +html, +body { + margin: 0; + min-height: 100dvh; + background-color: #020617; /* slate-950 */ +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..8aa7ed0 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,15 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import { App } from './ui/App'; + +const root = document.getElementById('root'); +if (!root) { + throw new Error('Root element #root not found'); +} + +createRoot(root).render( + + + , +); diff --git a/src/state/.gitkeep b/src/state/.gitkeep new file mode 100644 index 0000000..a67fc4a --- /dev/null +++ b/src/state/.gitkeep @@ -0,0 +1 @@ +# Zustand bridge: engine snapshots -> view (~10 fps). Built in T3.4. diff --git a/src/state/__tests__/persistence.test.ts b/src/state/__tests__/persistence.test.ts new file mode 100644 index 0000000..c22d4a4 --- /dev/null +++ b/src/state/__tests__/persistence.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from 'vitest'; +import { buildContent } from '../../content/schema'; +import { createGameState, startAction } from '../../engine/game'; +import { createSave, serializeSave } from '../../engine/save'; +import { createMemoryBackend, loadGame, saveGame } from '../persistence'; + +function testContent() { + return buildContent({ + resources: [{ id: 'gold', name: 'Gold', startAmount: 0 }], + actions: [ + { id: 'forage', name: 'Forage', durationMs: 3000, yields: { resourceId: 'gold', amount: 1 } }, + ], + }); +} + +describe('loadGame()', () => { + it('returns a fresh game when no save exists', async () => { + const content = testContent(); + const result = await loadGame(content, createMemoryBackend(), 1000); + expect(result.state.resources.gold).toBe(0); + expect(result.offlineMs).toBe(0); + }); + + it('round-trips a saved game and credits offline progress', async () => { + const content = testContent(); + const state = createGameState(content); + state.resources.gold = 10; + startAction(state, content, 'forage'); + const backend = createMemoryBackend(); + await saveGame(state, backend, 1000); + + const result = await loadGame(content, backend, 1000 + 9000); // 9s offline + expect(result.offlineMs).toBe(9000); + expect(result.state.resources.gold).toBe(13); // 10 + 9000/3000 + expect(result.state.activeActionId).toBe('forage'); + }); + + it('falls back to a fresh game on a corrupt save instead of throwing', async () => { + const content = testContent(); + const result = await loadGame(content, createMemoryBackend('@@@garbage@@@'), 1000); + expect(result.state.resources.gold).toBe(0); + expect(result.offlineMs).toBe(0); + }); + + it('drops an active action that no longer exists in content', async () => { + const content = testContent(); + const stale = serializeSave( + createSave( + { resources: { gold: 1 }, activeActionId: 'ghost-action', actionElapsedMs: 0 }, + 1000, + ), + ); + const result = await loadGame(content, createMemoryBackend(stale), 1000); + expect(result.state.activeActionId).toBeNull(); + }); +}); diff --git a/src/state/__tests__/viewModel.test.ts b/src/state/__tests__/viewModel.test.ts new file mode 100644 index 0000000..971e2ff --- /dev/null +++ b/src/state/__tests__/viewModel.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, it } from 'vitest'; +import { buildContent } from '../../content/schema'; +import { createGameState, startAction } from '../../engine/game'; +import { formatOfflineDuration, toView } from '../viewModel'; + +function testContent() { + return buildContent({ + resources: [{ id: 'gold', name: 'Gold', startAmount: 4 }], + actions: [ + { id: 'forage', name: 'Forage', durationMs: 200, yields: { resourceId: 'gold', amount: 1 } }, + ], + }); +} + +describe('toView()', () => { + it('maps resources with their names and amounts', () => { + const content = testContent(); + const view = toView(createGameState(content), content); + expect(view.resources).toEqual([{ id: 'gold', name: 'Gold', amount: 4 }]); + }); + + it('reports no progress and no action name when idle', () => { + const content = testContent(); + const view = toView(createGameState(content), content); + expect(view.activeActionId).toBeNull(); + expect(view.actionName).toBeNull(); + expect(view.actionProgress).toBe(0); + }); + + it('reports the active action name and fractional progress', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + state.actionElapsedMs = 50; // of 200ms + const view = toView(state, content); + expect(view.actionName).toBe('Forage'); + expect(view.actionProgress).toBeCloseTo(0.25); + }); + + it('clamps progress to at most 1', () => { + const content = testContent(); + const state = createGameState(content); + startAction(state, content, 'forage'); + state.actionElapsedMs = 999; + expect(toView(state, content).actionProgress).toBe(1); + }); +}); + +describe('formatOfflineDuration()', () => { + it('formats sub-minute durations in seconds', () => { + expect(formatOfflineDuration(0)).toBe('0s'); + expect(formatOfflineDuration(45_000)).toBe('45s'); + }); + + it('formats minutes with seconds', () => { + expect(formatOfflineDuration(90_000)).toBe('1m 30s'); + expect(formatOfflineDuration(120_000)).toBe('2m'); + }); + + it('formats hours with minutes', () => { + expect(formatOfflineDuration(3_660_000)).toBe('1h 1m'); + expect(formatOfflineDuration(7_200_000)).toBe('2h'); + }); +}); diff --git a/src/state/persistence.ts b/src/state/persistence.ts new file mode 100644 index 0000000..cc77c3d --- /dev/null +++ b/src/state/persistence.ts @@ -0,0 +1,117 @@ +import { del, get, set } from 'idb-keyval'; +import type { Content } from '../content/schema'; +import { createGameState, type GameState } from '../engine/game'; +import { applyOfflineProgress, createSave, deserializeSave, serializeSave } from '../engine/save'; + +/** + * Persistence adapter (IO layer — deliberately outside the pure engine). + * + * A `SaveBackend` is a pluggable string store; the orchestration (`loadGame`, + * `saveGame`) turns it into typed game state via the engine's save module and + * credits offline progress on boot. The real backend prefers IndexedDB + * (idb-keyval) and falls back to localStorage, then to an in-memory store so the + * game still runs (without persistence) in hostile environments. + */ + +export const SAVE_KEY = 'idlegame:save:v1'; + +export interface SaveBackend { + load(): Promise; + save(serialized: string): Promise; + clear(): Promise; +} + +export function createMemoryBackend(initial: string | null = null): SaveBackend { + let value = initial; + return { + load: () => Promise.resolve(value), + save: (serialized) => { + value = serialized; + return Promise.resolve(); + }, + clear: () => { + value = null; + return Promise.resolve(); + }, + }; +} + +function createLocalStorageBackend(key: string): SaveBackend { + return { + load: () => Promise.resolve(globalThis.localStorage.getItem(key)), + save: (serialized) => { + globalThis.localStorage.setItem(key, serialized); + return Promise.resolve(); + }, + clear: () => { + globalThis.localStorage.removeItem(key); + return Promise.resolve(); + }, + }; +} + +function createIdbBackend(key: string): SaveBackend { + return { + load: async () => (await get(key)) ?? null, + save: (serialized) => set(key, serialized), + clear: () => del(key), + }; +} + +/** Choose the best available backend for the current environment. */ +export function createDefaultBackend(key: string = SAVE_KEY): SaveBackend { + if (typeof indexedDB !== 'undefined') { + return createIdbBackend(key); + } + if (typeof localStorage !== 'undefined') { + return createLocalStorageBackend(key); + } + return createMemoryBackend(); +} + +export interface LoadResult { + state: GameState; + /** Milliseconds of offline time credited on this load (0 for a fresh game). */ + offlineMs: number; +} + +/** + * Load and hydrate game state, crediting offline progress. A missing or corrupt + * save yields a fresh game rather than throwing — never block boot on bad data. + */ +export async function loadGame( + content: Content, + backend: SaveBackend, + now: number, +): Promise { + const raw = await backend.load(); + if (!raw) { + return { state: createGameState(content), offlineMs: 0 }; + } + + let savedAt: number; + let state: GameState; + try { + const save = deserializeSave(raw); + const base = createGameState(content); + const activeActionId = + save.state.activeActionId && content.actionsById[save.state.activeActionId] + ? save.state.activeActionId + : null; + state = { + resources: { ...base.resources, ...save.state.resources }, + activeActionId, + actionElapsedMs: save.state.actionElapsedMs, + }; + savedAt = save.savedAt; + } catch { + return { state: createGameState(content), offlineMs: 0 }; + } + + const offlineMs = applyOfflineProgress(state, content, savedAt, now); + return { state, offlineMs }; +} + +export async function saveGame(state: GameState, backend: SaveBackend, now: number): Promise { + await backend.save(serializeSave(createSave(state, now))); +} diff --git a/src/state/runtime.ts b/src/state/runtime.ts new file mode 100644 index 0000000..63b3ea8 --- /dev/null +++ b/src/state/runtime.ts @@ -0,0 +1,120 @@ +import { content } from '../content'; +import { startAction as engineStartAction, type GameState, tickGame } from '../engine/game'; +import { advance, createTickLoop, TICK_MS, type TickLoop } from '../engine/tickLoop'; +import { createDefaultBackend, loadGame, type SaveBackend, saveGame } from './persistence'; +import { useGameStore } from './store'; +import { formatOfflineDuration, toView } from './viewModel'; + +/** + * The game runtime: the one place that owns the engine state and the wall clock. + * + * It drives the fixed-timestep tick loop off requestAnimationFrame, mirrors a + * view snapshot into the Zustand store at ~10 fps, autosaves on an interval, and + * persists on tab-hide / unload. Boot loads the save and credits offline time. + * All environment coupling (RAF, Date.now, DOM events, IndexedDB) lives here so + * the engine stays pure. + */ + +const PUBLISH_INTERVAL_MS = 100; // ~10 fps view refresh +const AUTOSAVE_INTERVAL_MS = 10_000; + +class GameRuntime { + private state: GameState | null = null; + private readonly loop: TickLoop = createTickLoop(); + private readonly backend: SaveBackend = createDefaultBackend(); + private rafId: number | null = null; + private lastPublishAt = 0; + private lastSaveAt = 0; + private booted = false; + + async boot(): Promise { + if (this.booted) { + return; + } + this.booted = true; + + const now = Date.now(); + const { state, offlineMs } = await loadGame(content, this.backend, now); + this.state = state; + this.lastSaveAt = now; + + const store = useGameStore.getState(); + store.appendLog( + offlineMs >= 1000 + ? `Welcome back — credited ${formatOfflineDuration(offlineMs)} of offline progress.` + : 'A new tale begins. Choose an action.', + ); + + this.publish(); + this.installLifecycleHooks(); + this.rafId = requestAnimationFrame(this.frame); + } + + /** Stop the loop. Used on teardown (e.g. HMR dispose); not needed in normal play. */ + stop(): void { + if (this.rafId !== null) { + cancelAnimationFrame(this.rafId); + this.rafId = null; + } + } + + startAction(actionId: string): void { + const state = this.state; + if (!state) { + return; + } + engineStartAction(state, content, actionId); + const action = content.actionsById[actionId]; + if (action) { + useGameStore.getState().appendLog(`Started: ${action.name}.`); + } + this.publish(); + } + + private readonly frame = (monoNow: number): void => { + const state = this.state; + if (state) { + advance(this.loop, monoNow, () => tickGame(state, content, TICK_MS)); + + if (monoNow - this.lastPublishAt >= PUBLISH_INTERVAL_MS) { + this.publish(); + this.lastPublishAt = monoNow; + } + + const wall = Date.now(); + if (wall - this.lastSaveAt >= AUTOSAVE_INTERVAL_MS) { + void this.save(wall); + } + } + this.rafId = requestAnimationFrame(this.frame); + }; + + private publish(): void { + const state = this.state; + if (state) { + useGameStore.getState().setView(toView(state, content)); + } + } + + private async save(now: number = Date.now()): Promise { + const state = this.state; + if (!state) { + return; + } + this.lastSaveAt = now; + await saveGame(state, this.backend, now); + } + + private installLifecycleHooks(): void { + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'hidden') { + void this.save(); + } + }); + window.addEventListener('beforeunload', () => { + void this.save(); + }); + } +} + +export const gameRuntime = new GameRuntime(); diff --git a/src/state/store.ts b/src/state/store.ts new file mode 100644 index 0000000..910e3fc --- /dev/null +++ b/src/state/store.ts @@ -0,0 +1,26 @@ +import { create } from 'zustand'; +import type { GameView } from './viewModel'; + +/** + * The view store. The runtime owns the authoritative engine state and pushes a + * fresh view snapshot here at ~10 fps; React components subscribe to slices of + * it. The store is a dumb mirror plus an event log — no game logic lives here. + */ + +const MAX_LOG_LINES = 50; + +export interface GameStoreState extends GameView { + log: string[]; + setView: (view: GameView) => void; + appendLog: (line: string) => void; +} + +export const useGameStore = create((set) => ({ + resources: [], + activeActionId: null, + actionName: null, + actionProgress: 0, + log: [], + setView: (view) => set(view), + appendLog: (line) => set((state) => ({ log: [...state.log, line].slice(-MAX_LOG_LINES) })), +})); diff --git a/src/state/viewModel.ts b/src/state/viewModel.ts new file mode 100644 index 0000000..77b1875 --- /dev/null +++ b/src/state/viewModel.ts @@ -0,0 +1,55 @@ +import type { Content } from '../content/schema'; +import type { GameState } from '../engine/game'; + +/** + * Pure mapping from engine state to the view model the React shell renders. + * No React, no store — just data shaping, so it can be unit-tested directly. + */ + +export interface ResourceView { + id: string; + name: string; + amount: number; +} + +export interface GameView { + resources: ResourceView[]; + activeActionId: string | null; + actionName: string | null; + /** Progress of the active action, clamped to 0..1. */ + actionProgress: number; +} + +export function toView(state: GameState, content: Content): GameView { + const resources: ResourceView[] = content.resources.map((resource) => ({ + id: resource.id, + name: resource.name, + amount: state.resources[resource.id] ?? 0, + })); + + const action = state.activeActionId ? content.actionsById[state.activeActionId] : undefined; + const actionProgress = action ? Math.min(1, state.actionElapsedMs / action.durationMs) : 0; + + return { + resources, + activeActionId: state.activeActionId, + actionName: action ? action.name : null, + actionProgress, + }; +} + +/** Render an offline gap as `45s`, `1m 30s`, `2m`, `1h 1m`, `2h`. */ +export function formatOfflineDuration(ms: number): string { + const totalSeconds = Math.floor(ms / 1000); + if (totalSeconds < 60) { + return `${totalSeconds}s`; + } + const totalMinutes = Math.floor(totalSeconds / 60); + if (totalMinutes < 60) { + const seconds = totalSeconds % 60; + return seconds === 0 ? `${totalMinutes}m` : `${totalMinutes}m ${seconds}s`; + } + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return minutes === 0 ? `${hours}h` : `${hours}h ${minutes}m`; +} diff --git a/src/ui/ActionPanel.tsx b/src/ui/ActionPanel.tsx new file mode 100644 index 0000000..027f5be --- /dev/null +++ b/src/ui/ActionPanel.tsx @@ -0,0 +1,38 @@ +import { content } from '../content'; +import { gameRuntime } from '../state/runtime'; +import { useGameStore } from '../state/store'; + +/** Action list — a start button per action, with a progress bar on the active one. */ +export function ActionPanel() { + const activeActionId = useGameStore((state) => state.activeActionId); + const actionProgress = useGameStore((state) => state.actionProgress); + + return ( +
+

Actions

+ {content.actions.map((action) => { + const isActive = action.id === activeActionId; + return ( + + ); + })} +
+ ); +} diff --git a/src/ui/App.tsx b/src/ui/App.tsx new file mode 100644 index 0000000..3d42523 --- /dev/null +++ b/src/ui/App.tsx @@ -0,0 +1,27 @@ +import { useEffect } from 'react'; +import { gameRuntime } from '../state/runtime'; +import { ActionPanel } from './ActionPanel'; +import { EventLog } from './EventLog'; +import { ResourceBar } from './ResourceBar'; + +/** + * Walking-skeleton view shell. Boots the runtime once on mount; everything else + * renders from the Zustand store the runtime feeds. M1 turns this into a game. + */ +export function App() { + useEffect(() => { + void gameRuntime.boot(); + }, []); + + return ( +
+
+

Idlegame

+

Walking skeleton — M0 scaffold.

+
+ + + +
+ ); +} diff --git a/src/ui/EventLog.tsx b/src/ui/EventLog.tsx new file mode 100644 index 0000000..6bb8f91 --- /dev/null +++ b/src/ui/EventLog.tsx @@ -0,0 +1,24 @@ +import { useGameStore } from '../state/store'; + +/** Event log stub — newest entries at the bottom. */ +export function EventLog() { + const log = useGameStore((state) => state.log); + + return ( +
+

Log

+
    + {log.length === 0 ? ( +
  1. + ) : ( + log.map((line, index) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: log is append-only; index is stable +
  2. + {line} +
  3. + )) + )} +
+
+ ); +} diff --git a/src/ui/ResourceBar.tsx b/src/ui/ResourceBar.tsx new file mode 100644 index 0000000..82d92cc --- /dev/null +++ b/src/ui/ResourceBar.tsx @@ -0,0 +1,27 @@ +import { formatNum } from '../engine/num'; +import { useGameStore } from '../state/store'; + +/** Resource readout — name + human-readable amount for each resource. */ +export function ResourceBar() { + const resources = useGameStore((state) => state.resources); + + return ( +
+ {resources.length === 0 ? ( + Loading… + ) : ( + resources.map((resource) => ( +
+ {resource.name} + + {formatNum(resource.amount)} + +
+ )) + )} +
+ ); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..be5b8b5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noEmit": true, + "resolveJsonModule": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "noImplicitOverride": true, + "forceConsistentCasingInFileNames": true, + + "skipLibCheck": true, + "types": ["vite/client"] + }, + "include": ["src", "vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..6eadaeb --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,43 @@ +/// +import tailwindcss from '@tailwindcss/vite'; +import react from '@vitejs/plugin-react'; +import { VitePWA } from 'vite-plugin-pwa'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + react(), + tailwindcss(), + VitePWA({ + registerType: 'autoUpdate', + manifest: { + name: 'Idlegame', + short_name: 'Idlegame', + description: 'Idle/incremental text-fantasy RPG.', + theme_color: '#0f172a', + background_color: '#0f172a', + display: 'standalone', + start_url: '/', + icons: [ + { src: 'pwa-192.png', sizes: '192x192', type: 'image/png' }, + { src: 'pwa-512.png', sizes: '512x512', type: 'image/png' }, + { src: 'pwa-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, + ], + }, + }), + ], + test: { + environment: 'node', + globals: false, + coverage: { + provider: 'v8', + include: ['src/engine/**'], + thresholds: { + lines: 80, + functions: 80, + branches: 80, + statements: 80, + }, + }, + }, +});