From 3b17f144098655ea8f88ff9b9d95a7a4e098b9bf Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Tue, 5 May 2026 22:44:26 +0700 Subject: [PATCH 01/19] Add Simon as sample app #24 --- cspell.config.yaml | 23 +- design/controls-preview.html | 1089 ++++ design/icon-proposals.html | 977 ++++ design/tech-icons.html | 157 + lefthook.yml | 21 +- package.json | 24 +- playwright.config.ts | 6 + pnpm-lock.yaml | 4783 +++++++++++++++-- prettier.config.js | 1 + sonar-project.properties | 2 +- src/app.html | 221 +- src/hooks.server.spec.ts | 76 + src/hooks.server.ts | 41 + src/lib/assets/favicon.svg | 1 + ...dio-distorted-electronic-click-472367.opus | Bin 0 -> 8772 bytes src/lib/components/Logo.svelte | 57 + src/lib/components/Logo.svelte.spec.ts | 52 + src/lib/game/credits-config.ts | 4 +- src/lib/messages/en.spec.ts | 63 + src/lib/messages/en.ts | 10 + src/lib/simon/SimonBoard.svelte | 152 + src/lib/simon/SimonBoard.svelte.spec.ts | 66 + src/lib/simon/SimonScene.svelte | 64 + src/lib/simon/SimonScene.svelte.spec.ts | 81 + src/lib/simon/audio.svelte.spec.ts | 134 + src/lib/simon/audio.ts | 77 + src/lib/simon/board-config.spec.ts | 46 + src/lib/simon/board-config.ts | 5 + src/lib/simon/credits.spec.ts | 139 + src/lib/simon/credits.ts | 245 + src/lib/simon/score.svelte.spec.ts | 292 + src/lib/simon/score.svelte.ts | 131 + src/lib/simon/simon-board-input.spec.ts | 70 + src/lib/simon/simon-board-input.ts | 42 + src/lib/simon/simon-flash.spec.ts | 160 + src/lib/simon/simon-flash.ts | 106 + src/lib/simon/simon.svelte.spec.ts | 484 ++ src/lib/simon/simon.svelte.ts | 220 + src/lib/simon/types.spec.ts | 27 + src/lib/simon/types.ts | 12 + src/routes/+layout.svelte | 46 + src/routes/+page.svelte | 35 +- src/routes/+page.ts | 1 + src/routes/demo/+page.svelte | 5 + src/routes/demo/playwright/+page.svelte | 1 + src/routes/demo/playwright/page.svelte.e2e.ts | 6 + src/routes/layout.css | 15 + src/routes/page.e2e.ts | 341 ++ src/routes/page.svelte.spec.ts | 25 + static/apple-touch-icon.png | Bin 0 -> 15452 bytes static/fonts/Orbitron.ttf | Bin 0 -> 17752 bytes static/fonts/PressStart2P.ttf | Bin 0 -> 115280 bytes static/icon-192.png | Bin 0 -> 16692 bytes static/icon-512.png | Bin 0 -> 61565 bytes static/icon.svg | 26 + static/robots.txt | 3 + svelte.config.js | 5 + tsconfig.json | 3 +- vite.config.ts | 47 +- wrangler.jsonc | 50 + 60 files changed, 10426 insertions(+), 344 deletions(-) create mode 100644 design/controls-preview.html create mode 100644 design/icon-proposals.html create mode 100644 design/tech-icons.html create mode 100644 playwright.config.ts create mode 100644 src/hooks.server.spec.ts create mode 100644 src/hooks.server.ts create mode 100644 src/lib/assets/favicon.svg create mode 100644 src/lib/assets/sound/dragon-studio-distorted-electronic-click-472367.opus create mode 100644 src/lib/components/Logo.svelte create mode 100644 src/lib/components/Logo.svelte.spec.ts create mode 100644 src/lib/messages/en.spec.ts create mode 100644 src/lib/simon/SimonBoard.svelte create mode 100644 src/lib/simon/SimonBoard.svelte.spec.ts create mode 100644 src/lib/simon/SimonScene.svelte create mode 100644 src/lib/simon/SimonScene.svelte.spec.ts create mode 100644 src/lib/simon/audio.svelte.spec.ts create mode 100644 src/lib/simon/audio.ts create mode 100644 src/lib/simon/board-config.spec.ts create mode 100644 src/lib/simon/board-config.ts create mode 100644 src/lib/simon/credits.spec.ts create mode 100644 src/lib/simon/credits.ts create mode 100644 src/lib/simon/score.svelte.spec.ts create mode 100644 src/lib/simon/score.svelte.ts create mode 100644 src/lib/simon/simon-board-input.spec.ts create mode 100644 src/lib/simon/simon-board-input.ts create mode 100644 src/lib/simon/simon-flash.spec.ts create mode 100644 src/lib/simon/simon-flash.ts create mode 100644 src/lib/simon/simon.svelte.spec.ts create mode 100644 src/lib/simon/simon.svelte.ts create mode 100644 src/lib/simon/types.spec.ts create mode 100644 src/lib/simon/types.ts create mode 100644 src/routes/+layout.svelte create mode 100644 src/routes/+page.ts create mode 100644 src/routes/demo/+page.svelte create mode 100644 src/routes/demo/playwright/+page.svelte create mode 100644 src/routes/demo/playwright/page.svelte.e2e.ts create mode 100644 src/routes/layout.css create mode 100644 src/routes/page.e2e.ts create mode 100644 src/routes/page.svelte.spec.ts create mode 100644 static/apple-touch-icon.png create mode 100644 static/fonts/Orbitron.ttf create mode 100644 static/fonts/PressStart2P.ttf create mode 100644 static/icon-192.png create mode 100644 static/icon-512.png create mode 100644 static/icon.svg create mode 100644 static/robots.txt create mode 100644 wrangler.jsonc diff --git a/cspell.config.yaml b/cspell.config.yaml index d59d25b..510fac9 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -1,15 +1,22 @@ version: "0.2" import: - "@joshuafolkken/kit/cspell/sveltekit" +ignorePaths: + - "**/credits-config.ts" + - "**/simon/credits.ts" words: - - threlte + - Baer + - Boisclair + - COEFF + - gameover + - Inerney + - mrdoob - Orbitron - - orbitron - - unstub + - Pixabay + - Raycaster + - SAMEORIGIN - spacebar + - threlte - WASD - - gameover - - GAMEOVER - - Raycaster - - raycaster -ignorePaths: [] + - trycloudflare + - unstub diff --git a/design/controls-preview.html b/design/controls-preview.html new file mode 100644 index 0000000..3b3b503 --- /dev/null +++ b/design/controls-preview.html @@ -0,0 +1,1089 @@ + + + + + + Controls Icon Candidates + + + +

CONTROLS ICON CANDIDATES

+ + +

SECTION 1 — SVG STYLE (keyboard + mouse)

+
+ +
+
A — Minimal line art
+
+ + + + + W + + + + + + A + + + + + + S + + + + + + D + + + + MOVE + + + + + + + + + + ESC + + + + + + Z + + + + / + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
B — Semi-opaque filled
+
+ + + + + W + + + + + + A + + + + + + S + + + + + + D + + + + MOVE + + + + + + + + + + ESC + + + + + + Z + + + + / + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
C — Neon glow  SELECTED
+
+ + + + + W + + + + + + A + + + + + + S + + + + + + D + + + + MOVE + + + + + + + + + + ESC + + + + + + Z + + + + / + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +

SECTION 2 — JUMP BUTTON ICON

+
+
+
A — Person silhouette
+
+ + + + + + + + +
+
+ +
+
B — Up arrow
+
+ + + + +
+
+ +
+
+ C — Double chevron  SELECTED +
+
+ + + + +
+
+ +
+
D — Spring / bounce
+
+ + + + + +
+
+
+ + +

SECTION 3 — MOBILE PAUSE BUTTON (top-right)

+
+
+
Pause button
+
+ + + + +
+
+
+ +

+ controls-preview.html — kept for reference +

+ + diff --git a/design/icon-proposals.html b/design/icon-proposals.html new file mode 100644 index 0000000..bf8bf4c --- /dev/null +++ b/design/icon-proposals.html @@ -0,0 +1,977 @@ + + + + + Simon Icon Proposals + + + +

SIMON — ICON PROPOSALS

+
+ +
+

A — Simon Disc(4色クアドラント)

+

+ Simonゲームの定番レイアウト。サイバーカラーで4つのボタンを表現。 +

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 512px dark bg +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 512px transparent +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 192px +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 32px +
+ +
+
+ + + + + + + + + + + + + + +
+ 16px +
+
+
+ + +
+

B — Pulse(サイバー同心円)

+

+ ゲームのシーケンス信号をイメージした放射状パターン。 +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 512px dark bg +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 512px transparent +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ 192px +
+
+
+ + + + + + + + + + +
+ 32px +
+
+
+ + + + + + + + + +
+ 16px +
+
+
+ + +
+

C — Arc Segments(扇形4ボタン)

+

+ 実際のSimonボタン形状に忠実な扇形。ギャップとグローで立体感を演出。 +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 512px dark bg +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ 512px transparent +
+
+
+ + + + + + + + + + + + + + + + + + +
+ 192px +
+
+
+ + + + + + + + +
+ 32px +
+
+
+ + + + + + + +
+ 16px +
+
+
+ + +
+

採点ポイント

+

+ A — + 一番わかりやすい。4色ブロックが即座にSimonと認識できる。ファビコン(16px)でも機能する。 +

+

+ B — + 最もアーティスティック。透過背景では小サイズで視認性が下がる。 +

+

+ C — + 実際のボードに最も忠実。扇形がゲームを想起させる。小サイズでも読める。 +

+
+
+ + diff --git a/design/tech-icons.html b/design/tech-icons.html new file mode 100644 index 0000000..a685d92 --- /dev/null +++ b/design/tech-icons.html @@ -0,0 +1,157 @@ + + + + + + Tech Stack + + + +
+

Built with

+

Simon

+

A Simon memory game built with a modern full-stack web toolchain.

+
+ + +
+
+ Svelte + Svelte 5 +
+
+ TypeScript + TypeScript +
+
+ Vite + Vite +
+
+ Tailwind + Tailwind +
+
+ Three.js + Three.js +
+
+ + +
+
+ Vitest + Vitest +
+
+ Playwright + Playwright +
+
+ ESLint + ESLint +
+
+ + +
+
+ Cloudflare + Cloudflare +
+
+ pnpm + pnpm +
+
+
+ + diff --git a/lefthook.yml b/lefthook.yml index 5e2347b..058fdda 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,21 +1,2 @@ extends: - - node_modules/@joshuafolkken/kit/lefthook/base.yml - -pre-commit: - parallel: true - commands: - cspell: - glob: '*.{svelte,ts,tsx,js,jsx,md,mdx,html,css,scss,yml,yaml,json}' - run: pnpm exec cspell lint --no-must-find-files --no-progress {staged_files} - - prettier: - glob: '*.{svelte,js,jsx,ts,tsx,mjs,cjs,html,css,scss,md,mdx,json,jsonc,yaml,yml}' - run: pnpm exec prettier --check --ignore-unknown {staged_files} - - eslint: - glob: '*.{svelte,js,jsx,ts,tsx,mjs,cjs}' - run: pnpm exec eslint --quiet --cache --cache-strategy content {staged_files} - - type-check: - glob: '*.{svelte,ts,js,mjs,cjs}' - run: pnpm exec svelte-kit sync && pnpm exec svelte-check --tsconfig ./tsconfig.json + - node_modules/@joshuafolkken/kit/lefthook/sveltekit.yml diff --git a/package.json b/package.json index 97fa7fc..e1731a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@joshuafolkken/game-kit", - "version": "0.7.0", + "version": "0.8.0", "keywords": [ "svelte" ], @@ -24,29 +24,40 @@ "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", "prepack": "svelte-kit sync && svelte-package && publint", + "gen:pre": "node -e \"const fs=require('node:fs');const d='.svelte-kit/cloudflare';if(fs.existsSync(d))fs.readdirSync(d).forEach(f=>f.startsWith('_worker.')&&fs.rmSync(d+'/'+f,{force:true}))\"", + "gen": "pnpm gen:pre && wrangler types", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "postinstall": "lefthook install", "josh": "josh", "size-limit": "size-limit" }, "devDependencies": { + "@axe-core/playwright": "^4.11.3", "@eslint/compat": "^2.0.5", "@eslint/js": "^10.0.1", "@ianvs/prettier-plugin-sort-imports": "^4.7.1", - "@joshuafolkken/kit": "0.128.0", + "@joshuafolkken/kit": "0.132.0", + "@playwright/test": "^1.59.1", "@size-limit/file": "^12.1.0", + "@sveltejs/adapter-cloudflare": "^7.2.8", "@sveltejs/kit": "^2.59.0", "@sveltejs/package": "^2.5.7", "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@tailwindcss/forms": "^0.5.11", + "@tailwindcss/typography": "^0.5.19", + "@tailwindcss/vite": "^4.2.4", "@threlte/core": "^8.5.11", "@threlte/extras": "^9.15.1", "@types/node": "^25.6.0", "@types/three": "^0.184.0", + "@vite-pwa/sveltekit": "^1.1.0", + "@vitest/browser-playwright": "^4.1.5", "cspell": "^10.0.0", "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.17.1", "globals": "^17.6.0", + "playwright": "^1.59.1", "prettier": "^3.8.3", "prettier-plugin-svelte": "^3.5.1", "prettier-plugin-tailwindcss": "^0.8.0", @@ -55,12 +66,15 @@ "size-limit": "^12.1.0", "svelte": "^5.55.5", "svelte-check": "^4.4.7", + "tailwindcss": "^4.2.4", "three": "^0.184.0", "typescript": "^6.0.3", "typescript-eslint": "^8.59.1", "vite": "^8.0.10", + "vite-plugin-pwa": "^1.2.0", "vitest": "^4.1.5", - "vitest-browser-svelte": "^2.1.1" + "vitest-browser-svelte": "^2.1.1", + "wrangler": "^4.87.0" }, "peerDependencies": { "@threlte/core": ">=8.0.0", @@ -83,7 +97,9 @@ "@joshuafolkken/kit", "esbuild", "lefthook", - "unrs-resolver" + "sharp", + "unrs-resolver", + "workerd" ] }, "sideEffects": [ diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..bdb1dfa --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,6 @@ +import { create_playwright_config } from '@joshuafolkken/kit/playwright/base' + +export default create_playwright_config({ + dev_port: 5173, + preview_port: 4173, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c6ad7c..4c08fb3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,30 +11,48 @@ importers: .: devDependencies: + '@axe-core/playwright': + specifier: ^4.11.3 + version: 4.11.3(playwright-core@1.59.1) '@eslint/compat': specifier: ^2.0.5 - version: 2.0.5(eslint@10.3.0) + version: 2.0.5(eslint@10.3.0(jiti@2.6.1)) '@eslint/js': specifier: ^10.0.1 - version: 10.0.1(eslint@10.3.0) + version: 10.0.1(eslint@10.3.0(jiti@2.6.1)) '@ianvs/prettier-plugin-sort-imports': specifier: ^4.7.1 version: 4.7.1(prettier@3.8.3) '@joshuafolkken/kit': - specifier: 0.128.0 - version: 0.128.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1)) + specifier: 0.132.0 + version: 0.132.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1)) + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 '@size-limit/file': specifier: ^12.1.0 - version: 12.1.0(size-limit@12.1.0) + version: 12.1.0(size-limit@12.1.0(jiti@2.6.1)) + '@sveltejs/adapter-cloudflare': + specifier: ^7.2.8 + version: 7.2.8(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(wrangler@4.87.0(@cloudflare/workers-types@4.20260504.1)) '@sveltejs/kit': specifier: ^2.59.0 - version: 2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + version: 2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) '@sveltejs/package': specifier: ^2.5.7 version: 2.5.7(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3) '@sveltejs/vite-plugin-svelte': specifier: ^7.0.0 - version: 7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + version: 7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + '@tailwindcss/forms': + specifier: ^0.5.11 + version: 0.5.11(tailwindcss@4.2.4) + '@tailwindcss/typography': + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@4.2.4) + '@tailwindcss/vite': + specifier: ^4.2.4 + version: 4.2.4(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) '@threlte/core': specifier: ^8.5.11 version: 8.5.11(svelte@5.55.5(@typescript-eslint/types@8.59.1))(three@0.184.0) @@ -47,21 +65,30 @@ importers: '@types/three': specifier: ^0.184.0 version: 0.184.0 + '@vite-pwa/sveltekit': + specifier: ^1.1.0 + version: 1.1.0(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + '@vitest/browser-playwright': + specifier: ^4.1.5 + version: 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) cspell: specifier: ^10.0.0 version: 10.0.0 eslint: specifier: ^10.3.0 - version: 10.3.0 + version: 10.3.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@10.3.0) + version: 10.1.8(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-svelte: specifier: ^3.17.1 - version: 3.17.1(eslint@10.3.0)(svelte@5.55.5(@typescript-eslint/types@8.59.1)) + version: 3.17.1(eslint@10.3.0(jiti@2.6.1))(svelte@5.55.5(@typescript-eslint/types@8.59.1)) globals: specifier: ^17.6.0 version: 17.6.0 + playwright: + specifier: ^1.59.1 + version: 1.59.1 prettier: specifier: ^3.8.3 version: 3.8.3 @@ -76,16 +103,19 @@ importers: version: 0.3.18 rollup-plugin-visualizer: specifier: ^7.0.1 - version: 7.0.1(rolldown@1.0.0-rc.17) + version: 7.0.1(rolldown@1.0.0-rc.17)(rollup@2.80.0) size-limit: specifier: ^12.1.0 - version: 12.1.0 + version: 12.1.0(jiti@2.6.1) svelte: specifier: ^5.55.5 version: 5.55.5(@typescript-eslint/types@8.59.1) svelte-check: specifier: ^4.4.7 version: 4.4.7(picomatch@4.0.4)(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3) + tailwindcss: + specifier: ^4.2.4 + version: 4.2.4 three: specifier: ^0.184.0 version: 0.184.0 @@ -94,31 +124,119 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.59.1 - version: 8.59.1(eslint@10.3.0)(typescript@6.0.3) + version: 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) vite: specifier: ^8.0.10 - version: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) + vite-plugin-pwa: + specifier: ^1.2.0 + version: 1.2.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) vitest: specifier: ^4.1.5 - version: 4.1.5(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) vitest-browser-svelte: specifier: ^2.1.1 - version: 2.1.1(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vitest@4.1.5(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4))) + version: 2.1.1(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vitest@4.1.5) + wrangler: + specifier: ^4.87.0 + version: 4.87.0(@cloudflare/workers-types@4.20260504.1) 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' + + '@axe-core/playwright@4.11.3': + resolution: {integrity: sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==} + peerDependencies: + playwright-core: '>= 1.0.0' + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + 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.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -127,176 +245,618 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.29.3': resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@cspell/cspell-bundled-dicts@10.0.0': - resolution: {integrity: sha512-ci410HEkng2582oOjlRHQtlGXwh+rUC/mVcN9dObLHpKhvPgzn2S6vT56pARstxxZpcCUG/oLhn3dCqdJlVzmA==} - engines: {node: '>=22.18.0'} - - '@cspell/cspell-json-reporter@10.0.0': - resolution: {integrity: sha512-hq5dui2ngYMZKbBauX7K1tkqlu81sX/uaCO49ZJLPjeZsE1auZLtHehDLfAr/ZXoj/dLYeQMSKiaJyE+qLVPHA==} - engines: {node: '>=22.18.0'} + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3': + resolution: {integrity: sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@cspell/cspell-performance-monitor@10.0.0': - resolution: {integrity: sha512-2vMh2pLt2dg/ArYvWjMP4v9HCm0pRhONsEJyc8oHdZyOYvX7trixX894I0M39+VBf3yWtPCEgYRh1UDXNIZRig==} - engines: {node: '>=22.18.0'} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 - '@cspell/cspell-pipe@10.0.0': - resolution: {integrity: sha512-qcgHhQvtEX8LSwIVsWrdUgiGim52lN3jT+ghlkdp72v+nBcGKsS2frEKTmbGLug+xcqppkzs6Q6VmsFp1MGtfA==} - engines: {node: '>=22.18.0'} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@cspell/cspell-resolver@10.0.0': - resolution: {integrity: sha512-8H+IUDB7SmrpcRugQ5f55qG81ZShk6nQRk+natLz41TEY98D8/LCmjHEkh/vhDPph9pVJmNUp7JcM2E1UHEa2g==} - engines: {node: '>=22.18.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 - '@cspell/cspell-service-bus@10.0.0': - resolution: {integrity: sha512-V7eigqg/TOoKwNK4Q18wr9KGxA8U5SFcoWVS8RyAxv4mQ+yNKHhvHEbRBifjPbQDer66afOrclb2UbqkIy2SOw==} - engines: {node: '>=22.18.0'} + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/cspell-types@10.0.0': - resolution: {integrity: sha512-IQA++Idqb8fZzkCbHq3+T+9yG9WpeaBxomOrG2KcR/Pj0CgnovzuApYKL2cc35UWLePboKinMeqEPiweFpHVug==} - engines: {node: '>=22.18.0'} + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/cspell-worker@10.0.0': - resolution: {integrity: sha512-V5bjMldNksilnja3fu8muQmkW5/guyua1yNVOhoE2r7othSvjuDlGMl8g2bQSrWjp+UXu0dP/BEZ6JC/IfNwTA==} - engines: {node: '>=22.18.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 - '@cspell/dict-ada@4.1.1': - resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-al@1.1.1': - resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-aws@4.0.17': - resolution: {integrity: sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==} + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-bash@4.2.2': - resolution: {integrity: sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==} + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-companies@3.2.11': - resolution: {integrity: sha512-0cmafbcz2pTHXLd59eLR1gvDvN6aWAOM0+cIL4LLF9GX9yB2iKDNrKsvs4tJRqutoaTdwNFBbV0FYv+6iCtebQ==} + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-cpp@7.0.2': - resolution: {integrity: sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==} + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-cryptocurrencies@5.0.5': - resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 - '@cspell/dict-csharp@4.0.8': - resolution: {integrity: sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==} + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-css@4.1.1': - resolution: {integrity: sha512-y/Vgo6qY08e1t9OqR56qjoFLBCpi4QfWMf2qzD1l9omRZwvSMQGRPz4x0bxkkkU4oocMAeztjzCsmLew//c/8w==} + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-dart@2.3.2': - resolution: {integrity: sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==} + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-data-science@2.0.13': - resolution: {integrity: sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==} + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-django@4.1.6': - resolution: {integrity: sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==} + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-docker@1.1.17': - resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@cspell/dict-dotnet@5.0.13': - resolution: {integrity: sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw==} + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-elixir@4.0.8': - resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-en-common-misspellings@2.1.12': - resolution: {integrity: sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==} + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-en-gb-mit@3.1.22': - resolution: {integrity: sha512-xE5Vg6gGdMkZ1Ep6z9SJMMioGkkT1GbxS5Mm0U3Ey1/H68P0G7cJcyiVr1CARxFbLqKE4QUpoV1o6jz1Z5Yl9Q==} + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-en_us@4.4.33': - resolution: {integrity: sha512-zWftVqfUStDA37wO1ZNDN1qMJOfcxELa8ucHW8W8wBAZY3TK5Nb6deLogCK/IJi/Qljf30dwwuqqv84Qqle9Tw==} + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-filetypes@3.0.18': - resolution: {integrity: sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw==} + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-flutter@1.1.1': - resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-fonts@4.0.6': - resolution: {integrity: sha512-aR/0csY01dNb0A1tw/UmN9rKgHruUxsYsvXu6YlSBJFu60s26SKr/k1o4LavpHTQ+lznlYMqAvuxGkE4Flliqw==} + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-fsharp@1.1.1': - resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-fullstack@3.2.9': - resolution: {integrity: sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==} + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-gaming-terms@1.1.2': - resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-git@3.1.0': - resolution: {integrity: sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==} + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-golang@6.0.26': - resolution: {integrity: sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==} + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-google@1.0.9': - resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-haskell@4.0.6': - resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@cspell/dict-html-symbol-entities@4.0.5': - resolution: {integrity: sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==} + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-html@4.0.15': - resolution: {integrity: sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==} + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-java@5.0.12': - resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-julia@1.1.1': - resolution: {integrity: sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==} + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-k8s@1.0.12': - resolution: {integrity: sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==} + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-kotlin@1.1.1': - resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-latex@5.1.0': - resolution: {integrity: sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw==} + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-lorem-ipsum@4.0.5': - resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-lua@4.0.8': - resolution: {integrity: sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==} + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-makefile@1.0.5': - resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspell/dict-markdown@2.0.16': - resolution: {integrity: sha512-976RRqKv6cwhrxdFCQP2DdnBVB86BF57oQtPHy4Zbf4jF/i2Oy29MCrxirnOBalS1W6KQeto7NdfDXRAwkK4PQ==} + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} peerDependencies: - '@cspell/dict-css': ^4.1.1 - '@cspell/dict-html': ^4.0.15 - '@cspell/dict-html-symbol-entities': ^4.0.5 - '@cspell/dict-typescript': ^3.2.3 + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.3': + resolution: {integrity: sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==} + 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.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} + + '@cloudflare/unenv-preset@2.16.1': + resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} + peerDependencies: + unenv: 2.0.0-rc.24 + workerd: '>1.20260305.0 <2.0.0-0' + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/workerd-darwin-64@1.20260430.1': + resolution: {integrity: sha512-ADohZUHf7NBvPp2PdZig2Opxx+hDkk3ve7jrTne3JRx9kDSB73zc4LzcEeEN8LKkbAcqZmvfRJfpChSlusu0lA==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20260430.1': + resolution: {integrity: sha512-/DoYC/1wHs+YRZzzqSQg1/EHB4hiv1yV5U8FnmapRRIzVaPtnt+ApeOXeMrIdKidgKOI8TqQzgBU8xbIM7Cl4Q==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20260430.1': + resolution: {integrity: sha512-koJhBWvEVZPKCVFtMLp2iMHlYr+lFCF47wGbnlKdHVlemV0zTxJEyHI8aLlrhPLhBmOmYLp46rXw09/qJkRIhQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20260430.1': + resolution: {integrity: sha512-hMdapNAzNQZDXGGkg4Slydc3fRJP5FUZLJVVcZCW/+imhhJro9Z1rv5n/wfR+txKoSWhTYR8eOp8Pyi2bzLzlw==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20260430.1': + resolution: {integrity: sha512-jS3ffixjb5USOwz4frw4WzCz0HrjVxkgyU3WiYb06N7hBAfN6eOrveAJ4QRef0+suK4V1vQFoB1oKdRBsXe9Dw==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workers-types@4.20260504.1': + resolution: {integrity: sha512-x/7BzBXSGBQS0wHdaY0/1bcaRszkkhCjTc5maQvNSHu/pTVdBF2f/L41W8pRSJJSbLL80ynW7O923O7v3PaLtg==} + + '@cspell/cspell-bundled-dicts@10.0.0': + resolution: {integrity: sha512-ci410HEkng2582oOjlRHQtlGXwh+rUC/mVcN9dObLHpKhvPgzn2S6vT56pARstxxZpcCUG/oLhn3dCqdJlVzmA==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-json-reporter@10.0.0': + resolution: {integrity: sha512-hq5dui2ngYMZKbBauX7K1tkqlu81sX/uaCO49ZJLPjeZsE1auZLtHehDLfAr/ZXoj/dLYeQMSKiaJyE+qLVPHA==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-performance-monitor@10.0.0': + resolution: {integrity: sha512-2vMh2pLt2dg/ArYvWjMP4v9HCm0pRhONsEJyc8oHdZyOYvX7trixX894I0M39+VBf3yWtPCEgYRh1UDXNIZRig==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-pipe@10.0.0': + resolution: {integrity: sha512-qcgHhQvtEX8LSwIVsWrdUgiGim52lN3jT+ghlkdp72v+nBcGKsS2frEKTmbGLug+xcqppkzs6Q6VmsFp1MGtfA==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-resolver@10.0.0': + resolution: {integrity: sha512-8H+IUDB7SmrpcRugQ5f55qG81ZShk6nQRk+natLz41TEY98D8/LCmjHEkh/vhDPph9pVJmNUp7JcM2E1UHEa2g==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-service-bus@10.0.0': + resolution: {integrity: sha512-V7eigqg/TOoKwNK4Q18wr9KGxA8U5SFcoWVS8RyAxv4mQ+yNKHhvHEbRBifjPbQDer66afOrclb2UbqkIy2SOw==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-types@10.0.0': + resolution: {integrity: sha512-IQA++Idqb8fZzkCbHq3+T+9yG9WpeaBxomOrG2KcR/Pj0CgnovzuApYKL2cc35UWLePboKinMeqEPiweFpHVug==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-worker@10.0.0': + resolution: {integrity: sha512-V5bjMldNksilnja3fu8muQmkW5/guyua1yNVOhoE2r7othSvjuDlGMl8g2bQSrWjp+UXu0dP/BEZ6JC/IfNwTA==} + engines: {node: '>=22.18.0'} + + '@cspell/dict-ada@4.1.1': + resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} + + '@cspell/dict-al@1.1.1': + resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} + + '@cspell/dict-aws@4.0.17': + resolution: {integrity: sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==} + + '@cspell/dict-bash@4.2.2': + resolution: {integrity: sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==} + + '@cspell/dict-companies@3.2.11': + resolution: {integrity: sha512-0cmafbcz2pTHXLd59eLR1gvDvN6aWAOM0+cIL4LLF9GX9yB2iKDNrKsvs4tJRqutoaTdwNFBbV0FYv+6iCtebQ==} + + '@cspell/dict-cpp@7.0.2': + resolution: {integrity: sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==} + + '@cspell/dict-cryptocurrencies@5.0.5': + resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} + + '@cspell/dict-csharp@4.0.8': + resolution: {integrity: sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==} + + '@cspell/dict-css@4.1.1': + resolution: {integrity: sha512-y/Vgo6qY08e1t9OqR56qjoFLBCpi4QfWMf2qzD1l9omRZwvSMQGRPz4x0bxkkkU4oocMAeztjzCsmLew//c/8w==} + + '@cspell/dict-dart@2.3.2': + resolution: {integrity: sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==} + + '@cspell/dict-data-science@2.0.13': + resolution: {integrity: sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==} + + '@cspell/dict-django@4.1.6': + resolution: {integrity: sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==} + + '@cspell/dict-docker@1.1.17': + resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} + + '@cspell/dict-dotnet@5.0.13': + resolution: {integrity: sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw==} + + '@cspell/dict-elixir@4.0.8': + resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} + + '@cspell/dict-en-common-misspellings@2.1.12': + resolution: {integrity: sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==} + + '@cspell/dict-en-gb-mit@3.1.22': + resolution: {integrity: sha512-xE5Vg6gGdMkZ1Ep6z9SJMMioGkkT1GbxS5Mm0U3Ey1/H68P0G7cJcyiVr1CARxFbLqKE4QUpoV1o6jz1Z5Yl9Q==} + + '@cspell/dict-en_us@4.4.33': + resolution: {integrity: sha512-zWftVqfUStDA37wO1ZNDN1qMJOfcxELa8ucHW8W8wBAZY3TK5Nb6deLogCK/IJi/Qljf30dwwuqqv84Qqle9Tw==} + + '@cspell/dict-filetypes@3.0.18': + resolution: {integrity: sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw==} + + '@cspell/dict-flutter@1.1.1': + resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} + + '@cspell/dict-fonts@4.0.6': + resolution: {integrity: sha512-aR/0csY01dNb0A1tw/UmN9rKgHruUxsYsvXu6YlSBJFu60s26SKr/k1o4LavpHTQ+lznlYMqAvuxGkE4Flliqw==} + + '@cspell/dict-fsharp@1.1.1': + resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} + + '@cspell/dict-fullstack@3.2.9': + resolution: {integrity: sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==} + + '@cspell/dict-gaming-terms@1.1.2': + resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} + + '@cspell/dict-git@3.1.0': + resolution: {integrity: sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==} + + '@cspell/dict-golang@6.0.26': + resolution: {integrity: sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==} + + '@cspell/dict-google@1.0.9': + resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} + + '@cspell/dict-haskell@4.0.6': + resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} + + '@cspell/dict-html-symbol-entities@4.0.5': + resolution: {integrity: sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==} + + '@cspell/dict-html@4.0.15': + resolution: {integrity: sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==} + + '@cspell/dict-java@5.0.12': + resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} + + '@cspell/dict-julia@1.1.1': + resolution: {integrity: sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==} + + '@cspell/dict-k8s@1.0.12': + resolution: {integrity: sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==} + + '@cspell/dict-kotlin@1.1.1': + resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} + + '@cspell/dict-latex@5.1.0': + resolution: {integrity: sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw==} + + '@cspell/dict-lorem-ipsum@4.0.5': + resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} + + '@cspell/dict-lua@4.0.8': + resolution: {integrity: sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==} + + '@cspell/dict-makefile@1.0.5': + resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} + + '@cspell/dict-markdown@2.0.16': + resolution: {integrity: sha512-976RRqKv6cwhrxdFCQP2DdnBVB86BF57oQtPHy4Zbf4jF/i2Oy29MCrxirnOBalS1W6KQeto7NdfDXRAwkK4PQ==} + peerDependencies: + '@cspell/dict-css': ^4.1.1 + '@cspell/dict-html': ^4.0.15 + '@cspell/dict-html-symbol-entities': ^4.0.5 + '@cspell/dict-typescript': ^3.2.3 '@cspell/dict-monkeyc@1.0.12': resolution: {integrity: sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==} @@ -378,6 +938,10 @@ packages: resolution: {integrity: sha512-q+0pHQ8DbqjemyaOn/mTtBRbCuKDqhnsVbZ6J9zkTsxPgMpccjy0s5oLXwomfrrxMRBH+UcbERwtUmE+SbnoIQ==} engines: {node: '>=22.18.0'} + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@dimforge/rapier3d-compat@0.12.0': resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} @@ -390,156 +954,312 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.27.7': resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.27.7': resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.27.7': resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.27.7': resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.27.7': resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.7': resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.27.7': resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.27.7': resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.27.7': resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.27.7': resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.27.7': resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.27.7': resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.27.7': resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.27.7': resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.27.7': resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.27.7': resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.7': resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.27.7': resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.7': resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.27.7': resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.27.7': resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.27.7': resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.27.7': resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.27.7': resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} @@ -632,18 +1352,170 @@ packages: prettier-plugin-ember-template-tag: optional: true - '@joshuafolkken/kit@0.128.0': - resolution: {integrity: sha512-y20tZsimo6ns27ernvS9qpCbu0OtUnG5RHm9lAPxMs4NJhVqpnX7+ZvdOXfTz1ONF8IW5IE7SNkxRy8JW7hzSQ==, tarball: https://npm.pkg.github.com/download/@joshuafolkken/kit/0.128.0/c706723e0c2256e1a20dd20bdbbd099ce9d5af62} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + + '@joshuafolkken/kit@0.132.0': + resolution: {integrity: sha512-5RVPADY1qfyfMk8XwGitrFUYUWhMmAUD7MMfTzSAGc1DrP2/M1OskVpZSgCvCwD977RU88yDsiUeizZfkaevFQ==, tarball: https://npm.pkg.github.com/download/@joshuafolkken/kit/0.132.0/92af7510b3e33710386fef47b16bb19313f48a95} engines: {node: '>=22.19.0'} hasBin: true peerDependencies: '@playwright/test': '>=1.0.0' svelte: '>=5.0.0' - peerDependenciesMeta: - '@playwright/test': - optional: true - svelte: - optional: true '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -655,12 +1527,18 @@ packages: 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==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@napi-rs/wasm-runtime@1.1.4': resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: @@ -678,6 +1556,15 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@poppinss/colors@4.1.6': + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + + '@poppinss/dumper@0.6.5': + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + + '@poppinss/exception@1.2.3': + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@publint/pack@0.1.4': resolution: {integrity: sha512-HDVTWq3H0uTXiU0eeSQntcVUTPP3GamzeXI41+x7uU9J65JgWQh3qWZHblR1i0npXfFtF+mxBiU2nJH8znxWnQ==} engines: {node: '>=18'} @@ -780,20 +1667,85 @@ packages: '@rolldown/pluginutils@1.0.0-rc.17': resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + '@rollup/plugin-babel@5.3.1': + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + + '@rollup/plugin-node-resolve@15.3.1': + resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@2.4.2': + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + + '@rollup/plugin-terser@0.4.4': + resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + '@size-limit/file@12.1.0': resolution: {integrity: sha512-eGwDcIufnNnvJRzv3liDOn6MAOGgmOTUdpeGQ2KuRTlgIgO54AJH1ilvktlJc6PIjNfwpYY0dOGyap1QgM1swQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: size-limit: 12.1.0 + '@speed-highlight/core@1.2.15': + resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@surma/rollup-plugin-off-main-thread@2.2.3': + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + '@sveltejs/acorn-typescript@1.0.9': resolution: {integrity: sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==} peerDependencies: acorn: ^8.9.0 + '@sveltejs/adapter-cloudflare@7.2.8': + resolution: {integrity: sha512-bIdhY/Fi4AQmqiBdQVKnafH1h9Gw+xbCvHyUu4EouC8rJOU02zwhi14k/FDhQ0mJF1iblIu3m8UNQ8GpGIvIOQ==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + wrangler: ^4.0.0 + '@sveltejs/kit@2.59.0': resolution: {integrity: sha512-WeJaGKvDf3uVQB4bnDHhM+BXCY34LC1v0HiPqnSpvNkjB54r8DAUP1rpk73s+5zprIirEKtUcVfgh6+fPODjzQ==} engines: {node: '>=18.13'} @@ -824,37 +1776,141 @@ packages: svelte: ^5.46.4 vite: ^8.0.0-beta.7 || ^8.0.0 - '@testing-library/svelte-core@1.0.0': - resolution: {integrity: sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==} - engines: {node: '>=16'} - peerDependencies: - svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 - - '@threejs-kit/instanced-sprite-mesh@2.5.1': - resolution: {integrity: sha512-pmt1ALRhbHhCJQTj2FuthH6PeLIeaM4hOuS2JO3kWSwlnvx/9xuUkjFR3JOi/myMqsH7pSsLIROSaBxDfttjeA==} + '@tailwindcss/forms@0.5.11': + resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==} peerDependencies: - three: '>=0.170.0' + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' - '@threlte/core@8.5.11': - resolution: {integrity: sha512-2IyYlrXAWG0c6UnBLnE6lBzaVfiPPF5nsGte3HIc6domRna4w9gG4WIYDnJ1FH8wcSnaq7afztbrpF01Y9m5vg==} - peerDependencies: - svelte: '>=5' - three: '>=0.160' + '@tailwindcss/node@4.2.4': + resolution: {integrity: sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==} - '@threlte/extras@9.15.1': - resolution: {integrity: sha512-+AhS+caKH9WIawwSWWYOlbqZzxPNW+kQ3PT9y4NyzzscCcVUPoFpZwd0izy8KEAIKM34J1uMs36cZff6U6LEjQ==} - peerDependencies: - svelte: '>=5' - three: '>=0.160' + '@tailwindcss/oxide-android-arm64@4.2.4': + resolution: {integrity: sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] - '@tweenjs/tween.js@23.1.3': - resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@tailwindcss/oxide-darwin-arm64@4.2.4': + resolution: {integrity: sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tailwindcss/oxide-darwin-x64@4.2.4': + resolution: {integrity: sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@tailwindcss/oxide-freebsd-x64@4.2.4': + resolution: {integrity: sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4': + resolution: {integrity: sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.4': + resolution: {integrity: sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.2.4': + resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.2.4': + resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.2.4': + resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.2.4': + resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==} + 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.2.4': + resolution: {integrity: sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.2.4': + resolution: {integrity: sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.2.4': + resolution: {integrity: sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==} + engines: {node: '>= 20'} + + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tailwindcss/vite@4.2.4': + resolution: {integrity: sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@testing-library/svelte-core@1.0.0': + resolution: {integrity: sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==} + engines: {node: '>=16'} + peerDependencies: + svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + + '@threejs-kit/instanced-sprite-mesh@2.5.1': + resolution: {integrity: sha512-pmt1ALRhbHhCJQTj2FuthH6PeLIeaM4hOuS2JO3kWSwlnvx/9xuUkjFR3JOi/myMqsH7pSsLIROSaBxDfttjeA==} + peerDependencies: + three: '>=0.170.0' + + '@threlte/core@8.5.11': + resolution: {integrity: sha512-2IyYlrXAWG0c6UnBLnE6lBzaVfiPPF5nsGte3HIc6domRna4w9gG4WIYDnJ1FH8wcSnaq7afztbrpF01Y9m5vg==} + peerDependencies: + svelte: '>=5' + three: '>=0.160' + + '@threlte/extras@9.15.1': + resolution: {integrity: sha512-+AhS+caKH9WIawwSWWYOlbqZzxPNW+kQ3PT9y4NyzzscCcVUPoFpZwd0izy8KEAIKM34J1uMs36cZff6U6LEjQ==} + peerDependencies: + svelte: '>=5' + three: '>=0.160' + + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -865,6 +1921,9 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@0.0.39': + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -874,6 +1933,9 @@ packages: '@types/node@25.6.0': resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/stats.js@0.17.4': resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} @@ -945,6 +2007,27 @@ packages: resolution: {integrity: sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vite-pwa/sveltekit@1.1.0': + resolution: {integrity: sha512-mMIf2tY+7Hg8jecpu/WY+Ki2ikoXy3hVmt3tOxi0K+lYYnKQrDYthuHireI0S+26Mg9BXzL7qQF1xeB5VYlYlg==} + engines: {node: '>=18.13'} + peerDependencies: + '@sveltejs/kit': ^1.3.1 || ^2.0.1 + '@vite-pwa/assets-generator': ^1.0.0 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + + '@vitest/browser-playwright@4.1.5': + resolution: {integrity: sha512-CWy0lBQJq97nionyJJdnaU4961IXTl43a7UCu5nHy51IoKxAt6PVIJLo+76rVl7KOOgcWHNkG4kbJu/pW7knvA==} + peerDependencies: + playwright: '*' + vitest: 4.1.5 + + '@vitest/browser@4.1.5': + resolution: {integrity: sha512-iCDGI8c4yg+xmjUg2VsygdAUSIIB4x5Rht/P68OXy1hPELKXHDkzh87lkuTcdYmemRChDkEpB426MmDjzC0ziA==} + peerDependencies: + vitest: 4.1.5 + '@vitest/expect@4.1.5': resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} @@ -987,6 +2070,9 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -1002,28 +2088,92 @@ packages: resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + 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'} + 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'} + + axe-core@4.11.4: + resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==} + engines: {node: '>=4'} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} 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.27: + resolution: {integrity: sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==} + engines: {node: '>=6.0.0'} + hasBin: true + bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@5.0.5: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} 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==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -1032,12 +2182,27 @@ packages: resolution: {integrity: sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA==} engines: {node: '>= 0.8'} + 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'} + camera-controls@3.1.2: resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==} engines: {node: '>=22.0.0', npm: '>=10.5.1'} peerDependencies: three: '>=0.126.1' + caniuse-lite@1.0.30001791: + resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -1070,10 +2235,17 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + comment-json@4.6.2: resolution: {integrity: sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==} engines: {node: '>= 6'} + 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==} @@ -1081,10 +2253,17 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + 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'} + cspell-config-lib@10.0.0: resolution: {integrity: sha512-HWK7SRnJ3N/kOThw/uzmXmQYCzBxu58Jkq2hHyte1voDl118BeNFoaNRWMpYdHbBi3kCj8gaZu8wGtm+Zmdhxw==} engines: {node: '>=22.18.0'} @@ -1131,6 +2310,18 @@ packages: engines: {node: '>=4'} hasBin: true + 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'} @@ -1158,10 +2349,18 @@ packages: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} + 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'} @@ -1172,19 +2371,67 @@ packages: diet-sprite@0.0.1: resolution: {integrity: sha512-zSHI2WDAn1wJqJYxcmjWfJv3Iw8oL9reQIbEyx2x2/EZ4/qmUTIo8/5qOCurnAcq61EwtJJaZ0XTy2NRYqpB5A==} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + earcut@2.2.4: resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.349: + resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + enhanced-resolve@5.21.0: + resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} + engines: {node: '>=10.13.0'} + env-paths@4.0.0: resolution: {integrity: sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==} engines: {node: '>=20'} + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + + 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.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + 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'} + + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.27.7: resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} @@ -1280,6 +2527,12 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1304,6 +2557,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1320,6 +2576,9 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1331,6 +2590,18 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + 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.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1341,10 +2612,28 @@ packages: 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'} + gensequence@8.0.8: resolution: {integrity: sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==} engines: {node: '>=20'} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1353,6 +2642,21 @@ packages: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} + 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'} + get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} @@ -1360,6 +2664,12 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + 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 + global-directory@5.0.0: resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} engines: {node: '>=20'} @@ -1372,6 +2682,43 @@ packages: resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} engines: {node: '>=18'} + 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-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.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1395,6 +2742,42 @@ packages: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} + 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.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + 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-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1404,6 +2787,14 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + 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-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1417,20 +2808,99 @@ packages: engines: {node: '>=14.16'} hasBin: true + 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-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + 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-safe-filename@0.1.1: resolution: {integrity: sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==} engines: {node: '>=20'} + 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'} + is-wsl@3.1.1: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + 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.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1449,11 +2919,26 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + 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'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} @@ -1462,6 +2947,13 @@ packages: known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1555,22 +3047,62 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + maath@0.10.8: resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==} peerDependencies: '@types/three': '>=0.134.0' three: '>=0.134.0' + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + meshoptimizer@1.1.1: resolution: {integrity: sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==} + mini-svg-data-uri@1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + + miniflare@4.20260430.0: + resolution: {integrity: sha512-MWvMm3Siho9Yj7lbJZidLs8hbrRvIcOrif2mnsHQZdvoKfedpea+GaN8XJxbpRcq0B2WzNI1BB1ihdnqes3/ZA==} + engines: {node: '>=22.0.0'} + hasBin: true + 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'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -1593,6 +3125,21 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + 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.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} @@ -1604,6 +3151,10 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1612,6 +3163,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} @@ -1623,12 +3177,26 @@ packages: 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} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -1643,6 +3211,14 @@ packages: engines: {node: '>=18'} hasBin: true + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss-load-config@3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -1667,6 +3243,10 @@ packages: peerDependencies: postcss: ^8.4.29 + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@7.1.1: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} @@ -1749,6 +3329,14 @@ packages: engines: {node: '>=14'} hasBin: true + 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} + publint@0.3.18: resolution: {integrity: sha512-JRJFeBTrfx4qLwEuGFPk+haJOJN97KnPuK01yj+4k/Wj5BgoOK5uNsivporiqBjk2JDaslg7qJOhGRnpltGeog==} engines: {node: '>=18'} @@ -1758,6 +3346,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -1766,6 +3357,36 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.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'} + + regexparam@3.0.0: + resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==} + engines: {node: '>=8'} + + 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'} @@ -1777,6 +3398,11 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + rolldown@1.0.0-rc.17: resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1795,6 +3421,11 @@ packages: rollup: optional: true + rollup@2.80.0: + resolution: {integrity: sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} @@ -1803,17 +3434,55 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + 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'} + scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + set-cookie-parser@3.1.0: resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + 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'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1822,9 +3491,29 @@ packages: 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.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + 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'} + sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} @@ -1839,6 +3528,10 @@ packages: jiti: optional: true + smob@1.6.1: + resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} + engines: {node: '>=20.0.0'} + smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} @@ -1847,28 +3540,80 @@ packages: 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.7.6: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + 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 + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + 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-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + 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-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + svelte-check@4.4.7: resolution: {integrity: sha512-JRafFTRmaPUOqmri4u1WuIKgBLiHi6wIaB57i99pmHq5BAc3ioIpzdUN/RX32ij9GhI6ALMHKvnVxu68sFZlag==} engines: {node: '>= 18.0.0'} @@ -1896,6 +3641,26 @@ packages: resolution: {integrity: sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw==} engines: {node: '>=18'} + tailwindcss@4.2.4: + resolution: {integrity: sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==} + + 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.46.2: + resolution: {integrity: sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==} + engines: {node: '>=10'} + hasBin: true + three-instanced-uniforms-mesh@0.52.4: resolution: {integrity: sha512-YwDBy05hfKZQtU+Rp0KyDf9yH4GxfhxMbVt9OYruxdgLfPwmDG5oAbGoW0DrKtKZSM3BfFcCiejiOHCjFBTeng==} peerDependencies: @@ -1938,6 +3703,9 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + troika-three-text@0.52.4: resolution: {integrity: sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==} peerDependencies: @@ -1972,6 +3740,26 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + 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.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + typescript-eslint@8.59.1: resolution: {integrity: sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1984,15 +3772,72 @@ packages: engines: {node: '>=14.17'} hasBin: true + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + undici-types@7.19.2: resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici@7.24.8: + resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + 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' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite-plugin-pwa@1.2.0: + resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==} + 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 + workbox-build: ^7.4.0 + workbox-window: ^7.4.0 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + vite@8.0.10: resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2097,91 +3942,841 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - webgl-sdf-generator@1.1.1: - resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==} + webgl-sdf-generator@1.1.1: + resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==} + + 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.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + 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 + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workbox-background-sync@7.4.0: + resolution: {integrity: sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w==} + + workbox-broadcast-update@7.4.0: + resolution: {integrity: sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA==} + + workbox-build@7.4.0: + resolution: {integrity: sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA==} + engines: {node: '>=20.0.0'} + + workbox-cacheable-response@7.4.0: + resolution: {integrity: sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ==} + + workbox-core@7.4.0: + resolution: {integrity: sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==} + + workbox-expiration@7.4.0: + resolution: {integrity: sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw==} + + workbox-google-analytics@7.4.0: + resolution: {integrity: sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ==} + + workbox-navigation-preload@7.4.0: + resolution: {integrity: sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==} + + workbox-precaching@7.4.0: + resolution: {integrity: sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==} + + workbox-range-requests@7.4.0: + resolution: {integrity: sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw==} + + workbox-recipes@7.4.0: + resolution: {integrity: sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==} + + workbox-routing@7.4.0: + resolution: {integrity: sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==} + + workbox-strategies@7.4.0: + resolution: {integrity: sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==} + + workbox-streams@7.4.0: + resolution: {integrity: sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg==} + + workbox-sw@7.4.0: + resolution: {integrity: sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw==} + + workbox-window@7.4.0: + resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==} + + workerd@1.20260430.1: + resolution: {integrity: sha512-KEgIWyiw3Jmn+DCd/L3ePo5fmiiYb/UcwKvDWPf/nLLOiwShDFzDSsegU5NY/JcwgvO/QsLHVi2FYrbkcXNY5Q==} + engines: {node: '>=16'} + hasBin: true + + worktop@0.8.0-next.18: + resolution: {integrity: sha512-+TvsA6VAVoMC3XDKR5MoC/qlLqDixEfOBysDEKnPIPou/NvoPWCAuXHXMsswwlvmEuvX56lQjvELLyLuzTKvRw==} + engines: {node: '>=12'} + + wrangler@4.87.0: + resolution: {integrity: sha512-lfhfKwLfQlowwgV0xhlYgE9fU3n0I30d4ccGY/rTCEm/n42Mjvlr0Ng3ZPNqlsrsKBcDR531V7dsPkgELvrk/Q==} + engines: {node: '>=22.0.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20260430.1 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + engines: {node: '>=20'} + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yaml@2.8.4: + resolution: {integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.0-beta.10: + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +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 + + '@axe-core/playwright@4.11.3(playwright-core@1.59.1)': + dependencies: + axe-core: 4.11.4 + playwright-core: 1.59.1 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@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.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - wsl-utils@0.3.1: - resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} - engines: {node: '>=20'} + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - xdg-basedir@5.1.0: - resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} - engines: {node: '>=12'} + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color - yaml@1.10.3: - resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} - engines: {node: '>= 6'} + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color - yaml@2.8.4: - resolution: {integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==} - engines: {node: '>= 14.6'} - hasBin: true + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - zimmerframe@1.1.4: - resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - zod@4.4.2: - resolution: {integrity: sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==} + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 -snapshots: + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/code-frame@7.29.0': + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/generator@7.29.1': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-globals@7.28.0': {} + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-string-parser@7.27.1': {} + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color - '@babel/parser@7.29.3': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': dependencies: @@ -2206,6 +4801,33 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@blazediff/core@1.9.1': {} + + '@cloudflare/kv-asset-handler@0.5.0': {} + + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260430.1)': + dependencies: + unenv: 2.0.0-rc.24 + optionalDependencies: + workerd: 1.20260430.1 + + '@cloudflare/workerd-darwin-64@1.20260430.1': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20260430.1': + optional: true + + '@cloudflare/workerd-linux-64@1.20260430.1': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20260430.1': + optional: true + + '@cloudflare/workerd-windows-64@1.20260430.1': + optional: true + + '@cloudflare/workers-types@4.20260504.1': {} + '@cspell/cspell-bundled-dicts@10.0.0': dependencies: '@cspell/dict-ada': 4.1.1 @@ -2428,6 +5050,10 @@ snapshots: '@cspell/url@10.0.0': {} + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@dimforge/rapier3d-compat@0.12.0': {} '@emnapi/core@1.10.0': @@ -2446,96 +5072,174 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.27.3': + optional: true + '@esbuild/aix-ppc64@0.27.7': optional: true + '@esbuild/android-arm64@0.27.3': + optional: true + '@esbuild/android-arm64@0.27.7': optional: true + '@esbuild/android-arm@0.27.3': + optional: true + '@esbuild/android-arm@0.27.7': optional: true + '@esbuild/android-x64@0.27.3': + optional: true + '@esbuild/android-x64@0.27.7': optional: true + '@esbuild/darwin-arm64@0.27.3': + optional: true + '@esbuild/darwin-arm64@0.27.7': optional: true + '@esbuild/darwin-x64@0.27.3': + optional: true + '@esbuild/darwin-x64@0.27.7': optional: true + '@esbuild/freebsd-arm64@0.27.3': + optional: true + '@esbuild/freebsd-arm64@0.27.7': optional: true + '@esbuild/freebsd-x64@0.27.3': + optional: true + '@esbuild/freebsd-x64@0.27.7': optional: true + '@esbuild/linux-arm64@0.27.3': + optional: true + '@esbuild/linux-arm64@0.27.7': optional: true + '@esbuild/linux-arm@0.27.3': + optional: true + '@esbuild/linux-arm@0.27.7': optional: true + '@esbuild/linux-ia32@0.27.3': + optional: true + '@esbuild/linux-ia32@0.27.7': optional: true + '@esbuild/linux-loong64@0.27.3': + optional: true + '@esbuild/linux-loong64@0.27.7': optional: true + '@esbuild/linux-mips64el@0.27.3': + optional: true + '@esbuild/linux-mips64el@0.27.7': optional: true + '@esbuild/linux-ppc64@0.27.3': + optional: true + '@esbuild/linux-ppc64@0.27.7': optional: true + '@esbuild/linux-riscv64@0.27.3': + optional: true + '@esbuild/linux-riscv64@0.27.7': optional: true + '@esbuild/linux-s390x@0.27.3': + optional: true + '@esbuild/linux-s390x@0.27.7': optional: true + '@esbuild/linux-x64@0.27.3': + optional: true + '@esbuild/linux-x64@0.27.7': optional: true + '@esbuild/netbsd-arm64@0.27.3': + optional: true + '@esbuild/netbsd-arm64@0.27.7': optional: true + '@esbuild/netbsd-x64@0.27.3': + optional: true + '@esbuild/netbsd-x64@0.27.7': optional: true + '@esbuild/openbsd-arm64@0.27.3': + optional: true + '@esbuild/openbsd-arm64@0.27.7': optional: true + '@esbuild/openbsd-x64@0.27.3': + optional: true + '@esbuild/openbsd-x64@0.27.7': optional: true + '@esbuild/openharmony-arm64@0.27.3': + optional: true + '@esbuild/openharmony-arm64@0.27.7': optional: true + '@esbuild/sunos-x64@0.27.3': + optional: true + '@esbuild/sunos-x64@0.27.7': optional: true + '@esbuild/win32-arm64@0.27.3': + optional: true + '@esbuild/win32-arm64@0.27.7': optional: true + '@esbuild/win32-ia32@0.27.3': + optional: true + '@esbuild/win32-ia32@0.27.7': optional: true + '@esbuild/win32-x64@0.27.3': + optional: true + '@esbuild/win32-x64@0.27.7': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0)': + '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0(jiti@2.6.1))': dependencies: - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@2.0.5(eslint@10.3.0)': + '@eslint/compat@2.0.5(eslint@10.3.0(jiti@2.6.1))': dependencies: '@eslint/core': 1.2.1 optionalDependencies: - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) '@eslint/config-array@0.23.5': dependencies: @@ -2553,9 +5257,9 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/js@10.0.1(eslint@10.3.0)': + '@eslint/js@10.0.1(eslint@10.3.0(jiti@2.6.1))': optionalDependencies: - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) '@eslint/object-schema@3.0.5': {} @@ -2591,15 +5295,112 @@ snapshots: transitivePeerDependencies: - supports-color - '@joshuafolkken/kit@0.128.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1))': + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.10.0 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@isaacs/cliui@9.0.0': {} + + '@joshuafolkken/kit@0.132.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1))': dependencies: + '@playwright/test': 1.59.1 js-yaml: 4.1.1 strip-json-comments: 5.0.3 - tsx: 4.21.0 - zod: 4.4.2 - optionalDependencies: - '@playwright/test': 1.59.1 svelte: 5.55.5(@typescript-eslint/types@8.59.1) + tsx: 4.21.0 + zod: 4.4.3 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -2613,6 +5414,11 @@ snapshots: '@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': @@ -2620,6 +5426,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -2629,12 +5440,23 @@ snapshots: '@oxc-project/types@0.127.0': {} - '@playwright/test@1.59.1': + '@playwright/test@1.59.1': + dependencies: + playwright: 1.59.1 + + '@polka/url@1.0.0-next.29': {} + + '@poppinss/colors@4.1.6': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.5': dependencies: - playwright: 1.59.1 - optional: true + '@poppinss/colors': 4.1.6 + '@sindresorhus/is': 7.2.0 + supports-color: 10.2.2 - '@polka/url@1.0.0-next.29': {} + '@poppinss/exception@1.2.3': {} '@publint/pack@0.1.4': {} @@ -2689,21 +5511,87 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.17': {} - '@size-limit/file@12.1.0(size-limit@12.1.0)': + '@rollup/plugin-babel@5.3.1(@babel/core@7.29.0)(rollup@2.80.0)': dependencies: - size-limit: 12.1.0 + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@rollup/pluginutils': 3.1.0(rollup@2.80.0) + rollup: 2.80.0 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-node-resolve@15.3.1(rollup@2.80.0)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@2.80.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.12 + optionalDependencies: + rollup: 2.80.0 + + '@rollup/plugin-replace@2.4.2(rollup@2.80.0)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.80.0) + magic-string: 0.25.9 + rollup: 2.80.0 + + '@rollup/plugin-terser@0.4.4(rollup@2.80.0)': + dependencies: + serialize-javascript: 6.0.2 + smob: 1.6.1 + terser: 5.46.2 + optionalDependencies: + rollup: 2.80.0 + + '@rollup/pluginutils@3.1.0(rollup@2.80.0)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.2 + rollup: 2.80.0 + + '@rollup/pluginutils@5.3.0(rollup@2.80.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 2.80.0 + + '@sindresorhus/is@7.2.0': {} + + '@size-limit/file@12.1.0(size-limit@12.1.0(jiti@2.6.1))': + dependencies: + size-limit: 12.1.0(jiti@2.6.1) + + '@speed-highlight/core@1.2.15': {} '@standard-schema/spec@1.1.0': {} + '@surma/rollup-plugin-off-main-thread@2.2.3': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.12 + '@sveltejs/acorn-typescript@1.0.9(acorn@8.16.0)': dependencies: acorn: 8.16.0 - '@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4))': + '@sveltejs/adapter-cloudflare@7.2.8(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(wrangler@4.87.0(@cloudflare/workers-types@4.20260504.1))': + dependencies: + '@cloudflare/workers-types': 4.20260504.1 + '@sveltejs/kit': 2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + worktop: 0.8.0-next.18 + wrangler: 4.87.0(@cloudflare/workers-types@4.20260504.1) + + '@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) - '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) '@types/cookie': 0.6.0 acorn: 8.16.0 cookie: 0.7.2 @@ -2715,7 +5603,7 @@ snapshots: set-cookie-parser: 3.1.0 sirv: 3.0.2 svelte: 5.55.5(@typescript-eslint/types@8.59.1) - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) optionalDependencies: typescript: 6.0.3 @@ -2730,14 +5618,92 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4))': + '@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))': dependencies: deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.1 svelte: 5.55.5(@typescript-eslint/types@8.59.1) - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4) - vitefu: 1.1.3(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) + vitefu: 1.1.3(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + + '@tailwindcss/forms@0.5.11(tailwindcss@4.2.4)': + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 4.2.4 + + '@tailwindcss/node@4.2.4': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.0 + jiti: 2.6.1 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.4 + + '@tailwindcss/oxide-android-arm64@4.2.4': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.2.4': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.2.4': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.2.4': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.2.4': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.4': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.2.4': + optional: true + + '@tailwindcss/oxide@4.2.4': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.4 + '@tailwindcss/oxide-darwin-arm64': 4.2.4 + '@tailwindcss/oxide-darwin-x64': 4.2.4 + '@tailwindcss/oxide-freebsd-x64': 4.2.4 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.4 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.4 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.4 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.4 + '@tailwindcss/oxide-linux-x64-musl': 4.2.4 + '@tailwindcss/oxide-wasm32-wasi': 4.2.4 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.4 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.4 + + '@tailwindcss/typography@0.5.19(tailwindcss@4.2.4)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.2.4 + + '@tailwindcss/vite@4.2.4(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))': + dependencies: + '@tailwindcss/node': 4.2.4 + '@tailwindcss/oxide': 4.2.4 + tailwindcss: 4.2.4 + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) '@testing-library/svelte-core@1.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))': dependencies: @@ -2790,6 +5756,8 @@ snapshots: '@types/esrecurse@4.3.1': {} + '@types/estree@0.0.39': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -2798,6 +5766,8 @@ snapshots: dependencies: undici-types: 7.19.2 + '@types/resolve@1.20.2': {} + '@types/stats.js@0.17.4': {} '@types/three@0.184.0': @@ -2813,15 +5783,15 @@ snapshots: '@types/webxr@0.5.24': {} - '@typescript-eslint/eslint-plugin@8.59.1(@typescript-eslint/parser@8.59.1(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.59.1(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.1(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.59.1 - '@typescript-eslint/type-utils': 8.59.1(eslint@10.3.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.1(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.59.1 - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -2829,14 +5799,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.1(eslint@10.3.0)(typescript@6.0.3)': + '@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.59.1 '@typescript-eslint/types': 8.59.1 '@typescript-eslint/typescript-estree': 8.59.1(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.59.1 debug: 4.4.3 - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2859,13 +5829,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.59.1(eslint@10.3.0)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.59.1 '@typescript-eslint/typescript-estree': 8.59.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.1(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) debug: 4.4.3 - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -2888,13 +5858,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.1(eslint@10.3.0)(typescript@6.0.3)': + '@typescript-eslint/utils@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.59.1 '@typescript-eslint/types': 8.59.1 '@typescript-eslint/typescript-estree': 8.59.1(typescript@6.0.3) - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2904,6 +5874,48 @@ snapshots: '@typescript-eslint/types': 8.59.1 eslint-visitor-keys: 5.0.1 + '@vite-pwa/sveltekit@1.1.0(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0)': + dependencies: + '@sveltejs/kit': 2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + kolorist: 1.8.0 + tinyglobby: 0.2.16 + vite-plugin-pwa: 1.2.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + transitivePeerDependencies: + - supports-color + - vite + - workbox-build + - workbox-window + + '@vitest/browser-playwright@4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5)': + dependencies: + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + playwright: 1.59.1 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/utils': 4.1.5 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + '@vitest/expect@4.1.5': dependencies: '@standard-schema/spec': 1.1.0 @@ -2913,13 +5925,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4))': + '@vitest/mocker@4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))': dependencies: '@vitest/spy': 4.1.5 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) '@vitest/pretty-format@4.1.5': dependencies: @@ -2958,6 +5970,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@6.2.2: {} ansi-styles@6.2.3: {} @@ -2966,32 +5985,122 @@ snapshots: aria-query@5.3.1: {} + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + array-timsort@1.0.3: {} + 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: {} + 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 + + axe-core@4.11.4: {} + axobject-query@4.1.0: {} + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + baseline-browser-mapping@2.10.27: {} + bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 + blake3-wasm@2.1.5: {} + + brace-expansion@2.1.0: + dependencies: + balanced-match: 1.0.2 + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.27 + caniuse-lite: 1.0.30001791 + electron-to-chromium: 1.5.349 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + buffer-from@1.1.2: {} + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 bytes-iec@3.1.1: {} + 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 + camera-controls@3.1.2(three@0.184.0): dependencies: three: 0.184.0 + caniuse-lite@1.0.30001791: {} + chai@6.2.2: {} chalk-template@1.1.2: @@ -3018,21 +6127,31 @@ snapshots: commander@14.0.3: {} + commander@2.20.3: {} + comment-json@4.6.2: dependencies: array-timsort: 1.0.3 esprima: 4.0.1 + common-tags@1.8.2: {} + convert-source-map@2.0.0: {} cookie@0.7.2: {} + 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: {} + cspell-config-lib@10.0.0: dependencies: '@cspell/cspell-types': 10.0.0 @@ -3125,6 +6244,24 @@ snapshots: cssesc@3.0.0: {} + 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 @@ -3142,24 +6279,162 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.1 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + define-lazy-prop@3.0.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: {} devalue@5.8.0: {} diet-sprite@0.0.1: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + earcut@2.2.4: {} + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.349: {} + emoji-regex@10.6.0: {} + enhanced-resolve@5.21.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + env-paths@4.0.0: dependencies: is-safe-filename: 0.1.1 + error-stack-parser-es@1.0.5: {} + + 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.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + 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.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + es-module-lexer@2.1.0: {} + es-object-atoms@1.1.1: + 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.3 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + esbuild@0.27.7: optionalDependencies: '@esbuild/aix-ppc64': 0.27.7 @@ -3193,15 +6468,15 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.3.0): + eslint-config-prettier@10.1.8(eslint@10.3.0(jiti@2.6.1)): dependencies: - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) - eslint-plugin-svelte@3.17.1(eslint@10.3.0)(svelte@5.55.5(@typescript-eslint/types@8.59.1)): + eslint-plugin-svelte@3.17.1(eslint@10.3.0(jiti@2.6.1))(svelte@5.55.5(@typescript-eslint/types@8.59.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) '@jridgewell/sourcemap-codec': 1.5.5 - eslint: 10.3.0 + eslint: 10.3.0(jiti@2.6.1) esutils: 2.0.3 globals: 16.5.0 known-css-properties: 0.37.0 @@ -3233,9 +6508,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.3.0: + eslint@10.3.0(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.5.5 @@ -3265,6 +6540,8 @@ snapshots: minimatch: 10.2.5 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -3300,6 +6577,10 @@ snapshots: estraverse@5.3.0: {} + estree-walker@1.0.1: {} + + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -3316,6 +6597,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -3326,6 +6609,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3338,18 +6625,77 @@ snapshots: flatted@3.4.2: {} + 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.2: optional: true 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.3 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + gensequence@8.0.8: {} + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} get-east-asian-width@1.5.0: {} + 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.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + 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.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -3358,6 +6704,15 @@ snapshots: dependencies: is-glob: 4.0.3 + 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 + global-directory@5.0.0: dependencies: ini: 6.0.0 @@ -3366,44 +6721,203 @@ snapshots: globals@17.6.0: {} + 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-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.3: + dependencies: + function-bind: 1.1.2 + + idb@7.1.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} - import-fresh@4.0.0: {} + import-fresh@4.0.0: {} + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + ini@6.0.0: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.3 + side-channel: 1.1.0 + + 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.1: + dependencies: + hasown: 2.0.3 + + 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-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + 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-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.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-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 - import-meta-resolve@4.2.0: {} + is-regexp@1.0.0: {} - imurmurhash@0.1.4: {} + is-safe-filename@0.1.1: {} - ini@6.0.0: {} + is-set@2.0.3: {} - is-docker@3.0.0: {} + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 - is-extglob@2.1.1: {} + is-stream@2.0.1: {} - is-glob@4.0.3: + is-string@1.1.1: dependencies: - is-extglob: 2.1.1 + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - is-in-ssh@1.0.0: {} + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 - is-inside-container@1.0.0: + is-typed-array@1.1.15: dependencies: - is-docker: 3.0.0 + which-typed-array: 1.1.20 - is-reference@3.0.3: + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: dependencies: - '@types/estree': 1.0.8 + call-bound: 1.0.4 - is-safe-filename@0.1.1: {} + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 + isarray@2.0.5: {} + isexe@2.0.0: {} + 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.6.1: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -3416,8 +6930,20 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3426,6 +6952,10 @@ snapshots: known-css-properties@0.37.0: {} + kolorist@1.8.0: {} + + leven@3.1.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -3490,21 +7020,59 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.debounce@4.0.8: {} + + lodash.sortby@4.7.0: {} + + lodash@4.18.1: {} + + lru-cache@11.3.5: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + maath@0.10.8(@types/three@0.184.0)(three@0.184.0): dependencies: '@types/three': 0.184.0 three: 0.184.0 + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + math-intrinsics@1.1.0: {} + meshoptimizer@1.1.1: {} + mini-svg-data-uri@1.4.4: {} + + miniflare@4.20260430.0: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + sharp: 0.34.5 + undici: 7.24.8 + workerd: 1.20260430.1 + ws: 8.18.0 + youch: 4.1.0-beta.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.0 + + minipass@7.1.3: {} + mri@1.2.0: {} mrmime@2.0.1: {} @@ -3519,6 +7087,21 @@ snapshots: natural-compare@1.4.0: {} + node-releases@2.0.38: {} + + 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.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + obug@2.1.1: {} open@11.0.0: @@ -3539,6 +7122,12 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -3547,27 +7136,42 @@ snapshots: dependencies: p-limit: 3.1.0 + package-json-from-dist@1.0.1: {} + package-manager-detector@1.6.0: {} path-exists@4.0.0: {} path-key@3.1.1: {} + path-parse@1.0.7: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.3.5 + minipass: 7.1.3 + + path-to-regexp@6.3.0: {} + pathe@2.0.3: {} picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.4: {} - playwright-core@1.59.1: - optional: true + playwright-core@1.59.1: {} playwright@1.59.1: dependencies: playwright-core: 1.59.1 optionalDependencies: fsevents: 2.3.2 - optional: true + + pngjs@7.0.0: {} + + possible-typed-array-names@1.1.0: {} postcss-load-config@3.1.4(postcss@8.5.13): dependencies: @@ -3584,6 +7188,11 @@ snapshots: dependencies: postcss: 8.5.13 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 @@ -3613,6 +7222,10 @@ snapshots: prettier@3.8.3: {} + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + publint@0.3.18: dependencies: '@publint/pack': 0.1.4 @@ -3622,16 +7235,70 @@ snapshots: punycode@2.3.1: {} + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + readdirp@4.1.2: {} readdirp@5.0.0: {} + 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.1 + 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 + + regexparam@3.0.0: {} + + 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-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + rolldown@1.0.0-rc.17: dependencies: '@oxc-project/types': 0.127.0 @@ -3653,7 +7320,7 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 - rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.17): + rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.17)(rollup@2.80.0): dependencies: open: 11.0.0 picomatch: 4.0.4 @@ -3661,6 +7328,11 @@ snapshots: yargs: 18.0.0 optionalDependencies: rolldown: 1.0.0-rc.17 + rollup: 2.80.0 + + rollup@2.80.0: + optionalDependencies: + fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -3668,56 +7340,239 @@ snapshots: dependencies: mri: 1.2.0 + 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-buffer@5.2.1: {} + + 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 + scule@1.3.0: {} + semver@6.3.1: {} + semver@7.7.4: {} + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + set-cookie-parser@3.1.0: {} + 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.1 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + 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.0: + 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: {} + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 - size-limit@12.1.0: + size-limit@12.1.0(jiti@2.6.1): dependencies: bytes-iec: 3.1.1 lilconfig: 3.1.3 nanospinner: 1.2.2 picocolors: 1.1.1 tinyglobby: 0.2.16 + optionalDependencies: + jiti: 2.6.1 + + smob@1.6.1: {} smol-toml@1.6.1: {} 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.7.6: {} + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + sourcemap-codec@1.4.8: {} + 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-width@7.2.0: dependencies: emoji-regex: 10.6.0 get-east-asian-width: 1.5.0 strip-ansi: 7.2.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.1 + 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.0 + + string.prototype.trim@1.2.10: + 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.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + 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-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 + strip-comments@2.0.1: {} + strip-json-comments@5.0.3: {} + supports-color@10.2.2: {} + + supports-preserve-symlinks-flag@1.0.0: {} + svelte-check@4.4.7(picomatch@4.0.4)(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -3770,6 +7625,26 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' + tailwindcss@4.2.4: {} + + 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.46.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + three-instanced-uniforms-mesh@0.52.4(three@0.184.0): dependencies: three: 0.184.0 @@ -3804,6 +7679,10 @@ snapshots: totalist@3.0.1: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + troika-three-text@0.52.4(three@0.184.0): dependencies: bidi-js: 1.0.3 @@ -3838,28 +7717,112 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.59.1(eslint@10.3.0)(typescript@6.0.3): + 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.7: dependencies: - '@typescript-eslint/eslint-plugin': 8.59.1(@typescript-eslint/parser@8.59.1(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3) - '@typescript-eslint/parser': 8.59.1(eslint@10.3.0)(typescript@6.0.3) + 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-eslint@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.1(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.59.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.1(eslint@10.3.0)(typescript@6.0.3) - eslint: 10.3.0 + '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.3.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color 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.19.2: {} + undici@7.24.8: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + 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 + uri-js@4.4.1: dependencies: punycode: 2.3.1 util-deprecate@1.0.2: {} - vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4): + vite-plugin-pwa@1.2.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.16 + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) + workbox-build: 7.4.0 + workbox-window: 7.4.0 + transitivePeerDependencies: + - supports-color + + vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -3870,23 +7833,25 @@ snapshots: '@types/node': 25.6.0 esbuild: 0.27.7 fsevents: 2.3.3 + jiti: 2.6.1 + terser: 5.46.2 tsx: 4.21.0 yaml: 2.8.4 - vitefu@1.1.3(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)): + vitefu@1.1.3(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)): optionalDependencies: - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) - vitest-browser-svelte@2.1.1(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vitest@4.1.5(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4))): + vitest-browser-svelte@2.1.1(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vitest@4.1.5): dependencies: '@testing-library/svelte-core': 1.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1)) svelte: 5.55.5(@typescript-eslint/types@8.59.1) - vitest: 4.1.5(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + vitest: 4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) - vitest@4.1.5(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)): + vitest@4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)): dependencies: '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) '@vitest/pretty-format': 4.1.5 '@vitest/runner': 4.1.5 '@vitest/snapshot': 4.1.5 @@ -3903,10 +7868,11 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.6.0 + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) transitivePeerDependencies: - msw @@ -3916,6 +7882,55 @@ snapshots: webgl-sdf-generator@1.1.1: {} + 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.20 + + 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.20: + 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 @@ -3927,12 +7942,159 @@ snapshots: word-wrap@1.2.5: {} + workbox-background-sync@7.4.0: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.0 + + workbox-broadcast-update@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-build@7.4.0: + dependencies: + '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) + '@babel/core': 7.29.0 + '@babel/preset-env': 7.29.3(@babel/core@7.29.0) + '@babel/runtime': 7.29.2 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(rollup@2.80.0) + '@rollup/plugin-node-resolve': 15.3.1(rollup@2.80.0) + '@rollup/plugin-replace': 2.4.2(rollup@2.80.0) + '@rollup/plugin-terser': 0.4.4(rollup@2.80.0) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.20.0 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 11.1.0 + lodash: 4.18.1 + pretty-bytes: 5.6.0 + rollup: 2.80.0 + 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.0 + workbox-broadcast-update: 7.4.0 + workbox-cacheable-response: 7.4.0 + workbox-core: 7.4.0 + workbox-expiration: 7.4.0 + workbox-google-analytics: 7.4.0 + workbox-navigation-preload: 7.4.0 + workbox-precaching: 7.4.0 + workbox-range-requests: 7.4.0 + workbox-recipes: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + workbox-streams: 7.4.0 + workbox-sw: 7.4.0 + workbox-window: 7.4.0 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-core@7.4.0: {} + + workbox-expiration@7.4.0: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.0 + + workbox-google-analytics@7.4.0: + dependencies: + workbox-background-sync: 7.4.0 + workbox-core: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + + workbox-navigation-preload@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-precaching@7.4.0: + dependencies: + workbox-core: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + + workbox-range-requests@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-recipes@7.4.0: + dependencies: + workbox-cacheable-response: 7.4.0 + workbox-core: 7.4.0 + workbox-expiration: 7.4.0 + workbox-precaching: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + + workbox-routing@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-strategies@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-streams@7.4.0: + dependencies: + workbox-core: 7.4.0 + workbox-routing: 7.4.0 + + workbox-sw@7.4.0: {} + + workbox-window@7.4.0: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 7.4.0 + + workerd@1.20260430.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20260430.1 + '@cloudflare/workerd-darwin-arm64': 1.20260430.1 + '@cloudflare/workerd-linux-64': 1.20260430.1 + '@cloudflare/workerd-linux-arm64': 1.20260430.1 + '@cloudflare/workerd-windows-64': 1.20260430.1 + + worktop@0.8.0-next.18: + dependencies: + mrmime: 2.0.1 + regexparam: 3.0.0 + + wrangler@4.87.0(@cloudflare/workers-types@4.20260504.1): + dependencies: + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260430.1) + blake3-wasm: 2.1.5 + esbuild: 0.27.3 + miniflare: 4.20260430.0 + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.24 + workerd: 1.20260430.1 + optionalDependencies: + '@cloudflare/workers-types': 4.20260504.1 + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 string-width: 7.2.0 strip-ansi: 7.2.0 + ws@8.18.0: {} + + ws@8.20.0: {} + wsl-utils@0.3.1: dependencies: is-wsl: 3.1.1 @@ -3942,6 +8104,8 @@ snapshots: y18n@5.0.8: {} + yallist@3.1.1: {} + yaml@1.10.3: {} yaml@2.8.4: {} @@ -3959,6 +8123,19 @@ snapshots: yocto-queue@0.1.0: {} + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.3 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.10: + dependencies: + '@poppinss/colors': 4.1.6 + '@poppinss/dumper': 0.6.5 + '@speed-highlight/core': 1.2.15 + cookie: 0.7.2 + youch-core: 0.3.3 + zimmerframe@1.1.4: {} - zod@4.4.2: {} + zod@4.4.3: {} diff --git a/prettier.config.js b/prettier.config.js index da0fb71..e241051 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -2,4 +2,5 @@ import { config } from '@joshuafolkken/kit/prettier' export default { ...config, + tailwindStylesheet: './src/routes/layout.css', } diff --git a/sonar-project.properties b/sonar-project.properties index 8b89206..1213df6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -14,7 +14,7 @@ sonar.coverage.exclusions=**/* # Exclude upstream-synced bootstrap scripts and dev/CI tooling from analysis. # .claude/ hosts setup shell scripts synced from joshuafolkken/tasks (must not drift). # scripts/ hosts git / issue / overrides / telegram workflow tooling — not production code. -sonar.exclusions=.claude/**,scripts/** +sonar.exclusions=.claude/**,scripts/**,design/** # Files to exclude from the duplicate check sonar.cpd.exclusions=**/*.spec.ts,**/*.spec.svelte.ts,**/*.test.ts diff --git a/src/app.html b/src/app.html index 359c394..bdcb31e 100644 --- a/src/app.html +++ b/src/app.html @@ -2,12 +2,231 @@ - + + + + + + %sveltekit.head% + + + +

Joshua Folkken

+ +
+

SIMON

+

v__APP_VERSION__

+
+

+ DOWNLOADING... + 0% +

+ +
+
%sveltekit.body%
diff --git a/src/hooks.server.spec.ts b/src/hooks.server.spec.ts new file mode 100644 index 0000000..d13ecb4 --- /dev/null +++ b/src/hooks.server.spec.ts @@ -0,0 +1,76 @@ +import { readFileSync } from 'node:fs' +import type { RequestEvent, ResolveOptions } from '@sveltejs/kit' +import { describe, expect, it, vi } from 'vitest' +import { handle, inject_version } from './hooks.server' + +const { version } = JSON.parse( + readFileSync(new URL('../package.json', import.meta.url), 'utf-8'), +) as { version: string } + +type ResolveFn = (event: RequestEvent, opts?: ResolveOptions) => Promise + +function make_resolve(): ResolveFn { + return vi.fn().mockResolvedValue(new Response(null, { status: 200 })) +} + +describe('inject_version', () => { + it('replaces the placeholder with the package version', () => { + const html = '

v__APP_VERSION__

' + expect(inject_version(html)).toBe(`

v${version}

`) + }) + + it('replaces all occurrences of the placeholder', () => { + const html = '__APP_VERSION__ and __APP_VERSION__' + expect(inject_version(html)).toBe(`${version} and ${version}`) + }) + + it('passes through html that has no placeholder', () => { + const html = '

no placeholder here

' + expect(inject_version(html)).toBe(html) + }) +}) + +describe('handle', () => { + it('adds X-Frame-Options: SAMEORIGIN', async () => { + const response = await handle({ event: {} as RequestEvent, resolve: make_resolve() }) + expect(response.headers.get('x-frame-options')).toBe('SAMEORIGIN') + }) + + it('adds X-Content-Type-Options: nosniff', async () => { + const response = await handle({ event: {} as RequestEvent, resolve: make_resolve() }) + expect(response.headers.get('x-content-type-options')).toBe('nosniff') + }) + + it('adds Referrer-Policy: strict-origin-when-cross-origin', async () => { + const response = await handle({ event: {} as RequestEvent, resolve: make_resolve() }) + expect(response.headers.get('referrer-policy')).toBe('strict-origin-when-cross-origin') + }) + + it('adds Permissions-Policy restricting camera, microphone, geolocation, payment', async () => { + const response = await handle({ event: {} as RequestEvent, resolve: make_resolve() }) + const policy = response.headers.get('permissions-policy') + expect(policy).toContain('camera=()') + expect(policy).toContain('microphone=()') + expect(policy).toContain('geolocation=()') + expect(policy).toContain('payment=()') + }) + + it("adds Content-Security-Policy with default-src 'self'", async () => { + const response = await handle({ event: {} as RequestEvent, resolve: make_resolve() }) + const csp = response.headers.get('content-security-policy') + expect(csp).toContain("default-src 'self'") + expect(csp).toContain("object-src 'none'") + expect(csp).toContain("frame-ancestors 'self'") + }) + + it('still injects app version via transformPageChunk', async () => { + let captured_transform: ResolveOptions['transformPageChunk'] | undefined + const resolve = vi.fn().mockImplementation((_event, opts) => { + captured_transform = opts?.transformPageChunk + return Promise.resolve(new Response(null, { status: 200 })) + }) + await handle({ event: {} as RequestEvent, resolve }) + const result = await captured_transform?.({ html: 'v__APP_VERSION__', done: true }) + expect(result).toBe(`v${version}`) + }) +}) diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..7efde5e --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,41 @@ +import type { Handle } from '@sveltejs/kit' +import { version } from '../package.json' + +const APP_VERSION_PLACEHOLDER = '__APP_VERSION__' +const CSP_POLICY = [ + "default-src 'self'", + "script-src 'self' 'unsafe-inline' blob:", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: blob:", + "media-src 'self' blob:", + "worker-src 'self' blob:", + "connect-src 'self'", + "font-src 'self'", + "object-src 'none'", + "base-uri 'self'", + "form-action 'self'", + "frame-ancestors 'self'", +].join('; ') +const PERMISSIONS_POLICY = 'camera=(), microphone=(), geolocation=(), payment=()' + +export function inject_version(html: string): string { + return html.replaceAll(APP_VERSION_PLACEHOLDER, version) +} + +function inject_security_headers(response: Response): void { + response.headers.set('X-Frame-Options', 'SAMEORIGIN') + response.headers.set('X-Content-Type-Options', 'nosniff') + response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin') + response.headers.set('Permissions-Policy', PERMISSIONS_POLICY) + response.headers.set('Content-Security-Policy', CSP_POLICY) +} + +export const handle: Handle = async function handle({ event, resolve }) { + const response = await resolve(event, { + transformPageChunk({ html }) { + return inject_version(html) + }, + }) + inject_security_headers(response) + return response +} diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/assets/sound/dragon-studio-distorted-electronic-click-472367.opus b/src/lib/assets/sound/dragon-studio-distorted-electronic-click-472367.opus new file mode 100644 index 0000000000000000000000000000000000000000..abbf3bc7d90a8f146f332e19712c0028f2069592 GIT binary patch literal 8772 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 literal 0 HcmV?d00001 diff --git a/src/lib/components/Logo.svelte b/src/lib/components/Logo.svelte new file mode 100644 index 0000000..872767a --- /dev/null +++ b/src/lib/components/Logo.svelte @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/components/Logo.svelte.spec.ts b/src/lib/components/Logo.svelte.spec.ts new file mode 100644 index 0000000..d420fca --- /dev/null +++ b/src/lib/components/Logo.svelte.spec.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest' +import { render } from 'vitest-browser-svelte' +import Logo from './Logo.svelte' + +const CUSTOM_SIZE = 128 +const CUSTOM_ARIA_LABEL = 'Test Label' +const EXPECTED_VIEW_BOX = '20 18 45 59' +const DEFAULT_SIZE_STRING = '96' +const DEFAULT_ARIA_LABEL = 'Geometric JF Fusion Logo' + +describe('Logo', () => { + it('renders an svg with the expected viewBox', () => { + const { container } = render(Logo) + const svg = container.querySelector('svg') + expect(svg).toBeTruthy() + expect(svg?.getAttribute('viewBox')).toBe(EXPECTED_VIEW_BOX) + }) + + it('uses the default size when no prop is given', () => { + const { container } = render(Logo) + const svg = container.querySelector('svg') + expect(svg?.getAttribute('width')).toBe(DEFAULT_SIZE_STRING) + }) + + it('reflects the size prop on the width attribute', () => { + const { container } = render(Logo, { size: CUSTOM_SIZE }) + const svg = container.querySelector('svg') + expect(svg?.getAttribute('width')).toBe(String(CUSTOM_SIZE)) + }) + + it('uses the default aria-label when no prop is given', () => { + const { container } = render(Logo) + const svg = container.querySelector('svg') + expect(svg?.getAttribute('aria-label')).toBe(DEFAULT_ARIA_LABEL) + }) + + it('reflects the aria_label prop on the aria-label attribute', () => { + const { container } = render(Logo, { aria_label: CUSTOM_ARIA_LABEL }) + const svg = container.querySelector('svg') + expect(svg?.getAttribute('aria-label')).toBe(CUSTOM_ARIA_LABEL) + }) + + it('generates a unique filter id per instance', () => { + const first = render(Logo) + const second = render(Logo) + const first_id = first.container.querySelector('filter')?.getAttribute('id') + const second_id = second.container.querySelector('filter')?.getAttribute('id') + expect(first_id).toBeTruthy() + expect(second_id).toBeTruthy() + expect(first_id).not.toBe(second_id) + }) +}) diff --git a/src/lib/game/credits-config.ts b/src/lib/game/credits-config.ts index f2c496c..f6f712e 100644 --- a/src/lib/game/credits-config.ts +++ b/src/lib/game/credits-config.ts @@ -10,7 +10,7 @@ export const FLOOR_TEXT_ROTATION_X = -Math.PI / 2 const HALF_DIVISOR = 2 -function make_credits_scroll_bounds( +export function make_credits_scroll_bounds( line_count: number, half_depth: number, ): { start_z: number; end_z: number } { @@ -19,7 +19,7 @@ function make_credits_scroll_bounds( return { start_z: offset, end_z: -offset } } -function advance_scroll( +export function advance_scroll( current_z: number, delta: number, start_z: number, diff --git a/src/lib/messages/en.spec.ts b/src/lib/messages/en.spec.ts new file mode 100644 index 0000000..a9c8250 --- /dev/null +++ b/src/lib/messages/en.spec.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from 'vitest' +import { base_messages, messages, simon_messages } from './en' + +const SIMON_SPECIFIC_KEYS = [ + 'game_title', + 'simon_start', + 'simon_round', + 'simon_gameover', + 'game_application_label', +] as const + +describe('messages', () => { + it('contains all original keys for backward compatibility', () => { + const original_keys = [ + 'game_title', + 'press_start', + 'cyber_switch_label', + 'fullscreen_switch_label', + 'fps_switch_label', + 'click_to_start', + 'tap_to_start', + 'simon_start', + 'simon_round', + 'simon_gameover', + 'sprint_button', + 'jump_button', + 'loading_downloading', + 'loading_initializing', + 'loading_loading_assets', + 'loading_ready', + 'score_high_score', + 'score_current', + 'score_round', + 'game_application_label', + 'game_started_announcement', + 'pause_button', + 'controls_move', + 'controls_look', + 'controls_action', + 'controls_jump', + 'controls_return', + ] + for (const key of original_keys) { + expect(messages).toHaveProperty(key) + } + }) +}) + +describe('base_messages', () => { + it('contains no simon-specific keys', () => { + for (const key of SIMON_SPECIFIC_KEYS) { + expect(base_messages).not.toHaveProperty(key) + } + }) +}) + +describe('simon_messages', () => { + it('contains all simon-specific keys', () => { + for (const key of SIMON_SPECIFIC_KEYS) { + expect(simon_messages).toHaveProperty(key) + } + }) +}) diff --git a/src/lib/messages/en.ts b/src/lib/messages/en.ts index 6bc9217..8cdf355 100644 --- a/src/lib/messages/en.ts +++ b/src/lib/messages/en.ts @@ -22,3 +22,13 @@ export const base_messages = { controls_jump: 'Jump', controls_return: 'Return to start', } as const + +export const simon_messages = { + game_title: 'SIMON', + simon_start: 'START', + simon_round: 'ROUND', + simon_gameover: 'GAME OVER', + game_application_label: 'Simon game', +} as const + +export const messages = { ...base_messages, ...simon_messages } as const diff --git a/src/lib/simon/SimonBoard.svelte b/src/lib/simon/SimonBoard.svelte new file mode 100644 index 0000000..a8b476a --- /dev/null +++ b/src/lib/simon/SimonBoard.svelte @@ -0,0 +1,152 @@ + + + + + + + + + {#each BUTTON_CONFIGS as btn (btn.color)} + + + simon_board_input.on_button_pointer_down(e, btn.color)} + onpointerup={() => simon_board_input.on_button_release()} + onpointerleave={() => simon_board_input.on_button_release()} + > + + {@const lit = btn_lit(btn)} + {@const dim = btn_dim(btn)} + {@const active = is_lit(btn.color)} + + + + {/each} + + simon_board_input.on_center_click()}> + + + + + + + + diff --git a/src/lib/simon/SimonBoard.svelte.spec.ts b/src/lib/simon/SimonBoard.svelte.spec.ts new file mode 100644 index 0000000..daa90d3 --- /dev/null +++ b/src/lib/simon/SimonBoard.svelte.spec.ts @@ -0,0 +1,66 @@ +import { describe, expect, it, vi } from 'vitest' +import { render } from 'vitest-browser-svelte' +import SimonBoard from './SimonBoard.svelte' +import type { SimonBoardData } from './types' + +vi.mock('@threlte/core', () => ({ T: {}, useTask: vi.fn() })) +vi.mock('@threlte/extras', () => ({ Text: function Text() {} })) + +function make_simon_data(overrides: Partial = {}): SimonBoardData { + return { + active_color: null, + pressed_color: null, + phase: 'idle', + round: 0, + flash_colors: [], + flash_intensity: 1, + ...overrides, + } +} + +const BOARD_TEXT_PROPS = { + is_alt: false, + text_gameover: 'GAME OVER', + text_round: 'ROUND', + text_start: 'START', +} + +describe('SimonBoard', () => { + it('renders without error in idle state', () => { + const { container } = render(SimonBoard, { + props: { simon_data: make_simon_data(), ...BOARD_TEXT_PROPS }, + }) + expect(container).toBeTruthy() + }) + + it('renders without error when a color is active', () => { + const { container } = render(SimonBoard, { + props: { simon_data: make_simon_data({ active_color: 'green' }), ...BOARD_TEXT_PROPS }, + }) + expect(container).toBeTruthy() + }) + + it('renders without error in gameover phase', () => { + const { container } = render(SimonBoard, { + props: { simon_data: make_simon_data({ phase: 'gameover' }), ...BOARD_TEXT_PROPS }, + }) + expect(container).toBeTruthy() + }) + + it('renders without error when round is in progress', () => { + const { container } = render(SimonBoard, { + props: { simon_data: make_simon_data({ phase: 'showing', round: 3 }), ...BOARD_TEXT_PROPS }, + }) + expect(container).toBeTruthy() + }) + + it('renders without error with flash colors', () => { + const { container } = render(SimonBoard, { + props: { + simon_data: make_simon_data({ flash_colors: ['red', 'blue'] }), + ...BOARD_TEXT_PROPS, + }, + }) + expect(container).toBeTruthy() + }) +}) diff --git a/src/lib/simon/SimonScene.svelte b/src/lib/simon/SimonScene.svelte new file mode 100644 index 0000000..5d68fbc --- /dev/null +++ b/src/lib/simon/SimonScene.svelte @@ -0,0 +1,64 @@ + + + + {#snippet game_board()} + + {/snippet} + diff --git a/src/lib/simon/SimonScene.svelte.spec.ts b/src/lib/simon/SimonScene.svelte.spec.ts new file mode 100644 index 0000000..e78ad4b --- /dev/null +++ b/src/lib/simon/SimonScene.svelte.spec.ts @@ -0,0 +1,81 @@ +import { make_credits_scroll_bounds } from '$lib/game/credits-config' +import { messages } from '$lib/messages/en' +import { score } from '$lib/simon/score.svelte' +import { describe, expect, it, vi } from 'vitest' +import { render } from 'vitest-browser-svelte' +import SimonScene from './SimonScene.svelte' + +vi.mock('$lib/game/SceneObjects.svelte', () => ({ default: function SceneObjects() {} })) +vi.mock('$lib/simon/SimonBoard.svelte', () => ({ default: function SimonBoard() {} })) +vi.mock('$lib/simon/board-config', () => ({ + SCORE_DISPLAY_Z: -4.65, +})) +vi.mock('$lib/game/state.svelte', () => ({ game_state: { is_alt: false } })) +vi.mock('$lib/messages/en', () => ({ + messages: { + game_title: 'SIMON', + cyber_switch_label: 'CYBER', + fullscreen_switch_label: 'FULLSCREEN', + fps_switch_label: 'FPS', + score_high_score: 'HI', + score_round: 'RND', + score_current: 'SCORE', + simon_gameover: 'GAME OVER', + simon_round: 'ROUND', + simon_start: 'START', + }, +})) +vi.mock('$lib/simon/simon.svelte', () => ({ + simon: { + active_color: null, + pressed_color: null, + phase: 'idle', + round: 0, + flash_colors: [], + flash_intensity: 1, + }, +})) +vi.mock('$lib/simon/score.svelte', () => ({ + score: { + high_score: 42, + current_score: 7, + is_new_high_score: true, + high_score_round: 5, + last_cleared_round: 3, + format_score: String, + }, +})) +vi.mock('$lib/simon/credits', () => ({ + CREDITS_TEXT: 'Credits', + CREDITS_LINE_COUNT: 1, +})) +vi.mock('$lib/game/credits-config', () => ({ + make_credits_scroll_bounds: vi.fn(() => ({ start_z: 10, end_z: -10 })), +})) +vi.mock('$lib/game/room-config', () => ({ ROOM_W: 10, ROOM_D: 10, ROOM_H: 5, HALF_D: 5 })) + +describe('SimonScene', () => { + it('renders without error', () => { + const { container } = render(SimonScene) + expect(container).toBeTruthy() + }) + + it('calls make_credits_scroll_bounds with CREDITS_LINE_COUNT and HALF_D', () => { + render(SimonScene) + expect(vi.mocked(make_credits_scroll_bounds)).toHaveBeenCalledWith(1, 5) + }) + + it('score module exposes all required score_data fields', () => { + expect(score.high_score).toBe(42) + expect(score.current_score).toBe(7) + expect(score.is_new_high_score).toBe(true) + expect(score.high_score_round).toBe(5) + expect(score.last_cleared_round).toBe(3) + }) + + it('messages use score_high_score key (no score_label prefix)', () => { + expect(messages.score_high_score).toBe('HI') + expect(messages.score_round).toBe('RND') + expect(messages.score_current).toBe('SCORE') + }) +}) diff --git a/src/lib/simon/audio.svelte.spec.ts b/src/lib/simon/audio.svelte.spec.ts new file mode 100644 index 0000000..3010106 --- /dev/null +++ b/src/lib/simon/audio.svelte.spec.ts @@ -0,0 +1,134 @@ +import { audio as game_audio } from '$lib/game/audio' +import { simon_audio } from '$lib/simon/audio' +import type { ButtonColor } from '$lib/simon/types' +import { afterEach, describe, expect, it, vi } from 'vitest' + +const ALL_COLORS: ButtonColor[] = ['green', 'red', 'yellow', 'blue'] + +function make_mock_ctx() { + const gain_node = { + gain: { + setValueAtTime: vi.fn(), + exponentialRampToValueAtTime: vi.fn(), + }, + connect: vi.fn(), + } + const osc_node = { + connect: vi.fn(), + frequency: { setValueAtTime: vi.fn() }, + type: '' as OscillatorType, + start: vi.fn(), + stop: vi.fn(), + } + const ctx = { + createOscillator: vi.fn().mockReturnValue(osc_node), + createGain: vi.fn().mockReturnValue(gain_node), + destination: {}, + currentTime: 0, + } + return { ctx, gain_node, osc_node } +} + +describe('simon audio', () => { + it.each(ALL_COLORS)('play_tone does not throw for %s', (color) => { + expect(() => simon_audio.play_tone(color, 100, false)).not.toThrow() + }) + + it('play_error_tone does not throw', () => { + expect(() => simon_audio.play_error_tone(100, false)).not.toThrow() + }) + + it.each(ALL_COLORS)('start_tone does not throw for %s', (color) => { + expect(() => simon_audio.start_tone(color, false)).not.toThrow() + }) + + it('stop_tone does not throw when no tone is playing', () => { + expect(() => simon_audio.stop_tone()).not.toThrow() + }) +}) + +describe('simon audio cyber mode', () => { + it.each(ALL_COLORS)('play_tone does not throw for %s in cyber mode', (color) => { + expect(() => simon_audio.play_tone(color, 100, true)).not.toThrow() + }) +}) + +describe('simon audio envelope', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + + it('normal mode uses flat envelope — no exponential ramp', () => { + const { ctx, gain_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.play_tone('green', 200, false) + + expect(gain_node.gain.setValueAtTime).toHaveBeenCalled() + expect(gain_node.gain.exponentialRampToValueAtTime).not.toHaveBeenCalled() + }) + + it('cyber mode applies exponential gain ramp', () => { + const { ctx, gain_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.play_tone('green', 200, true) + + expect(gain_node.gain.exponentialRampToValueAtTime).toHaveBeenCalled() + }) + + it('play_error_tone uses ERROR_FREQ', () => { + const { ctx, osc_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.play_error_tone(3000, false) + + expect(osc_node.frequency.setValueAtTime).toHaveBeenCalledWith(simon_audio.ERROR_FREQ, 0) + }) + + it('start_tone starts oscillator without calling stop', () => { + const { ctx, osc_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.start_tone('green', false) + + expect(osc_node.start).toHaveBeenCalled() + expect(osc_node.stop).not.toHaveBeenCalled() + }) + + it('stop_tone calls stop on the active oscillator', () => { + const { ctx, osc_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.start_tone('red', false) + simon_audio.stop_tone() + + expect(osc_node.stop).toHaveBeenCalledTimes(1) + }) + + it('play_error_tone normal mode uses flat envelope', () => { + const { ctx, gain_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.play_error_tone(3000, false) + + expect(gain_node.gain.setValueAtTime).toHaveBeenCalled() + expect(gain_node.gain.exponentialRampToValueAtTime).not.toHaveBeenCalled() + }) + + it('play_error_tone cyber mode applies exponential gain ramp', () => { + const { ctx, gain_node } = make_mock_ctx() + vi.spyOn(game_audio, 'init_audio').mockImplementation(() => {}) + vi.spyOn(game_audio, 'get_audio_context').mockReturnValue(ctx as unknown as AudioContext) + + simon_audio.play_error_tone(3000, true) + + expect(gain_node.gain.exponentialRampToValueAtTime).toHaveBeenCalled() + }) +}) diff --git a/src/lib/simon/audio.ts b/src/lib/simon/audio.ts new file mode 100644 index 0000000..b613971 --- /dev/null +++ b/src/lib/simon/audio.ts @@ -0,0 +1,77 @@ +import { audio as game_audio } from '$lib/game/audio' +import type { ButtonColor } from './types' + +const FREQ: Record = { green: 415, red: 310, yellow: 252, blue: 209 } +const CYBER_FREQ: Record = { green: 880, red: 698, yellow: 587, blue: 523 } +const ERROR_FREQ = 180 +const MS_PER_SECOND = 1000 +const GAIN_VALUE = 0.5 +const GAIN_FLOOR = 0.001 +const NORMAL_WAVE: OscillatorType = 'sine' +const CYBER_WAVE: OscillatorType = 'square' + +let active_osc: OscillatorNode | null = null + +type OscGraph = { osc: OscillatorNode; gain: GainNode; ctx: AudioContext } + +function create_osc_graph(freq: number, is_alt: boolean): OscGraph | null { + game_audio.init_audio() + const ctx = game_audio.get_audio_context() + if (!ctx) return null + const osc = ctx.createOscillator() + const gain = ctx.createGain() + osc.connect(gain) + gain.connect(ctx.destination) + osc.frequency.setValueAtTime(freq, ctx.currentTime) + osc.type = is_alt ? CYBER_WAVE : NORMAL_WAVE + gain.gain.setValueAtTime(GAIN_VALUE, ctx.currentTime) + return { osc, gain, ctx } +} + +function stop_tone(): void { + if (!active_osc) return + try { + active_osc.stop() + } catch { + // already stopped + } + active_osc = null +} + +function start_tone_raw(freq: number, is_alt: boolean): void { + stop_tone() + const nodes = create_osc_graph(freq, is_alt) + if (!nodes) return + nodes.osc.start(nodes.ctx.currentTime) + active_osc = nodes.osc +} + +function play_raw_tone(freq: number, duration_ms: number, is_alt: boolean): void { + const nodes = create_osc_graph(freq, is_alt) + if (!nodes) return + const { osc, gain, ctx } = nodes + const duration_s = duration_ms / MS_PER_SECOND + if (is_alt) gain.gain.exponentialRampToValueAtTime(GAIN_FLOOR, ctx.currentTime + duration_s) + osc.start(ctx.currentTime) + osc.stop(ctx.currentTime + duration_s) +} + +function start_tone(color: ButtonColor, is_alt: boolean): void { + start_tone_raw(is_alt ? CYBER_FREQ[color] : FREQ[color], is_alt) +} + +function play_tone(color: ButtonColor, duration_ms: number, is_alt: boolean): void { + play_raw_tone(is_alt ? CYBER_FREQ[color] : FREQ[color], duration_ms, is_alt) +} + +function play_error_tone(duration_ms: number, is_alt: boolean): void { + play_raw_tone(ERROR_FREQ, duration_ms, is_alt) +} + +export const simon_audio = { + play_tone, + play_error_tone, + start_tone, + stop_tone, + ERROR_FREQ, +} diff --git a/src/lib/simon/board-config.spec.ts b/src/lib/simon/board-config.spec.ts new file mode 100644 index 0000000..6cd0b51 --- /dev/null +++ b/src/lib/simon/board-config.spec.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest' +import { + BOARD_LABEL_Z, + BOARD_Y, + BOARD_Z, + SCORE_DISPLAY_Z, + SCORE_DISPLAY_Z_OFFSET, +} from './board-config.js' + +describe('BOARD_LABEL_Z', () => { + it('floats text in front of the board backing (0.05)', () => { + expect(BOARD_LABEL_Z).toBe(0.05) + }) + + it('is positive (in front of board face)', () => { + expect(BOARD_LABEL_Z).toBeGreaterThan(0) + }) +}) + +describe('BOARD_Y', () => { + it('is defined', () => { + expect(BOARD_Y).toBeDefined() + }) +}) + +describe('BOARD_Z', () => { + it('is negative (behind origin)', () => { + expect(BOARD_Z).toBeLessThan(0) + }) +}) + +describe('SCORE_DISPLAY_Z_OFFSET', () => { + it('is positive (score display floats in front of board)', () => { + expect(SCORE_DISPLAY_Z_OFFSET).toBeGreaterThan(0) + }) +}) + +describe('SCORE_DISPLAY_Z', () => { + it('equals BOARD_Z + SCORE_DISPLAY_Z_OFFSET', () => { + expect(SCORE_DISPLAY_Z).toBeCloseTo(BOARD_Z + SCORE_DISPLAY_Z_OFFSET) + }) + + it('is in front of BOARD_Z (closer to camera)', () => { + expect(SCORE_DISPLAY_Z).toBeGreaterThan(BOARD_Z) + }) +}) diff --git a/src/lib/simon/board-config.ts b/src/lib/simon/board-config.ts new file mode 100644 index 0000000..168a9eb --- /dev/null +++ b/src/lib/simon/board-config.ts @@ -0,0 +1,5 @@ +export const BOARD_Y = 1.2 +export const BOARD_Z = -4.8 +export const BOARD_LABEL_Z = 0.05 +export const SCORE_DISPLAY_Z_OFFSET = 0.15 +export const SCORE_DISPLAY_Z = BOARD_Z + SCORE_DISPLAY_Z_OFFSET diff --git a/src/lib/simon/credits.spec.ts b/src/lib/simon/credits.spec.ts new file mode 100644 index 0000000..5082288 --- /dev/null +++ b/src/lib/simon/credits.spec.ts @@ -0,0 +1,139 @@ +import { describe, expect, it } from 'vitest' +import { CREDITS_LINE_COUNT, CREDITS_TEXT } from './credits' + +describe('credits', () => { + describe('CREDITS_TEXT', () => { + it('is a non-empty string', () => { + expect(typeof CREDITS_TEXT).toBe('string') + expect(CREDITS_TEXT.length).toBeGreaterThan(0) + }) + + it('includes core framework credits', () => { + expect(CREDITS_TEXT).toContain('Svelte') + expect(CREDITS_TEXT).toContain('Three.js') + expect(CREDITS_TEXT).toContain('Threlte') + expect(CREDITS_TEXT).toContain('TypeScript') + }) + + it('includes @joshuafolkken/kit credit', () => { + expect(CREDITS_TEXT).toContain('@joshuafolkken/kit') + }) + + it('includes deployment platform credit', () => { + expect(CREDITS_TEXT).toContain('Cloudflare') + }) + + it('includes font credits', () => { + expect(CREDITS_TEXT).toContain('Press Start 2P') + expect(CREDITS_TEXT).toContain('Orbitron') + }) + + it('includes font author credits', () => { + expect(CREDITS_TEXT).toContain('Boisclair') + expect(CREDITS_TEXT).toContain('McInerney') + }) + + it('includes font license', () => { + expect(CREDITS_TEXT).toContain('SIL Open Font License') + }) + + it('includes game concept attribution', () => { + expect(CREDITS_TEXT).toContain('Ralph H. Baer') + expect(CREDITS_TEXT).toContain('Howard J. Morrison') + expect(CREDITS_TEXT).toContain('Milton Bradley') + }) + + it('includes open source library credits', () => { + expect(CREDITS_TEXT).toContain('mrdoob/three.js') + expect(CREDITS_TEXT).toContain('microsoft/playwright') + }) + + it('includes testing and tooling section', () => { + expect(CREDITS_TEXT).toContain('Vitest') + expect(CREDITS_TEXT).toContain('Playwright') + expect(CREDITS_TEXT).toContain('ESLint') + }) + + it('credits Claude Sonnet 4.6 as engineering staff', () => { + expect(CREDITS_TEXT).toContain('Claude Sonnet 4.6') + }) + + it('ends with thank you message', () => { + expect(CREDITS_TEXT).toContain('THANK YOU FOR PLAYING') + }) + + it('includes DRAGON-STUDIO sound effect credit', () => { + expect(CREDITS_TEXT).toContain('Sound Effect by') + expect(CREDITS_TEXT).toContain('DRAGON-STUDIO') + expect(CREDITS_TEXT).toContain('from Pixabay') + }) + + it('includes sponsors section', () => { + expect(CREDITS_TEXT).toContain('SPONSORS') + expect(CREDITS_TEXT).toContain('Incognito') + }) + + it('section headers remain in ALL CAPS', () => { + expect(CREDITS_TEXT).toContain('SPONSORS') + expect(CREDITS_TEXT).toContain('GAME CONCEPT') + expect(CREDITS_TEXT).toContain('BUILT WITH') + expect(CREDITS_TEXT).toContain('FONTS') + expect(CREDITS_TEXT).toContain('TESTING & TOOLING') + expect(CREDITS_TEXT).toContain('OPEN SOURCE LIBRARIES') + }) + + it('includes the signature line', () => { + expect(CREDITS_TEXT).toContain('A JOSHUA FOLKKEN GAME') + }) + + it('includes the STAFF section header', () => { + expect(CREDITS_TEXT).toContain('STAFF') + }) + + it('includes Joshua Folkken creative roles', () => { + expect(CREDITS_TEXT).toContain('CREATIVE DIRECTOR') + expect(CREDITS_TEXT).toContain('GAME DIRECTOR') + expect(CREDITS_TEXT).toContain('GAME DESIGNER') + expect(CREDITS_TEXT).toContain('PRODUCER') + expect(CREDITS_TEXT).toContain('WORLD ARCHITECT') + expect(CREDITS_TEXT).toContain('ART DIRECTOR') + expect(CREDITS_TEXT).toContain('SOUND DIRECTOR') + expect(CREDITS_TEXT).toContain('EXPERIENCE DESIGNER') + }) + + it('includes Claude Sonnet engineering roles', () => { + expect(CREDITS_TEXT).toContain('TECHNOLOGY DIRECTOR') + expect(CREDITS_TEXT).toContain('LEAD PROGRAMMER') + expect(CREDITS_TEXT).toContain('SYSTEMS ARCHITECT') + expect(CREDITS_TEXT).toContain('ENGINE PROGRAMMER') + expect(CREDITS_TEXT).toContain('UI PROGRAMMER') + expect(CREDITS_TEXT).toContain('AUDIO PROGRAMMER') + expect(CREDITS_TEXT).toContain('TEST ENGINEER') + expect(CREDITS_TEXT).toContain('QA ENGINEER') + expect(CREDITS_TEXT).toContain('REFACTORING LEAD') + expect(CREDITS_TEXT).toContain('CODE REVIEWER') + expect(CREDITS_TEXT).toContain('BUILD ENGINEER') + }) + + it('includes Joshua Folkken as a credited person', () => { + expect(CREDITS_TEXT).toContain('Joshua Folkken') + }) + + it('includes the directed-by and engineered-by end card', () => { + expect(CREDITS_TEXT).toContain('DIRECTED BY') + expect(CREDITS_TEXT).toContain('ENGINEERED BY') + }) + }) + + describe('CREDITS_LINE_COUNT', () => { + it('is a positive integer', () => { + expect(Number.isInteger(CREDITS_LINE_COUNT)).toBe(true) + expect(CREDITS_LINE_COUNT).toBeGreaterThan(0) + }) + + it('matches the number of newlines in CREDITS_TEXT plus one', () => { + const line_count_from_text = CREDITS_TEXT.split('\n').length + expect(CREDITS_LINE_COUNT).toBe(line_count_from_text) + }) + }) +}) diff --git a/src/lib/simon/credits.ts b/src/lib/simon/credits.ts new file mode 100644 index 0000000..e993bc3 --- /dev/null +++ b/src/lib/simon/credits.ts @@ -0,0 +1,245 @@ +export const CREDITS_LINES = [ + 'SIMON', + 'CREDITS', + '', + '', + '', + '', + 'A JOSHUA FOLKKEN GAME', + '', + '', + '', + '', + 'SPONSORS', + '', + '', + 'Dejisapo', + '', + 'Incognito', + '', + '', + '', + '', + 'SPECIAL THANKS', + '', + '', + 'Unicorn', + 'aopuyodaihuku', + 'sakuramoti', + '@DocSeventeenth', + '@product_shokai', + 'Longinus', + 'fish', + 'Ejirou', + 'Satou', + '@pirategamesdev', + '@minotaursmx', + '', + '', + '', + '', + 'GAME CONCEPT', + '', + '', + 'Simon is inspired by the', + 'Classic Electronic Memory Game', + 'originally designed by', + 'Ralph H. Baer', + 'and Howard J. Morrison', + 'published by Milton Bradley', + 'in 1978', + '', + '', + '', + '', + 'BUILT WITH', + '', + '', + 'LANGUAGE', + 'TypeScript', + '', + '', + 'FRAMEWORK', + 'Svelte 5 (Runes)', + 'SvelteKit', + '', + '', + '3D RENDERING', + 'Three.js', + 'Threlte', + '', + '', + 'STYLING', + 'Tailwind CSS v4', + '', + '', + 'BUILD TOOL', + 'Vite', + '', + '', + 'AUDIO', + 'Web Audio API', + 'Sound Effect by DRAGON-STUDIO', + 'from Pixabay', + '', + '', + 'DEPLOYMENT', + 'Cloudflare Workers via Wrangler', + '', + '', + '', + '', + 'FONTS', + '', + '', + 'Press Start 2P', + 'Cody "CodeMan38" Boisclair', + 'SIL Open Font License', + 'via Google Fonts', + '', + 'Orbitron', + 'Matt McInerney', + 'SIL Open Font License', + 'via Google Fonts', + '', + '', + '', + '', + 'TESTING & TOOLING', + '', + '', + 'Vitest', + 'Playwright', + 'ESLint', + 'Prettier', + 'pnpm', + 'Lefthook', + '@joshuafolkken/kit', + '', + '', + '', + '', + 'OPEN SOURCE LIBRARIES', + '', + '', + 'svelte/svelte MIT', + 'sveltejs/kit MIT', + 'threlte/threlte MIT', + 'mrdoob/three.js MIT', + 'vitejs/vite MIT', + 'vitest-dev/vitest MIT', + 'microsoft/playwright Apache 2.0', + 'tailwindlabs/tailwindcss MIT', + '', + '', + '', + '', + 'STAFF', + '', + '', + 'CREATIVE DIRECTOR', + 'Joshua Folkken', + '', + '', + 'GAME DIRECTOR', + 'Joshua Folkken', + '', + '', + 'GAME DESIGNER', + 'Joshua Folkken', + '', + '', + 'PRODUCER', + 'Joshua Folkken', + '', + '', + 'WORLD ARCHITECT', + 'Joshua Folkken', + '', + '', + 'ART DIRECTOR', + 'Joshua Folkken', + '', + '', + 'SOUND DIRECTOR', + 'Joshua Folkken', + '', + '', + 'EXPERIENCE DESIGNER', + 'Joshua Folkken', + '', + '', + '- - -', + '', + '', + 'TECHNOLOGY DIRECTOR', + 'Claude Sonnet 4.6', + '', + '', + 'LEAD PROGRAMMER', + 'Claude Sonnet 4.6', + '', + '', + 'SYSTEMS ARCHITECT', + 'Claude Sonnet 4.6', + '', + '', + 'ENGINE PROGRAMMER', + 'Claude Sonnet 4.6', + '', + '', + 'UI PROGRAMMER', + 'Claude Sonnet 4.6', + '', + '', + 'AUDIO PROGRAMMER', + 'Claude Sonnet 4.6', + '', + '', + 'TEST ENGINEER', + 'Claude Sonnet 4.6', + '', + '', + 'QA ENGINEER', + 'Claude Sonnet 4.6', + '', + '', + 'REFACTORING LEAD', + 'Claude Sonnet 4.6', + '', + '', + 'CODE REVIEWER', + 'Claude Sonnet 4.6', + '', + '', + 'BUILD ENGINEER', + 'Claude Sonnet 4.6', + '', + '', + '', + '', + 'ENGINEERED BY', + '', + '', + 'Claude Sonnet 4.6', + '', + '', + '', + '', + '', + '', + 'DIRECTED BY', + '', + '', + 'Joshua Folkken', + '', + '', + '', + '', + '', + '', + 'THANK YOU FOR PLAYING !!', +] as const + +export const CREDITS_TEXT = CREDITS_LINES.join('\n') +export const CREDITS_LINE_COUNT = CREDITS_LINES.length diff --git a/src/lib/simon/score.svelte.spec.ts b/src/lib/simon/score.svelte.spec.ts new file mode 100644 index 0000000..b9c1be3 --- /dev/null +++ b/src/lib/simon/score.svelte.spec.ts @@ -0,0 +1,292 @@ +import { + compute_check, + create_score, + load_stored_data, + score, + type StorageKeys, +} from '$lib/simon/score.svelte' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const ROUND_1 = 1 +const ROUND_5 = 5 +const SEQ_1 = 1 +const SEQ_5 = 5 +const ELAPSED_0 = 0 +const ELAPSED_5S = 5_000 +const ELAPSED_10S = 10_000 +const ELAPSED_100S = 100_000 + +const DEFAULT_KEYS: StorageKeys = { + score: 'simon_high_score', + round: 'simon_high_score_round', + check: 'simon_high_score_check', +} + +describe('score', () => { + beforeEach(() => { + score.reset() + }) + + describe('calculate_time_coefficient', () => { + it('returns 1.0 for zero elapsed time', () => { + expect(score.calculate_time_coefficient(ELAPSED_0, SEQ_1)).toBe(1) + }) + + it('decays by 0.1 per avg second per button', () => { + const avg_s = ELAPSED_5S / 1000 / SEQ_5 + const expected = 1 - avg_s * 0.1 + expect(score.calculate_time_coefficient(ELAPSED_5S, SEQ_5)).toBeCloseTo(expected, 5) + }) + + it('returns MIN_TIME_COEFF (0.1) when avg seconds is very large', () => { + expect(score.calculate_time_coefficient(ELAPSED_100S, SEQ_1)).toBe(0.1) + }) + + it('normalizes by sequence length so longer rounds are not unfairly penalized', () => { + const coeff_short = score.calculate_time_coefficient(ELAPSED_5S, SEQ_1) + const coeff_long = score.calculate_time_coefficient(ELAPSED_5S * SEQ_5, SEQ_5) + expect(coeff_short).toBeCloseTo(coeff_long, 5) + }) + }) + + describe('calculate_round_score', () => { + it('returns BASE_SCORE * round for zero elapsed time', () => { + expect(score.calculate_round_score(ELAPSED_0, SEQ_1, ROUND_1)).toBe(1_000) + expect(score.calculate_round_score(ELAPSED_0, SEQ_5, ROUND_5)).toBe(5_000) + }) + + it('scales with round number', () => { + const r1 = score.calculate_round_score(ELAPSED_0, ROUND_1, ROUND_1) + const r5 = score.calculate_round_score(ELAPSED_0, ROUND_5, ROUND_5) + expect(r5).toBe(r1 * ROUND_5) + }) + + it('produces a lower score for slower responses', () => { + const fast = score.calculate_round_score(ELAPSED_0, SEQ_1, ROUND_1) + const slow = score.calculate_round_score(ELAPSED_10S, SEQ_1, ROUND_1) + expect(slow).toBeLessThan(fast) + }) + + it('floors at 10% of BASE_SCORE * round for very slow responses', () => { + const floor_score = score.calculate_round_score(ELAPSED_100S, SEQ_1, ROUND_5) + expect(floor_score).toBe(500) + }) + }) + + describe('format_score', () => { + it('formats numbers below 1000 without comma', () => { + expect(score.format_score(999)).toBe('999') + }) + + it('formats numbers at 1000 with comma', () => { + expect(score.format_score(1_000)).toBe('1,000') + }) + + it('formats large numbers with multiple commas', () => { + expect(score.format_score(1_234_567)).toBe('1,234,567') + }) + + it('formats zero', () => { + expect(score.format_score(0)).toBe('0') + }) + }) + + describe('add_round_score', () => { + it('accumulates current_score after each round', () => { + score.add_round_score(ELAPSED_0, SEQ_1, ROUND_1) + expect(score.current_score).toBe(1_000) + score.add_round_score(ELAPSED_0, SEQ_1, ROUND_1) + expect(score.current_score).toBe(2_000) + }) + + it('updates high_score when current_score exceeds it', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + expect(score.high_score).toBeGreaterThan(prev_high) + expect(score.high_score).toBe(score.current_score) + }) + + it('does not update high_score when current_score stays below it', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + const established = score.high_score + score.reset() + score.add_round_score(ELAPSED_100S, SEQ_1, ROUND_1) + expect(score.high_score).toBe(established) + }) + + it('sets is_new_high_score to true when current_score exceeds high_score', () => { + expect(score.is_new_high_score).toBe(false) + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + expect(score.is_new_high_score).toBe(true) + }) + }) + + describe('reset', () => { + it('resets current_score to 0', () => { + score.add_round_score(ELAPSED_0, SEQ_1, ROUND_1) + score.reset() + expect(score.current_score).toBe(0) + }) + + it('resets is_new_high_score to false', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + expect(score.is_new_high_score).toBe(true) + score.reset() + expect(score.is_new_high_score).toBe(false) + }) + + it('preserves high_score across reset', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + const new_high = score.high_score + score.reset() + expect(score.high_score).toBe(new_high) + }) + }) + + describe('last_cleared_round', () => { + it('starts at 0', () => { + expect(score.last_cleared_round).toBe(0) + }) + + it('is set to the round passed to add_round_score', () => { + score.add_round_score(ELAPSED_0, SEQ_1, ROUND_5) + expect(score.last_cleared_round).toBe(ROUND_5) + }) + + it('is reset to 0 by reset()', () => { + score.add_round_score(ELAPSED_0, SEQ_1, ROUND_5) + score.reset() + expect(score.last_cleared_round).toBe(0) + }) + }) + + describe('high_score_round', () => { + it('is updated to the round when a new high score is set', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + expect(score.high_score_round).toBe(prev_high + 2) + }) + + it('is preserved across reset', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 2) + const expected_round = prev_high + 2 + score.reset() + expect(score.high_score_round).toBe(expected_round) + }) + + it('is not updated when current score stays below high score', () => { + const prev_high = score.high_score + score.add_round_score(ELAPSED_0, SEQ_1, prev_high + 5) + const saved_round = score.high_score_round + score.reset() + score.add_round_score(ELAPSED_100S, SEQ_1, ROUND_1) + expect(score.high_score_round).toBe(saved_round) + }) + }) +}) + +describe('compute_check', () => { + it('returns a non-zero value for (0, 0)', () => { + expect(compute_check(0, 0)).not.toBe(0) + }) + + it('is deterministic — same inputs produce same output', () => { + expect(compute_check(1_000, 3)).toBe(compute_check(1_000, 3)) + }) + + it('produces different values when score differs', () => { + expect(compute_check(1_000, 3)).not.toBe(compute_check(2_000, 3)) + }) + + it('produces different values when round differs', () => { + expect(compute_check(1_000, 3)).not.toBe(compute_check(1_000, 4)) + }) +}) + +describe('load_stored_data', () => { + afterEach(() => { + vi.unstubAllGlobals() + }) + + it('returns {score: 0, round: 0} when nothing is stored', () => { + vi.stubGlobal('localStorage', { getItem: () => null, setItem: () => {} }) + expect(load_stored_data(DEFAULT_KEYS)).toEqual({ score: 0, round: 0 }) + }) + + it('returns correct values when data is valid', () => { + const stored_score = 5_000 + const stored_round = 3 + const stored_check = compute_check(stored_score, stored_round) + vi.stubGlobal('localStorage', { + getItem: (key: string) => { + if (key === DEFAULT_KEYS.score) return String(stored_score) + if (key === DEFAULT_KEYS.round) return String(stored_round) + if (key === DEFAULT_KEYS.check) return String(stored_check) + return null + }, + setItem: () => {}, + }) + expect(load_stored_data(DEFAULT_KEYS)).toEqual({ score: stored_score, round: stored_round }) + }) + + it('returns {score: 0, round: 0} when check digit does not match (tampered)', () => { + vi.stubGlobal('localStorage', { + getItem: (key: string) => { + if (key === DEFAULT_KEYS.score) return '5000' + if (key === DEFAULT_KEYS.round) return '3' + if (key === DEFAULT_KEYS.check) return '99999' + return null + }, + setItem: () => {}, + }) + expect(load_stored_data(DEFAULT_KEYS)).toEqual({ score: 0, round: 0 }) + }) + + it('returns {score: 0, round: 0} when localStorage throws', () => { + vi.stubGlobal('localStorage', { + getItem: () => { + throw new Error('unavailable') + }, + }) + expect(load_stored_data(DEFAULT_KEYS)).toEqual({ score: 0, round: 0 }) + }) + + it('uses keys from the provided StorageKeys object', () => { + const custom_keys: StorageKeys = { score: 'test_s', round: 'test_r', check: 'test_c' } + const stored_score = 1_000 + const stored_round = 1 + const stored_check = compute_check(stored_score, stored_round) + vi.stubGlobal('localStorage', { + getItem: (key: string) => { + if (key === custom_keys.score) return String(stored_score) + if (key === custom_keys.round) return String(stored_round) + if (key === custom_keys.check) return String(stored_check) + return null + }, + setItem: () => {}, + }) + expect(load_stored_data(custom_keys)).toEqual({ score: stored_score, round: stored_round }) + }) +}) + +describe('create_score isolation', () => { + it('two instances do not share current_score state', () => { + const a = create_score() + const b = create_score() + a.add_round_score(ELAPSED_0, SEQ_1, ROUND_1) + expect(a.current_score).toBeGreaterThan(0) + expect(b.current_score).toBe(0) + }) + + it('custom key prefix stores to different keys than default', () => { + const custom = create_score('test') + vi.stubGlobal('localStorage', { getItem: () => null, setItem: () => {} }) + expect(custom.high_score).toBe(0) + vi.unstubAllGlobals() + }) +}) diff --git a/src/lib/simon/score.svelte.ts b/src/lib/simon/score.svelte.ts new file mode 100644 index 0000000..9ee02f6 --- /dev/null +++ b/src/lib/simon/score.svelte.ts @@ -0,0 +1,131 @@ +const BASE_SCORE = 1_000 +const TIME_COEFF_DECAY = 0.1 +const MIN_TIME_COEFF = 0.1 +const CHECK_SEED = 0x9e3779b9 +const SCORE_FORMATTER = new Intl.NumberFormat('en-US') + +export const SIMON_SCORE_KEY_PREFIX = 'simon' + +export type StorageKeys = { score: string; round: string; check: string } +type RoundData = { elapsed_ms: number; sequence_length: number; round: number } + +export function compute_check(value: number, round: number): number { + return (Math.imul(value + 1, CHECK_SEED) ^ Math.imul(round + 1, CHECK_SEED >>> 1)) >>> 0 +} + +export function load_stored_data(keys: StorageKeys): { score: number; round: number } { + try { + const stored_score = Number(localStorage.getItem(keys.score)) + const stored_round = Number(localStorage.getItem(keys.round)) + const stored_check = Number(localStorage.getItem(keys.check)) + const is_valid_score = Number.isFinite(stored_score) && stored_score > 0 + const is_valid_round = Number.isFinite(stored_round) && stored_round >= 0 + const is_check_ok = compute_check(stored_score, stored_round) === stored_check + if (!is_valid_score || !is_valid_round || !is_check_ok) return { score: 0, round: 0 } + return { score: stored_score, round: stored_round } + } catch { + return { score: 0, round: 0 } + } +} + +function save_high_score(value: number, round: number, keys: StorageKeys): void { + try { + localStorage.setItem(keys.score, String(value)) + localStorage.setItem(keys.round, String(round)) + localStorage.setItem(keys.check, String(compute_check(value, round))) + } catch { + // storage not available in this environment + } +} + +export function calculate_time_coefficient(elapsed_ms: number, sequence_length: number): number { + const avg_s = elapsed_ms / 1000 / sequence_length + return Math.max(MIN_TIME_COEFF, 1 - avg_s * TIME_COEFF_DECAY) +} + +export function calculate_round_score( + elapsed_ms: number, + sequence_length: number, + round: number, +): number { + return Math.round(BASE_SCORE * calculate_time_coefficient(elapsed_ms, sequence_length) * round) +} + +export function format_score(value: number): string { + return SCORE_FORMATTER.format(value) +} + +type ScoreState = { + current_score: number + high_score: number + high_score_round: number + is_new_high_score: boolean + last_cleared_round: number +} + +function update_high_score(s: ScoreState, round: number, keys: StorageKeys): void { + if (s.current_score <= s.high_score) return + s.high_score = s.current_score + s.high_score_round = round + s.is_new_high_score = true + save_high_score(s.high_score, round, keys) +} + +function add_round_score_impl(s: ScoreState, data: RoundData, keys: StorageKeys): void { + s.current_score += calculate_round_score(data.elapsed_ms, data.sequence_length, data.round) + s.last_cleared_round = data.round + update_high_score(s, data.round, keys) +} + +function reset_score_impl(s: ScoreState): void { + s.current_score = 0 + s.is_new_high_score = false + s.last_cleared_round = 0 +} + +function make_score_api(s: ScoreState, keys: StorageKeys) { + return { + get current_score(): number { + return s.current_score + }, + get high_score(): number { + return s.high_score + }, + get high_score_round(): number { + return s.high_score_round + }, + get is_new_high_score(): boolean { + return s.is_new_high_score + }, + get last_cleared_round(): number { + return s.last_cleared_round + }, + add_round_score: (elapsed_ms: number, sequence_length: number, round: number): void => + add_round_score_impl(s, { elapsed_ms, sequence_length, round }, keys), + reset: (): void => reset_score_impl(s), + format_score, + calculate_time_coefficient, + calculate_round_score, + } +} + +export function create_score(key_prefix: string = SIMON_SCORE_KEY_PREFIX) { + const keys: StorageKeys = { + score: `${key_prefix}_high_score`, + round: `${key_prefix}_high_score_round`, + check: `${key_prefix}_high_score_check`, + } + const loaded = load_stored_data(keys) + const s = $state({ + current_score: 0, + high_score: loaded.score, + high_score_round: loaded.round, + is_new_high_score: false, + last_cleared_round: 0, + }) + return make_score_api(s, keys) +} + +export type ScoreInstance = ReturnType + +export const score = create_score() diff --git a/src/lib/simon/simon-board-input.spec.ts b/src/lib/simon/simon-board-input.spec.ts new file mode 100644 index 0000000..5e36c07 --- /dev/null +++ b/src/lib/simon/simon-board-input.spec.ts @@ -0,0 +1,70 @@ +import { session } from '$lib/game/session.svelte' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { simon_board_input } from './simon-board-input' +import type { ButtonColor } from './types' + +const LEFT_BUTTON = 0 +const RIGHT_BUTTON = 2 + +describe('simon_board_input', () => { + let press_mock: ReturnType void>> + let release_mock: ReturnType void>> + let start_mock: ReturnType void>> + + beforeEach(() => { + session.reset_session() + press_mock = vi.fn<(color: ButtonColor) => void>() + release_mock = vi.fn<() => void>() + start_mock = vi.fn<() => void>() + simon_board_input.configure({ + on_press: press_mock, + on_release: release_mock, + on_start: start_mock, + }) + }) + + describe('on_button_pointer_down', () => { + it('does not call on_press when session is not started', () => { + simon_board_input.on_button_pointer_down({ nativeEvent: { button: LEFT_BUTTON } }, 'green') + expect(press_mock).not.toHaveBeenCalled() + }) + + it('calls on_press once session has started', () => { + session.start_session() + simon_board_input.on_button_pointer_down({ nativeEvent: { button: LEFT_BUTTON } }, 'red') + expect(press_mock).toHaveBeenCalledWith('red') + }) + + it('does not call on_press for non-left click even after session started', () => { + session.start_session() + simon_board_input.on_button_pointer_down({ nativeEvent: { button: RIGHT_BUTTON } }, 'blue') + expect(press_mock).not.toHaveBeenCalled() + }) + }) + + describe('on_button_release', () => { + it('does not call on_release when session is not started', () => { + simon_board_input.on_button_release() + expect(release_mock).not.toHaveBeenCalled() + }) + + it('calls on_release once session has started', () => { + session.start_session() + simon_board_input.on_button_release() + expect(release_mock).toHaveBeenCalledTimes(1) + }) + }) + + describe('on_center_click', () => { + it('does not call on_start when session is not started', () => { + simon_board_input.on_center_click() + expect(start_mock).not.toHaveBeenCalled() + }) + + it('calls on_start once session has started', () => { + session.start_session() + simon_board_input.on_center_click() + expect(start_mock).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/src/lib/simon/simon-board-input.ts b/src/lib/simon/simon-board-input.ts new file mode 100644 index 0000000..41fb25f --- /dev/null +++ b/src/lib/simon/simon-board-input.ts @@ -0,0 +1,42 @@ +import { pointer_button } from '$lib/game/pointer-button' +import { session } from '$lib/game/session.svelte' +import type { ButtonColor } from './types' + +type BoardCallbacks = { + on_press: (color: ButtonColor) => void + on_release: () => void + on_start: () => void +} + +let board_callbacks: BoardCallbacks = { + on_press: () => {}, + on_release: () => {}, + on_start: () => {}, +} + +function configure(cbs: BoardCallbacks): void { + board_callbacks = cbs +} + +function on_button_pointer_down(e: { nativeEvent: { button: number } }, color: ButtonColor): void { + if (!session.is_session_started) return + if (!pointer_button.is_left_click(e)) return + board_callbacks.on_press(color) +} + +function on_button_release(): void { + if (!session.is_session_started) return + board_callbacks.on_release() +} + +function on_center_click(): void { + if (!session.is_session_started) return + board_callbacks.on_start() +} + +export const simon_board_input = { + configure, + on_button_pointer_down, + on_button_release, + on_center_click, +} diff --git a/src/lib/simon/simon-flash.spec.ts b/src/lib/simon/simon-flash.spec.ts new file mode 100644 index 0000000..80c7bb8 --- /dev/null +++ b/src/lib/simon/simon-flash.spec.ts @@ -0,0 +1,160 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { simon_audio } from './audio' +import { + cancel_flash, + FLASH_BURST_CYCLES, + FLASH_BURST_OFF_MS, + FLASH_BURST_ON_MS, + FLASH_CASCADE_FWD_MS, + FLASH_CASCADE_REV_MS, + FLASH_FINALE_MS, + run_victory_flash, + type FlashState, + type FlashTimers, +} from './simon-flash' +import type { ButtonColor } from './types' + +const COLORS: ButtonColor[] = ['green', 'red', 'yellow', 'blue'] + +function make_state(): FlashState { + return { flash_colors: [], flash_intensity: 1 } +} + +function make_timers(): FlashTimers { + return { flash_gen: 0 } +} + +describe('flash constants', () => { + it('FLASH_BURST_ON_MS is 30', () => { + expect(FLASH_BURST_ON_MS).toBe(30) + }) + + it('FLASH_BURST_OFF_MS is 20', () => { + expect(FLASH_BURST_OFF_MS).toBe(20) + }) + + it('FLASH_BURST_CYCLES is 4', () => { + expect(FLASH_BURST_CYCLES).toBe(4) + }) + + it('FLASH_CASCADE_FWD_MS is 65', () => { + expect(FLASH_CASCADE_FWD_MS).toBe(65) + }) + + it('FLASH_CASCADE_REV_MS is 40', () => { + expect(FLASH_CASCADE_REV_MS).toBe(40) + }) + + it('FLASH_FINALE_MS is 320', () => { + expect(FLASH_FINALE_MS).toBe(320) + }) +}) + +describe('cancel_flash', () => { + it('increments flash_gen', () => { + const s = make_state() + const t = make_timers() + cancel_flash(s, t) + expect(t.flash_gen).toBe(1) + }) + + it('clears flash_colors', () => { + const s: FlashState = { flash_colors: ['green', 'red'], flash_intensity: 2.5 } + const t = make_timers() + cancel_flash(s, t) + expect(s.flash_colors).toHaveLength(0) + }) + + it('resets flash_intensity to 1', () => { + const s: FlashState = { flash_colors: ['green'], flash_intensity: 2.5 } + const t = make_timers() + cancel_flash(s, t) + expect(s.flash_intensity).toBe(1) + }) + + it('increments flash_gen each call', () => { + const s = make_state() + const t = make_timers() + cancel_flash(s, t) + cancel_flash(s, t) + expect(t.flash_gen).toBe(2) + }) +}) + +describe('run_victory_flash', () => { + beforeEach(() => { + vi.useFakeTimers() + vi.spyOn(simon_audio, 'play_tone').mockImplementation(() => {}) + }) + + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + vi.restoreAllMocks() + }) + + it('sets flash_colors to all colors at start of burst', async () => { + const s = make_state() + const t = make_timers() + void run_victory_flash(s, t, COLORS, 0) + await vi.advanceTimersByTimeAsync(0) + expect(s.flash_colors).toEqual(expect.arrayContaining(COLORS)) + }) + + it('sets flash_intensity above 1 during burst', async () => { + const s = make_state() + const t = make_timers() + void run_victory_flash(s, t, COLORS, 0) + await vi.advanceTimersByTimeAsync(0) + expect(s.flash_intensity).toBeGreaterThan(1) + }) + + it('calls play_tone for each color during burst', async () => { + const spy = vi.spyOn(simon_audio, 'play_tone').mockImplementation(() => {}) + const s = make_state() + const t = make_timers() + void run_victory_flash(s, t, COLORS, 0) + await vi.advanceTimersByTimeAsync(FLASH_BURST_ON_MS) + const called_colors = spy.mock.calls.map((c) => c[0]) + for (const color of COLORS) expect(called_colors).toContain(color) + }) + + it('aborts when flash_gen changes mid-run', async () => { + const s = make_state() + const t = make_timers() + void run_victory_flash(s, t, COLORS, 0) + await vi.advanceTimersByTimeAsync(0) + cancel_flash(s, t) + const calls_before = (vi.mocked(simon_audio.play_tone) as ReturnType).mock.calls + .length + await vi.runAllTimersAsync() + const calls_after = (vi.mocked(simon_audio.play_tone) as ReturnType).mock.calls + .length + expect(calls_after).toBe(calls_before) + }) + + it('clears flash_colors after full run completes', async () => { + const s = make_state() + const t = make_timers() + const flash_total_ms = + FLASH_BURST_CYCLES * (FLASH_BURST_ON_MS + FLASH_BURST_OFF_MS) + + COLORS.length * (FLASH_CASCADE_FWD_MS + FLASH_CASCADE_REV_MS) + + FLASH_FINALE_MS + void run_victory_flash(s, t, COLORS, 0) + await vi.advanceTimersByTimeAsync(flash_total_ms + 10) + expect(s.flash_colors).toHaveLength(0) + expect(s.flash_intensity).toBe(1) + }) + + it('works with a custom color subset', async () => { + const custom: ButtonColor[] = ['green', 'blue'] + const spy = vi.spyOn(simon_audio, 'play_tone').mockImplementation(() => {}) + const s = make_state() + const t = make_timers() + void run_victory_flash(s, t, custom, 0) + await vi.advanceTimersByTimeAsync(FLASH_BURST_ON_MS) + const called_colors = spy.mock.calls.map((c) => c[0]) + expect(called_colors).toContain('green') + expect(called_colors).toContain('blue') + }) +}) diff --git a/src/lib/simon/simon-flash.ts b/src/lib/simon/simon-flash.ts new file mode 100644 index 0000000..f70666a --- /dev/null +++ b/src/lib/simon/simon-flash.ts @@ -0,0 +1,106 @@ +import { game_state } from '$lib/game/state.svelte' +import { simon_audio } from './audio' +import type { ButtonColor } from './types' + +export const FLASH_BURST_ON_MS = 30 +export const FLASH_BURST_OFF_MS = 20 +export const FLASH_BURST_CYCLES = 4 +export const FLASH_CASCADE_FWD_MS = 65 +export const FLASH_CASCADE_REV_MS = 40 +export const FLASH_FINALE_MS = 320 + +const FLASH_INTENSITY_BURST = 2.5 +const FLASH_INTENSITY_FINALE = 4 +const FLASH_INTENSITY_RESET = 1 + +export type FlashState = { + flash_colors: ButtonColor[] + flash_intensity: number +} + +export type FlashTimers = { + flash_gen: number +} + +function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +function play_all_tones(colors: readonly ButtonColor[], duration_ms: number): void { + const is_alt = game_state.is_alt + for (const color of colors) simon_audio.play_tone(color, duration_ms, is_alt) +} + +export function cancel_flash(s: FlashState, t: FlashTimers): void { + t.flash_gen += 1 + s.flash_colors = [] + s.flash_intensity = FLASH_INTENSITY_RESET +} + +async function flash_burst( + s: FlashState, + t: FlashTimers, + colors: readonly ButtonColor[], + gen: number, +): Promise { + for (let i = 0; i < FLASH_BURST_CYCLES; i++) { + if (t.flash_gen !== gen) return + s.flash_colors = [...colors] + s.flash_intensity = FLASH_INTENSITY_BURST + play_all_tones(colors, FLASH_BURST_ON_MS) + await delay(FLASH_BURST_ON_MS) + if (t.flash_gen !== gen) return + s.flash_colors = [] + s.flash_intensity = FLASH_INTENSITY_RESET + await delay(FLASH_BURST_OFF_MS) + } +} + +async function flash_cascade( + s: FlashState, + t: FlashTimers, + colors: readonly ButtonColor[], + gen: number, +): Promise { + for (const color of colors) { + if (t.flash_gen !== gen) return + s.flash_colors = [color] + s.flash_intensity = FLASH_INTENSITY_BURST + simon_audio.play_tone(color, FLASH_CASCADE_FWD_MS, game_state.is_alt) + await delay(FLASH_CASCADE_FWD_MS) + } + for (const color of [...colors].reverse()) { + if (t.flash_gen !== gen) return + s.flash_colors = [color] + s.flash_intensity = FLASH_INTENSITY_BURST + simon_audio.play_tone(color, FLASH_CASCADE_REV_MS, game_state.is_alt) + await delay(FLASH_CASCADE_REV_MS) + } +} + +async function flash_finale( + s: FlashState, + t: FlashTimers, + colors: readonly ButtonColor[], + gen: number, +): Promise { + if (t.flash_gen !== gen) return + s.flash_colors = [...colors] + s.flash_intensity = FLASH_INTENSITY_FINALE + play_all_tones(colors, FLASH_FINALE_MS) + await delay(FLASH_FINALE_MS) + if (t.flash_gen !== gen) return + s.flash_colors = [] + s.flash_intensity = FLASH_INTENSITY_RESET +} + +export async function run_victory_flash( + s: FlashState, + t: FlashTimers, + colors: readonly ButtonColor[], + gen: number, +): Promise { + await flash_burst(s, t, colors, gen) + await flash_cascade(s, t, colors, gen) + await flash_finale(s, t, colors, gen) +} diff --git a/src/lib/simon/simon.svelte.spec.ts b/src/lib/simon/simon.svelte.spec.ts new file mode 100644 index 0000000..d880632 --- /dev/null +++ b/src/lib/simon/simon.svelte.spec.ts @@ -0,0 +1,484 @@ +import { simon_audio } from '$lib/simon/audio' +import { create_score, score } from '$lib/simon/score.svelte' +import { + FLASH_BURST_CYCLES, + FLASH_BURST_OFF_MS, + FLASH_BURST_ON_MS, + FLASH_CASCADE_FWD_MS, + FLASH_CASCADE_REV_MS, + FLASH_FINALE_MS, +} from '$lib/simon/simon-flash' +import { + create_simon, + ERROR_BEEP_MS, + OFF_RATIO, + ON_RATIO, + RESTART_DELAY_MS, + simon, + STEP_MS_1_5, +} from '$lib/simon/simon.svelte' +import type { ButtonColor } from '$lib/simon/types' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const ALL_COLORS: ButtonColor[] = ['green', 'red', 'yellow', 'blue'] +const TONE_MS = 200 +const ON_MS = STEP_MS_1_5 * ON_RATIO +const OFF_MS = STEP_MS_1_5 * OFF_RATIO + +function wrong_color(color: ButtonColor): ButtonColor { + return ALL_COLORS.find((c) => c !== color) ?? 'red' +} + +function seq_at(i: number): ButtonColor { + const color = simon.sequence[i] + if (!color) throw new Error(`sequence index ${String(i)} out of range`) + return color +} + +describe('simon FSM', () => { + beforeEach(() => { + vi.useFakeTimers() + simon.reset() + }) + + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + vi.restoreAllMocks() + simon.reset() + }) + + it('starts in idle phase with empty sequence and round 0', () => { + expect(simon.phase).toBe('idle') + expect(simon.sequence).toHaveLength(0) + expect(simon.round).toBe(0) + expect(simon.active_color).toBeNull() + expect(simon.pressed_color).toBeNull() + }) + + it('start() transitions to showing, sets round 1, adds one sequence item', () => { + simon.start() + expect(simon.phase).toBe('showing') + expect(simon.round).toBe(1) + expect(simon.sequence).toHaveLength(1) + }) + + it('start() sets active_color to first sequence item immediately', () => { + simon.start() + expect(simon.active_color).toBe(seq_at(0)) + }) + + it('showing phase transitions to player_input after timers complete', async () => { + simon.start() + await vi.runAllTimersAsync() + expect(simon.phase).toBe('player_input') + expect(simon.position).toBe(0) + expect(simon.active_color).toBeNull() + }) + + it('active_color clears after on_ms and phase becomes player_input after off_ms', async () => { + simon.start() + expect(simon.active_color).toBe(seq_at(0)) + await vi.advanceTimersByTimeAsync(ON_MS) + expect(simon.active_color).toBeNull() + await vi.advanceTimersByTimeAsync(OFF_MS) + expect(simon.phase).toBe('player_input') + }) + + it('final correct press + release advances to showing for the next round', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.phase).toBe('showing') + expect(simon.round).toBe(1) + }) + + it('round does not advance while last button is still held', async () => { + simon.start() + await vi.runAllTimersAsync() + const final_color = seq_at(0) + simon.press(final_color) + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS * 2) + expect(simon.phase).toBe('player_input') + expect(simon.round).toBe(1) + expect(simon.pressed_color).toBe(final_color) + }) + + it('next round starts after 1 second delay following release of final button', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.phase).toBe('showing') + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS) + expect(simon.round).toBe(2) + expect(simon.sequence).toHaveLength(2) + }) + + it('press is ignored while another button is being held', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + const spy = vi.spyOn(simon_audio, 'start_tone') + simon.press('green') + simon.press('red') + expect(spy).not.toHaveBeenCalled() + expect(simon.phase).toBe('player_input') + }) + + it('reset() while a button is held returns to idle', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.reset() + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS) + expect(simon.phase).toBe('idle') + expect(simon.round).toBe(0) + }) + + it('reset() cancels restart timer so next round does not start', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + simon.reset() + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS) + expect(simon.phase).toBe('idle') + expect(simon.round).toBe(0) + }) + + it('correct intermediate press + release advances position without completing round', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) // complete round 1 + simon.release() + await vi.runAllTimersAsync() // drain round 2 show + const first_color = seq_at(0) + simon.press(first_color) // first of two correct presses + simon.release() + expect(simon.position).toBe(1) + expect(simon.phase).toBe('player_input') + expect(simon.round).toBe(2) + }) + + it('wrong press + release triggers gameover', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(wrong_color(seq_at(0))) + simon.release() + expect(simon.phase).toBe('gameover') + }) + + it('wrong press + release plays error tone for ERROR_BEEP_MS', async () => { + const spy = vi.spyOn(simon_audio, 'play_error_tone') + simon.start() + await vi.runAllTimersAsync() + simon.press(wrong_color(seq_at(0))) + simon.release() + expect(spy).toHaveBeenCalledWith(ERROR_BEEP_MS, false) + }) + + it('press() is ignored when not in player_input phase', () => { + simon.start() // phase = showing + simon.press('green') + expect(simon.phase).toBe('showing') + }) + + it('pressed_color is set on press and does not auto-clear', async () => { + simon.start() + await vi.runAllTimersAsync() + const color = seq_at(0) + simon.press(color) + expect(simon.pressed_color).toBe(color) + await vi.advanceTimersByTimeAsync(TONE_MS + 10) + expect(simon.pressed_color).toBe(color) + }) + + it('release() clears pressed_color', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.pressed_color).toBeNull() + }) + + it('reset() returns all state to initial values', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.reset() + expect(simon.phase).toBe('idle') + expect(simon.sequence).toHaveLength(0) + expect(simon.position).toBe(0) + expect(simon.active_color).toBeNull() + expect(simon.pressed_color).toBeNull() + expect(simon.round).toBe(0) + }) + + it('reset() clears pressed_color immediately', async () => { + simon.start() + await vi.runAllTimersAsync() + const wrong = wrong_color(seq_at(0)) + simon.press(wrong) + expect(simon.pressed_color).toBe(wrong) + simon.reset() + expect(simon.pressed_color).toBeNull() + }) + + it('reset() cancels an in-progress sequence display', async () => { + simon.start() + simon.reset() + await vi.runAllTimersAsync() + expect(simon.phase).toBe('idle') + }) + + it('release while phase is showing does not schedule an extra next-round timer', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) // holding the final button (phase still player_input) + simon.release() // completes round 1 → phase becomes showing + simon.release() // should be ignored while showing + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS) + expect(simon.round).toBe(2) + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS) + expect(simon.round).toBe(2) + }) + + it('start() from gameover restarts the game', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(wrong_color(seq_at(0))) + simon.release() + expect(simon.phase).toBe('gameover') + simon.start() + expect(simon.phase).toBe('showing') + expect(simon.round).toBe(1) + }) + + it('start() is ignored while showing', () => { + simon.start() + simon.start() + expect(simon.round).toBe(1) + }) + + it('start() is ignored during player_input', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.start() + expect(simon.phase).toBe('player_input') + }) + + it('press() starts tone for pressed color', async () => { + const spy = vi.spyOn(simon_audio, 'start_tone') + simon.start() + await vi.runAllTimersAsync() + const color = seq_at(0) + simon.press(color) + expect(spy).toHaveBeenCalledWith(color, false) + }) + + it('press() does not start tone when not in player_input phase', () => { + simon.start() // phase = showing + const spy = vi.spyOn(simon_audio, 'start_tone') + simon.press('green') + expect(spy).not.toHaveBeenCalled() + }) + + it('release() stops the tone', async () => { + const spy = vi.spyOn(simon_audio, 'stop_tone') + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(spy).toHaveBeenCalled() + }) +}) + +describe('score integration', () => { + beforeEach(() => { + vi.useFakeTimers() + simon.reset() + }) + + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + vi.restoreAllMocks() + simon.reset() + }) + + it('current_score is 0 while the final button is held and increases after release', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + expect(score.current_score).toBe(0) + simon.release() + expect(score.current_score).toBeGreaterThan(0) + }) + + it('current_score is 1000 when round 1 is cleared with ~0 elapsed time', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(score.current_score).toBe(1_000) + }) + + it('current_score resets to 0 after simon.reset()', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(score.current_score).toBeGreaterThan(0) + simon.reset() + expect(score.current_score).toBe(0) + }) + + it('current_score resets to 0 when a new game starts via simon.start()', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(score.current_score).toBeGreaterThan(0) + await vi.runAllTimersAsync() + simon.press(wrong_color(seq_at(0))) + simon.release() + expect(simon.phase).toBe('gameover') + simon.start() + expect(score.current_score).toBe(0) + }) +}) + +describe('victory flash', () => { + beforeEach(() => { + vi.useFakeTimers() + simon.reset() + }) + + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + vi.restoreAllMocks() + simon.reset() + }) + + it('flash_colors is empty before any round completes', () => { + simon.start() + expect(simon.flash_colors).toHaveLength(0) + }) + + it('flash_colors contains all 4 colors immediately after release on round_complete', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.flash_colors).toHaveLength(4) + expect(simon.flash_colors).toEqual(expect.arrayContaining(ALL_COLORS)) + }) + + it('flash_intensity is greater than 1 immediately after release on round_complete', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.flash_intensity).toBeGreaterThan(1) + }) + + it('play_tone is called for all colors during burst stage', async () => { + const spy = vi.spyOn(simon_audio, 'play_tone') + simon.start() + await vi.runAllTimersAsync() + spy.mockClear() + simon.press(seq_at(0)) + simon.release() + const called_colors = spy.mock.calls.map((c) => c[0]) + expect(called_colors).toContain('green') + expect(called_colors).toContain('red') + expect(called_colors).toContain('yellow') + expect(called_colors).toContain('blue') + }) + + it('flash_colors and flash_intensity reset after full flash duration', async () => { + const flash_total_ms = + FLASH_BURST_CYCLES * (FLASH_BURST_ON_MS + FLASH_BURST_OFF_MS) + + ALL_COLORS.length * (FLASH_CASCADE_FWD_MS + FLASH_CASCADE_REV_MS) + + FLASH_FINALE_MS + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + await vi.advanceTimersByTimeAsync(flash_total_ms + 10) + expect(simon.flash_colors).toHaveLength(0) + expect(simon.flash_intensity).toBe(1) + }) + + it('reset() clears flash_colors and flash_intensity immediately', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.flash_colors).toHaveLength(4) + simon.reset() + expect(simon.flash_colors).toHaveLength(0) + expect(simon.flash_intensity).toBe(1) + }) + + it('flash_colors and flash_intensity cleared when next round starts', async () => { + simon.start() + await vi.runAllTimersAsync() + simon.press(seq_at(0)) + simon.release() + expect(simon.flash_colors).toHaveLength(4) + await vi.advanceTimersByTimeAsync(RESTART_DELAY_MS) + expect(simon.flash_colors).toHaveLength(0) + expect(simon.flash_intensity).toBe(1) + }) +}) + +describe('create_simon isolation', () => { + beforeEach(() => { + vi.useFakeTimers() + }) + + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + vi.restoreAllMocks() + }) + + it('two instances do not share phase state', () => { + const score_a = create_score() + const score_b = create_score() + const a = create_simon(score_a) + const b = create_simon(score_b) + a.start() + expect(a.phase).toBe('showing') + expect(b.phase).toBe('idle') + a.reset() + }) + + it('two instances do not share sequence state', () => { + const score_a = create_score() + const score_b = create_score() + const a = create_simon(score_a) + const b = create_simon(score_b) + a.start() + expect(a.sequence).toHaveLength(1) + expect(b.sequence).toHaveLength(0) + a.reset() + }) + + it('create_simon with custom colors only uses those colors in sequence', () => { + const score_c = create_score() + const custom_colors: ButtonColor[] = ['green', 'blue'] + const c = create_simon(score_c, { colors: custom_colors }) + c.start() + for (let i = 0; i < 20; i++) { + c.reset() + c.start() + } + const used = new Set(c.sequence) + for (const color of used) expect(custom_colors).toContain(color) + c.reset() + }) +}) diff --git a/src/lib/simon/simon.svelte.ts b/src/lib/simon/simon.svelte.ts new file mode 100644 index 0000000..dbc02ab --- /dev/null +++ b/src/lib/simon/simon.svelte.ts @@ -0,0 +1,220 @@ +import { game_state } from '$lib/game/state.svelte' +import { simon_audio } from './audio' +import { score as default_score, type ScoreInstance } from './score.svelte' +import { cancel_flash, run_victory_flash, type FlashState, type FlashTimers } from './simon-flash' +import type { ButtonColor, SimonPhase } from './types' + +export const STEP_MS_1_5 = 500 +export const STEP_MS_6_13 = 400 +export const STEP_MS_14_20 = 250 +export const STEP_MS_21_PLUS = 150 +export const ON_RATIO = 0.7 +export const OFF_RATIO = 0.3 +export const ERROR_BEEP_MS = 3000 +export const RESTART_DELAY_MS = 1000 + +const DEFAULT_COLORS: readonly ButtonColor[] = ['green', 'red', 'yellow', 'blue'] +const FALLBACK_COLOR: ButtonColor = 'green' + +type SimonState = { + phase: SimonPhase + sequence: ButtonColor[] + position: number + active_color: ButtonColor | null + pressed_color: ButtonColor | null + round: number +} & FlashState + +type SimonTimers = { + show_gen: number + restart_timer: ReturnType | null + input_start_ms: number +} & FlashTimers + +function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +function get_step_ms(len: number): number { + if (len <= 5) return STEP_MS_1_5 + if (len <= 13) return STEP_MS_6_13 + if (len <= 20) return STEP_MS_14_20 + return STEP_MS_21_PLUS +} + +function add_to_sequence(s: SimonState, colors: readonly ButtonColor[]): void { + const index = Math.floor(Math.random() * colors.length) // NOSONAR — game RNG, not security-sensitive + s.sequence.push(colors[index] ?? FALLBACK_COLOR) +} + +function cancel_restart_timer(t: SimonTimers): void { + if (t.restart_timer !== null) clearTimeout(t.restart_timer) + t.restart_timer = null +} + +async function run_show(s: SimonState, t: SimonTimers, gen: number): Promise { + const step_ms = get_step_ms(s.sequence.length) + const on_ms = step_ms * ON_RATIO + const off_ms = step_ms * OFF_RATIO + for (const color of s.sequence) { + if (gen !== t.show_gen) return + s.active_color = color + simon_audio.play_tone(color, on_ms, game_state.is_alt) + await delay(on_ms) + if (gen !== t.show_gen) return + s.active_color = null + await delay(off_ms) + } + if (gen !== t.show_gen) return + t.input_start_ms = Date.now() + s.phase = 'player_input' + s.position = 0 +} + +function start_next_round(s: SimonState, t: SimonTimers, colors: readonly ButtonColor[]): void { + t.restart_timer = null + cancel_flash(s, t) + s.round += 1 + add_to_sequence(s, colors) + t.show_gen += 1 + void run_show(s, t, t.show_gen) +} + +function schedule_next_round(s: SimonState, t: SimonTimers, colors: readonly ButtonColor[]): void { + cancel_restart_timer(t) + cancel_flash(s, t) + s.phase = 'showing' + void run_victory_flash(s, t, colors, t.flash_gen) + t.restart_timer = setTimeout(() => start_next_round(s, t, colors), RESTART_DELAY_MS) +} + +function handle_correct_release( + s: SimonState, + t: SimonTimers, + score: ScoreInstance, + colors: readonly ButtonColor[], +): void { + s.position += 1 + if (s.position < s.sequence.length) return + score.add_round_score(Date.now() - t.input_start_ms, s.sequence.length, s.round) + s.phase = 'round_complete' + schedule_next_round(s, t, colors) +} + +function start_simon( + s: SimonState, + t: SimonTimers, + score: ScoreInstance, + colors: readonly ButtonColor[], +): void { + if (s.phase === 'showing' || s.phase === 'player_input') return + cancel_restart_timer(t) + score.reset() + s.phase = 'showing' + s.round = 1 + s.sequence = [] + add_to_sequence(s, colors) + t.show_gen += 1 + void run_show(s, t, t.show_gen) +} + +function release_simon( + s: SimonState, + t: SimonTimers, + score: ScoreInstance, + colors: readonly ButtonColor[], +): void { + simon_audio.stop_tone() + const color = s.pressed_color + s.pressed_color = null + if (s.phase !== 'player_input') return + if (color === null) return + if (color === s.sequence[s.position]) { + handle_correct_release(s, t, score, colors) + } else { + simon_audio.play_error_tone(ERROR_BEEP_MS, game_state.is_alt) + s.phase = 'gameover' + } +} + +function press_simon(s: SimonState, color: ButtonColor): void { + if (s.phase !== 'player_input') return + if (s.pressed_color !== null) return + s.pressed_color = color + simon_audio.start_tone(color, game_state.is_alt) +} + +function reset_simon(s: SimonState, t: SimonTimers, score: ScoreInstance): void { + t.show_gen += 1 + simon_audio.stop_tone() + s.pressed_color = null + cancel_restart_timer(t) + cancel_flash(s, t) + score.reset() + s.phase = 'idle' + s.sequence = [] + s.position = 0 + s.active_color = null + t.input_start_ms = 0 + s.round = 0 +} + +function make_simon_api( + s: SimonState, + t: SimonTimers, + score: ScoreInstance, + colors: readonly ButtonColor[], +) { + return { + get phase() { + return s.phase + }, + get sequence() { + return s.sequence + }, + get position() { + return s.position + }, + get active_color() { + return s.active_color + }, + get pressed_color() { + return s.pressed_color + }, + get round() { + return s.round + }, + get flash_colors() { + return s.flash_colors + }, + get flash_intensity() { + return s.flash_intensity + }, + start: (): void => start_simon(s, t, score, colors), + press: (color: ButtonColor): void => press_simon(s, color), + release: (): void => release_simon(s, t, score, colors), + reset: (): void => reset_simon(s, t, score), + } +} + +type SimonConfig = { colors?: readonly ButtonColor[] } + +export function create_simon(score: ScoreInstance, config: SimonConfig = {}) { + const colors = config.colors ?? DEFAULT_COLORS + const s = $state({ + phase: 'idle', + sequence: [], + position: 0, + active_color: null, + pressed_color: null, + round: 0, + flash_colors: [], + flash_intensity: 1, + }) + const t: SimonTimers = { show_gen: 0, flash_gen: 0, restart_timer: null, input_start_ms: 0 } + return make_simon_api(s, t, score, colors) +} + +export type SimonInstance = ReturnType + +export const simon = create_simon(default_score) diff --git a/src/lib/simon/types.spec.ts b/src/lib/simon/types.spec.ts new file mode 100644 index 0000000..70b0d19 --- /dev/null +++ b/src/lib/simon/types.spec.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest' +import type { ButtonColor, SimonBoardData } from './types' + +const ALL_BUTTON_COLORS: ButtonColor[] = ['green', 'red', 'yellow', 'blue'] + +describe('simon types', () => { + it('ButtonColor covers all four expected colors', () => { + expect(ALL_BUTTON_COLORS).toHaveLength(4) + expect(ALL_BUTTON_COLORS).toContain('green') + expect(ALL_BUTTON_COLORS).toContain('red') + expect(ALL_BUTTON_COLORS).toContain('yellow') + expect(ALL_BUTTON_COLORS).toContain('blue') + }) + + it('SimonBoardData accepts a valid shape', () => { + const data: SimonBoardData = { + active_color: null, + pressed_color: 'green', + phase: 'idle', + round: 0, + flash_colors: [], + flash_intensity: 1, + } + expect(data.phase).toBe('idle') + expect(data.pressed_color).toBe('green') + }) +}) diff --git a/src/lib/simon/types.ts b/src/lib/simon/types.ts new file mode 100644 index 0000000..c00bbb5 --- /dev/null +++ b/src/lib/simon/types.ts @@ -0,0 +1,12 @@ +export type ButtonColor = 'green' | 'red' | 'yellow' | 'blue' + +export interface SimonBoardData { + active_color: ButtonColor | null + pressed_color: ButtonColor | null + phase: string + round: number + flash_colors: readonly ButtonColor[] + flash_intensity: number +} + +export type SimonPhase = 'idle' | 'showing' | 'player_input' | 'round_complete' | 'gameover' diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..f06db87 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,46 @@ + + + + {messages.game_title} + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 9c42926..bee544f 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,3 +1,32 @@ -

Welcome to your library project

-

Create your package using @sveltejs/package and preview/showcase your work with SvelteKit

-

Visit svelte.dev/docs/kit to read the documentation

+ + + + + diff --git a/src/routes/+page.ts b/src/routes/+page.ts new file mode 100644 index 0000000..62ad4e4 --- /dev/null +++ b/src/routes/+page.ts @@ -0,0 +1 @@ +export const ssr = false diff --git a/src/routes/demo/+page.svelte b/src/routes/demo/+page.svelte new file mode 100644 index 0000000..470dde4 --- /dev/null +++ b/src/routes/demo/+page.svelte @@ -0,0 +1,5 @@ + + +playwright diff --git a/src/routes/demo/playwright/+page.svelte b/src/routes/demo/playwright/+page.svelte new file mode 100644 index 0000000..5f0f2c9 --- /dev/null +++ b/src/routes/demo/playwright/+page.svelte @@ -0,0 +1 @@ +

Playwright e2e test demo

diff --git a/src/routes/demo/playwright/page.svelte.e2e.ts b/src/routes/demo/playwright/page.svelte.e2e.ts new file mode 100644 index 0000000..c559970 --- /dev/null +++ b/src/routes/demo/playwright/page.svelte.e2e.ts @@ -0,0 +1,6 @@ +import { expect, test } from '@playwright/test' + +test('has expected h1', async ({ page }) => { + await page.goto('/demo/playwright') + await expect(page.locator('h1')).toBeVisible() +}) diff --git a/src/routes/layout.css b/src/routes/layout.css new file mode 100644 index 0000000..a97d7a2 --- /dev/null +++ b/src/routes/layout.css @@ -0,0 +1,15 @@ +@import 'tailwindcss'; +@plugin '@tailwindcss/forms'; +@plugin '@tailwindcss/typography'; + +@font-face { + font-family: 'PressStart2P'; + src: url('/fonts/PressStart2P.ttf') format('truetype'); + font-display: swap; +} + +@font-face { + font-family: 'Orbitron'; + src: url('/fonts/Orbitron.ttf') format('truetype'); + font-display: swap; +} diff --git a/src/routes/page.e2e.ts b/src/routes/page.e2e.ts new file mode 100644 index 0000000..10e241f --- /dev/null +++ b/src/routes/page.e2e.ts @@ -0,0 +1,341 @@ +import { readFileSync } from 'node:fs' +import AxeBuilder from '@axe-core/playwright' +import { expect, test, type Page } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'), +) as { version: string } + +const LOADING_OVERLAY_TIMEOUT_MS = 8000 +const FULLSCREEN_NOT_CALLED_WAIT_MS = 200 +const TOUCH_PRIMARY_QUERY = '(hover: none) and (pointer: coarse)' +const READY_PROGRESS_VALUE = 100 +const HIGH_SCORE_STORAGE_KEY = 'simon_high_score' +const HIGH_SCORE_ROUND_KEY = 'simon_high_score_round' +const HIGH_SCORE_CHECK_KEY = 'simon_high_score_check' +const CHECK_SEED = 0x9e3779b9 +const SAMPLE_HIGH_SCORE = 5000 +const SAMPLE_HIGH_ROUND = 3 + +async function stub_touch_primary(page: Page, is_touch: boolean): Promise { + await page.addInitScript( + ([query, matches]) => { + const original = globalThis.matchMedia.bind(globalThis) + globalThis.matchMedia = function patched(input: string): MediaQueryList { + if (input === query) { + return { + matches: matches as boolean, + media: input, + onchange: null, + addEventListener() {}, + removeEventListener() {}, + addListener() {}, + removeListener() {}, + dispatchEvent() { + return false + }, + } as MediaQueryList + } + return original(input) + } + }, + [TOUCH_PRIMARY_QUERY, is_touch] as const, + ) +} + +test('page response includes HTTP security headers', async ({ page }) => { + const response = await page.goto('/') + const headers = response?.headers() ?? {} + expect(headers['x-frame-options']).toBe('SAMEORIGIN') + expect(headers['x-content-type-options']).toBe('nosniff') + expect(headers['referrer-policy']).toBe('strict-origin-when-cross-origin') + expect(headers['permissions-policy']).toContain('camera=()') + expect(headers['content-security-policy']).toContain("default-src 'self'") +}) + +test('game scene renders immediately with canvas', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + await expect(page.locator('[data-testid="game-scene"] canvas')).toBeVisible() +}) + +test('loading overlay is visible immediately on page load', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"]')).toBeVisible() +}) + +test('loading overlay displays the logo svg', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] svg.logo')).toBeVisible() +}) + +test('loading overlay displays Joshua Folkken below the logo', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] .brand')).toHaveText('Joshua Folkken') +}) + +test('loading overlay displays game title below the brand', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] .game-title')).toHaveText('SIMON') +}) + +test('loading overlay displays game version below the brand', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] .game-version')).toHaveText( + `v${version}`, + ) +}) + +test('loading overlay reaches 100% progress once the scene is ready', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] .progress')).toHaveText('100%', { + timeout: LOADING_OVERLAY_TIMEOUT_MS, + }) +}) + +test('loading overlay shows ready text once the scene is ready', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] .status')).toHaveText('READY', { + timeout: LOADING_OVERLAY_TIMEOUT_MS, + }) +}) + +test('loading overlay disappears once the scene is ready', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"]')).toBeHidden({ + timeout: LOADING_OVERLAY_TIMEOUT_MS, + }) + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() +}) + +test('controls overlay is visible before the user clicks', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="controls-overlay"]')).toBeVisible() + await expect(page.locator('[data-testid="start-hint"]')).toBeVisible() +}) + +test('controls overlay disappears after the game scene is clicked', async ({ page }) => { + await page.goto('/') + await page.locator('[data-testid="game-scene"]').click() + await expect(page.locator('[data-testid="controls-overlay"]')).toHaveCount(0) +}) + +test('first click on the game scene does not toggle cyber mode while controls overlay is shown', async ({ + page, +}) => { + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + await expect(page.locator('[data-testid="controls-overlay"]')).toBeVisible() + const glow_locator = page.locator('[data-testid="cyber-glow"]') + const initial_glow_count = await glow_locator.count() + await page.locator('[data-testid="game-scene"]').click() + await expect(page.locator('[data-testid="controls-overlay"]')).toHaveCount(0) + const after_glow_count = await glow_locator.count() + expect(after_glow_count).toBe(initial_glow_count) +}) + +test('fullscreen is requested on touch-primary devices when start hint is clicked', async ({ + page, +}) => { + await stub_touch_primary(page, true) + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + + const fullscreen_target = await page.evaluate( + () => + new Promise((resolve) => { + const scene = document.querySelector('[data-testid="game-scene"]') + if (!scene) { + resolve('no-scene') + return + } + scene.requestFullscreen = function (): Promise { + resolve('game-scene') + return Promise.resolve() + } + scene.click() + }), + ) + + expect(fullscreen_target).toBe('game-scene') +}) + +test('fullscreen is NOT requested on desktop devices when start hint is clicked', async ({ + page, +}) => { + await stub_touch_primary(page, false) + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + + const was_called = await page.evaluate( + (wait_ms) => + new Promise((resolve) => { + const scene = document.querySelector('[data-testid="game-scene"]') + if (!scene) { + resolve(false) + return + } + let called = false + scene.requestFullscreen = function (): Promise { + called = true + return Promise.resolve() + } + scene.click() + setTimeout(() => resolve(called), wait_ms) + }), + FULLSCREEN_NOT_CALLED_WAIT_MS, + ) + + expect(was_called).toBe(false) + await expect(page.locator('[data-testid="controls-overlay"]')).toHaveCount(0) +}) + +test('pseudo-fullscreen class is applied when native API is unavailable on touch devices', async ({ + page, +}) => { + await stub_touch_primary(page, true) + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + + await page.evaluate(() => { + const scene = document.querySelector('[data-testid="game-scene"]') + if (!scene) return + Object.defineProperty(scene, 'requestFullscreen', { value: undefined, configurable: true }) + Object.defineProperty(scene, 'webkitRequestFullscreen', { + value: undefined, + configurable: true, + }) + scene.click() + }) + + await expect(page.locator('[data-testid="game-scene"]')).toHaveClass(/pseudo-fullscreen/) +}) + +test('game scene has role="application" for screen reader keyboard pass-through', async ({ + page, +}) => { + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toHaveAttribute('role', 'application') +}) + +test('game scene can be started with Enter key after focusing via Tab', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + await page.keyboard.press('Tab') + await page.keyboard.press('Enter') + await expect(page.locator('.click-hint')).toHaveCount(0) +}) + +test('game scene can be started with Space key after focusing via Tab', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="game-scene"]')).toBeVisible() + await page.keyboard.press('Tab') + await page.keyboard.press('Space') + await expect(page.locator('.click-hint')).toHaveCount(0) +}) + +test('loading overlay uses native progress element for accessible progress', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] progress.bar')).toBeVisible() + await expect(page.locator('[data-testid="loading-overlay"] progress.bar')).toHaveAttribute( + 'max', + '100', + ) +}) + +test('loading overlay progress element reaches 100 when the scene is ready', async ({ page }) => { + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"] progress.bar')).toHaveJSProperty( + 'value', + READY_PROGRESS_VALUE, + { + timeout: LOADING_OVERLAY_TIMEOUT_MS, + }, + ) +}) + +test('page has no critical or serious accessibility violations', async ({ page }) => { + await page.goto('/') + const results = await new AxeBuilder({ page }).exclude('canvas').analyze() + const violations = results.violations.filter( + (v) => v.impact === 'critical' || v.impact === 'serious', + ) + expect(violations).toHaveLength(0) +}) + +test('high score persists in localStorage across page reload', async ({ page }) => { + const stored_check = + (Math.imul(SAMPLE_HIGH_SCORE + 1, CHECK_SEED) ^ + Math.imul(SAMPLE_HIGH_ROUND + 1, CHECK_SEED >>> 1)) >>> + 0 + await page.goto('/') + await page.evaluate( + ([sk, rk, ck, score, round, check]) => { + localStorage.setItem(sk, String(score)) + localStorage.setItem(rk, String(round)) + localStorage.setItem(ck, String(check)) + }, + [ + HIGH_SCORE_STORAGE_KEY, + HIGH_SCORE_ROUND_KEY, + HIGH_SCORE_CHECK_KEY, + SAMPLE_HIGH_SCORE, + SAMPLE_HIGH_ROUND, + stored_check, + ] as const, + ) + await page.goto('/') + const [score_val, round_val, check_val] = await page.evaluate( + ([sk, rk, ck]) => [ + localStorage.getItem(sk), + localStorage.getItem(rk), + localStorage.getItem(ck), + ], + [HIGH_SCORE_STORAGE_KEY, HIGH_SCORE_ROUND_KEY, HIGH_SCORE_CHECK_KEY] as const, + ) + expect(score_val).toBe(String(SAMPLE_HIGH_SCORE)) + expect(round_val).toBe(String(SAMPLE_HIGH_ROUND)) + expect(check_val).toBe(String(stored_check)) +}) + +test('game scene loads without shadow-related WebGL errors', async ({ page }) => { + const errors: string[] = [] + page.on('pageerror', (err) => errors.push(err.message)) + await page.goto('/') + await expect(page.locator('[data-testid="loading-overlay"]')).toBeHidden({ + timeout: LOADING_OVERLAY_TIMEOUT_MS, + }) + const webgl_errors = errors.filter( + (e) => e.toLowerCase().includes('shadow') || e.toLowerCase().includes('webgl'), + ) + expect(webgl_errors).toHaveLength(0) +}) + +test('favicon link points to the Simon icon, not the Svelte logo', async ({ page }) => { + await page.goto('/') + const icon_href = await page.evaluate(() => { + const links = document.querySelectorAll('link[rel="icon"]') + const last = links[links.length - 1] + return last?.getAttribute('href') ?? null + }) + expect(icon_href).toBe('/icon.svg') +}) + +test('PWA manifest is linked in document head', async ({ page }) => { + await page.goto('/') + const manifest_href = await page.evaluate(() => { + const link = document.querySelector('link[rel="manifest"]') + return link?.href ?? null + }) + expect(manifest_href).not.toBeNull() +}) + +test('service worker is ready after page load', async ({ page }) => { + await page.goto('/') + const scope = await page.evaluate(async () => { + if (!('serviceWorker' in navigator)) return null + const reg = await navigator.serviceWorker.ready + return reg.scope + }) + expect(scope).toBeTruthy() +}) diff --git a/src/routes/page.svelte.spec.ts b/src/routes/page.svelte.spec.ts new file mode 100644 index 0000000..6c11b5e --- /dev/null +++ b/src/routes/page.svelte.spec.ts @@ -0,0 +1,25 @@ +import { game_state } from '$lib/game/state.svelte' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { render } from 'vitest-browser-svelte' +import Page from './+page.svelte' + +describe('Home page', () => { + beforeEach(() => { + if (game_state.is_alt) game_state.toggle_alt() + }) + + afterEach(() => { + if (game_state.is_alt) game_state.toggle_alt() + }) + + it('does not render cyber-glow in normal mode', () => { + const { container } = render(Page) + expect(container.querySelector('[data-testid="cyber-glow"]')).toBeNull() + }) + + it('renders cyber-glow when cyber mode is active', () => { + game_state.toggle_alt() + const { container } = render(Page) + expect(container.querySelector('[data-testid="cyber-glow"]')).toBeTruthy() + }) +}) diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a6ee9d2b6391336b12c7dd0c314c3a66ad631d7 GIT binary patch literal 15452 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 UTH4{a|8BtKq?9G=#Z5#14@K&o#Q*>R literal 0 HcmV?d00001 diff --git a/static/fonts/Orbitron.ttf b/static/fonts/Orbitron.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a24fa0212d16c335dacf992101e08de299f1101e GIT binary patch literal 17752 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 ALjV8( literal 0 HcmV?d00001 diff --git a/static/icon-192.png b/static/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b1a9bea262d6d41e16afcf8357702dee70e75b97 GIT binary patch literal 16692 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 + + + + + + + + + + + + + + + + + + + + + diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js index 327aec1..4e5a768 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,8 +1,13 @@ +import adapter from '@sveltejs/adapter-cloudflare' + /** @type {import('@sveltejs/kit').Config} */ const config = { compilerOptions: { runes: true, }, + kit: { + adapter: adapter(), + }, } export default config diff --git a/tsconfig.json b/tsconfig.json index 5687286..156e675 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,11 +7,12 @@ "rewriteRelativeImportExtensions": true, "allowJs": true, "checkJs": true, + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, "strict": true, - "noEmitOnError": false + "moduleResolution": "bundler" } } diff --git a/vite.config.ts b/vite.config.ts index a159477..96e720d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,56 @@ import { sveltekit } from '@sveltejs/kit/vite' +import tailwindcss from '@tailwindcss/vite' +import { SvelteKitPWA } from '@vite-pwa/sveltekit' +import { playwright } from '@vitest/browser-playwright' import { defineConfig } from 'vitest/config' +const BRAND_COLOR = '#0d0d12' + +const PWA_MANIFEST = { + name: 'Simon', + short_name: 'Simon', + description: 'A Simon memory game', + start_url: '/', + display: 'standalone' as const, + background_color: BRAND_COLOR, + theme_color: BRAND_COLOR, + icons: [ + { src: '/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' }, + { src: '/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any maskable' }, + { src: '/icon.svg', sizes: 'any', type: 'image/svg+xml', purpose: 'any' }, + ], +} + export default defineConfig({ - plugins: [sveltekit()], + plugins: [ + tailwindcss(), + sveltekit(), + SvelteKitPWA({ + registerType: 'autoUpdate', + injectRegister: null, + manifest: PWA_MANIFEST, + workbox: { globPatterns: ['**/*.{js,css,html,ico,png,svg,opus,woff2}'] }, + }), + ], + server: { + allowedHosts: ['.trycloudflare.com'], + }, test: { expect: { requireAssertions: true }, projects: [ + { + extends: './vite.config.ts', + test: { + name: 'client', + browser: { + enabled: true, + provider: playwright(), + instances: [{ browser: 'chromium', headless: true }], + }, + include: ['src/**/*.svelte.{test,spec}.{js,ts}'], + exclude: ['src/lib/server/**'], + }, + }, { extends: './vite.config.ts', test: { diff --git a/wrangler.jsonc b/wrangler.jsonc new file mode 100644 index 0000000..a6ed059 --- /dev/null +++ b/wrangler.jsonc @@ -0,0 +1,50 @@ +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "name": "game-kit", + "compatibility_date": "2026-05-05", + "compatibility_flags": ["nodejs_compat"], + "main": ".svelte-kit/cloudflare/_worker.js", + "assets": { + "binding": "ASSETS", + "directory": ".svelte-kit/cloudflare" + }, + "workers_dev": true, + "preview_urls": true, + + "observability": { + "logs": { + "enabled": true, + "head_sampling_rate": 0.1, + "invocation_logs": true + }, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } + }, + + "routes": [ + { + "pattern": "game-kit.joshuafolkken.com", + "custom_domain": true + } + ] + + // "kv_namespaces": [ + // { + // "binding": "CACHE", + // "id": "", + // "preview_id": "" + // } + // ], + + // "d1_databases": [ + // { + // "binding": "DB", + // "database_name": "your-db-name", + // "database_id": "", + // "migrations_dir": "drizzle/migrations" + // // "remote": true + // } + // ] +} From 5ac1c03f105005367cdf4e1010dad2d97a65ef75 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Tue, 5 May 2026 22:45:38 +0700 Subject: [PATCH 02/19] Fix playwright.config.ts to use defineConfig directly #24 Co-Authored-By: Claude Sonnet 4.6 --- playwright.config.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index bdb1dfa..2c13793 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,6 +1,7 @@ -import { create_playwright_config } from '@joshuafolkken/kit/playwright/base' +import { defineConfig } from '@playwright/test' -export default create_playwright_config({ - dev_port: 5173, - preview_port: 4173, +export default defineConfig({ + webServer: { command: 'pnpm run build && pnpm run preview', port: 4173 }, + testMatch: '**/*.e2e.{ts,js}', + workers: 1, }) From 6dda8280b2b5507613b3bf67917c3fb65d5a61a3 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Tue, 5 May 2026 22:47:08 +0700 Subject: [PATCH 03/19] Revert playwright.config.ts to create_playwright_config #24 Co-Authored-By: Claude Sonnet 4.6 --- playwright.config.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index 2c13793..bdb1dfa 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,7 +1,6 @@ -import { defineConfig } from '@playwright/test' +import { create_playwright_config } from '@joshuafolkken/kit/playwright/base' -export default defineConfig({ - webServer: { command: 'pnpm run build && pnpm run preview', port: 4173 }, - testMatch: '**/*.e2e.{ts,js}', - workers: 1, +export default create_playwright_config({ + dev_port: 5173, + preview_port: 4173, }) From a319320549787aeb18f13b7c761d7c0599a12e9d Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Tue, 5 May 2026 22:50:42 +0700 Subject: [PATCH 04/19] Restore noEmitOnError false for library type generation #24 Co-Authored-By: Claude Sonnet 4.6 --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 156e675..d475c82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "moduleResolution": "bundler" + "moduleResolution": "bundler", + "noEmitOnError": false } } From 19855e7da8b7a4f724123bcb1248f3575f47500b Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 21:52:07 +0700 Subject: [PATCH 05/19] Fix playwright config and update project files #24 Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/auto-tag.yml | 54 +++++++++++-------------------- .github/workflows/ci.yml | 4 +-- .npmrc | 1 + AGENTS.md | 10 +++--- CLAUDE.md | 10 +++--- GEMINI.md | 10 +++--- package.json | 13 ++++++-- playwright.config.ts | 58 +++++++++++++++++++++++++++++++--- pnpm-lock.yaml | 29 +++++++++-------- pnpm-workspace.yaml | 11 ++++--- src/app.html | 1 + 11 files changed, 127 insertions(+), 74 deletions(-) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 4286a56..04abcee 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -1,10 +1,8 @@ name: Auto Tag on Version Change on: - push: - branches: [main] - paths: - - 'package.json' + repository_dispatch: + types: [ci-passed-on-main] jobs: create-tag: @@ -12,11 +10,11 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - actions: read steps: - name: Checkout code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: + ref: ${{ github.event.client_payload.sha }} fetch-depth: 2 - name: Setup Node.js @@ -24,8 +22,20 @@ jobs: with: node-version: '24' + - name: Check if package.json changed + id: pkg_changed + run: | + if git diff HEAD~1 --name-only | grep -q '^package.json$'; then + echo "changed=true" >> $GITHUB_OUTPUT + echo "✅ package.json changed in this commit" + else + echo "changed=false" >> $GITHUB_OUTPUT + echo "ℹ️ package.json not changed, skipping tag creation" + fi + - name: Get current version id: current_version + if: steps.pkg_changed.outputs.changed == 'true' run: | VERSION=$(node -p "require('./package.json').version") echo "version=$VERSION" >> $GITHUB_OUTPUT @@ -33,6 +43,7 @@ jobs: - name: Check if version changed id: version_check + if: steps.pkg_changed.outputs.changed == 'true' env: CURRENT_VERSION: ${{ steps.current_version.outputs.version }} run: | @@ -54,38 +65,9 @@ jobs: echo "ℹ️ First commit, creating tag" fi - - name: Wait for CI to pass on this commit - id: ci_gate - if: steps.version_check.outputs.changed == 'true' - env: - SHA: ${{ github.sha }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - for attempt in $(seq 1 18); do - result=$(gh run list --workflow ci.yml --repo "$GITHUB_REPOSITORY" \ - --json headSha,conclusion,status \ - --jq "[.[] | select(.headSha == \"$SHA\")] | .[0] | .conclusion // .status // \"none\"" 2>/dev/null || echo "none") - case "$result" in - success) - echo "passed=true" >> $GITHUB_OUTPUT - echo "✅ CI passed, proceeding with tag creation" - exit 0 - ;; - failure | cancelled | timed_out) - echo "passed=false" >> $GITHUB_OUTPUT - echo "⚠️ CI $result on this commit, skipping tag creation" - exit 0 - ;; - esac - echo "CI status: $result (attempt $attempt/18)" - sleep 10 - done - echo "passed=false" >> $GITHUB_OUTPUT - echo "⏱️ Timed out waiting for CI" - - name: Check if tag already exists id: tag_check - if: steps.version_check.outputs.changed == 'true' && steps.ci_gate.outputs.passed == 'true' + if: steps.version_check.outputs.changed == 'true' env: VERSION: ${{ steps.current_version.outputs.version }} run: | @@ -101,7 +83,7 @@ jobs: - name: Create and push tag id: create_tag - if: steps.version_check.outputs.changed == 'true' && steps.ci_gate.outputs.passed == 'true' && steps.tag_check.outputs.exists == 'false' + if: steps.version_check.outputs.changed == 'true' && steps.tag_check.outputs.exists == 'false' env: VERSION: ${{ steps.current_version.outputs.version }} run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bc2176..5cc4d6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install pnpm - uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6.0.3 + uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 @@ -116,7 +116,7 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install pnpm - uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6.0.3 + uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 diff --git a/.npmrc b/.npmrc index 3f1658b..1686e47 100644 --- a/.npmrc +++ b/.npmrc @@ -3,3 +3,4 @@ engine-strict=true minimum-release-age=1440 +confirmModulesPurge=false diff --git a/AGENTS.md b/AGENTS.md index f7a8860..f042c0b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,7 @@ GitHub operations use the `gh` CLI. Authenticate once with `gh auth login`; no a - **NEVER** remove or modify entries in `pnpm.overrides` without explicit user approval. - After running `pnpm update`, `josh latest`, or any dependency-update command, verify that `pnpm.overrides` is unchanged **and** that `devDependencies` versions still respect the overrides. If any entry was removed, modified, or bumped past an override, restore it immediately. +- **NEVER** modify the `devEngines` field in `package.json` without explicit user confirmation. `devEngines` pins the required development toolchain (e.g. pnpm version); silently changing it can break CI or other contributors' environments. After any dependency-update command, verify `devEngines` is unchanged. If it was modified, restore it immediately and ask the user before making any change. ## Code Change Rules @@ -140,6 +141,7 @@ Before every `git commit` — including follow-up commits on the same branch — - **No commits** unless explicitly requested by the user - **No PR merges, branch deletions, force pushes, or other shared-state mutations** unless explicitly requested in the current turn. The default end state is PR still OPEN — do not run `gh pr merge` on your own. **Exception**: invoking `fullrun` or `fullrun new` is explicit authorization to merge; use `pnpm josh followup --merge` in that flow. - For git operations: use `pnpm josh git` +- **Recovery after failed push**: If `pnpm josh git -y` fails at the push step (e.g. pre-push hook blocked), fix the issue, push manually, then run `pnpm josh pr` (or `pnpm josh git -y --skip-commit --skip-push`) to create the PR. **Never** use `gh pr create` directly — it bypasses `closes #N` generation and the Issue will not auto-close. - **Start-of-conversation git status is a stale snapshot.** The `gitStatus` block in the environment preamble is captured once at session start and never refreshes. Before acting on any assumption about working-tree / index / stash / branch state, run `git status` live first. Never report state or propose a plan based on the snapshot alone. ## Collaboration Workflow @@ -155,8 +157,8 @@ Before every `git commit` — including follow-up commits on the same branch — #### `fullrun` — Full execution (plan → implement → PR → completion notify) -- `fullrun #`: Read Issue #N → **normalize the title**: if the title is not in English or can be phrased more clearly/conventionally, derive a better English title and run `gh issue edit --title ""` → **add `in-progress` label** (create if missing: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`) → post the agreed plan only if the Issue body is blank (use `gh issue edit <N> --body "<plan>"`); if the body already has content, skip the plan-posting step → implement → `pnpm josh bump minor` → `pnpm josh git -y` → run `/review` skill → `pnpm josh followup --merge`. Issue plan comments MUST be written in English. Before implementing, run `git switch main && git pull`, then `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding (do NOT remove intentional overrides without user approval).** After committing, run the `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean before proceeding to `followup`. When running `pnpm josh followup --merge`, compose an implementation summary in English and pass it via `--notify-message`. Format: `"Implemented <title>:\n- <change1>\n- <change2>\n..."`. **`pnpm josh followup --merge` waits for CI, verifies AI review findings, sends the completion notification, then merges — all in one step. If AI review blockers are found, followup exits non-zero; fix the findings and re-run `pnpm josh followup --merge`.** -- `fullrun new` or `fullrun new "<title>"`: Shortcut that combines `kickoff new` + `fullrun #<N>` into a single run. Steps: (1) Derive an English title from the conversation, or use the provided title. (2) Create Issue: `gh issue create --title "<title>" --body "<body>"`. Capture the new Issue number `<N>`. (3) Add `in-progress` label: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`. (4) Post the agreed plan in English. (5) Run `git switch main && git pull`. (6) Run `josh latest`. **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, restore it before proceeding.** (7) Implement. (8) `pnpm josh bump minor`. (9) `pnpm josh git -y "<title> #<N>"`. (10) Run `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean. (11) `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- <change1>\n- <change2>\n..."`. +- `fullrun #<N>`: Read Issue #N → **normalize the title**: if the title is not in English or can be phrased more clearly/conventionally, derive a better English title and run `gh issue edit <N> --title "<title>"` → **add `in-progress` label** (create if missing: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`) → post the agreed plan only if the Issue body is blank (use `gh issue edit <N> --body "<plan>"`); if the body already has content, skip the plan-posting step → implement → `pnpm josh bump minor` → `pnpm josh git -y` → run `/review` skill → `pnpm josh followup --merge`. Issue plan comments MUST be written in English. Before implementing, run `git switch main && git pull`, then `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding (do NOT remove intentional overrides without user approval). Also verify `devEngines` is unchanged — restore it and ask the user before making any change if it was modified.** After committing, run the `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean before proceeding to `followup`. When running `pnpm josh followup --merge`, compose an implementation summary in English and pass it via `--notify-message`. Format: `"Implemented <title>:\n- <change1>\n- <change2>\n..."`. **`pnpm josh followup --merge` waits for CI, verifies AI review findings, sends the completion notification, then merges — all in one step. If AI review blockers are found, followup exits non-zero; fix the findings and re-run `pnpm josh followup --merge`.** +- `fullrun new` or `fullrun new "<title>"`: Shortcut that combines `kickoff new` + `fullrun #<N>` into a single run. Steps: (1) Derive an English title from the conversation, or use the provided title. (2) Create Issue: `gh issue create --title "<title>" --body "<body>"`. Capture the new Issue number `<N>`. (3) Add `in-progress` label: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`. (4) Post the agreed plan in English. (5) If the working tree already has staged or modified files (e.g., user pre-staged kit/config changes), stash them first: `git stash`. (6) Run `git switch main && git pull`. (7) Run `josh latest` — **mandatory, never skip even if the working tree had modifications**. **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, restore it before proceeding. Also verify `devEngines` is unchanged — restore it and ask the user before making any change if it was modified. If you stashed changes in step 5, restore them now: `git stash pop`.** (8) Implement. (9) `pnpm josh bump minor`. (10) `pnpm josh git -y "<title> #<N>"`. (11) Run `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean. (12) `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- <change1>\n- <change2>\n..."`. #### `queue` — Sequential multi-issue fullrun @@ -164,7 +166,7 @@ Before every `git commit` — including follow-up commits on the same branch — **Steps:** -1. Run `git switch main && git pull`, then `josh latest` once (before the first issue). Verify `pnpm.overrides` is unchanged after `josh latest`. +1. If the working tree already has staged or modified files, stash them first: `git stash`. Run `git switch main && git pull`, then `josh latest` once (before the first issue) — **mandatory, never skip**. Verify `pnpm.overrides` and `devEngines` are unchanged after `josh latest`. If you stashed changes, restore them: `git stash pop`. 2. For each issue `#<N>` in the supplied order: a. From the 2nd issue onward: run `git switch main && git pull` to incorporate the previous PR's merge. b. Execute the full `fullrun #<N>` flow: normalize title → add `in-progress` label → post plan if body is blank → implement → `pnpm josh bump minor` → `pnpm josh git -y "<title> #<N>"` → run `/review` skill → `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- ..."` (sends per-issue completion notification and merges, exactly as `fullrun` does). @@ -174,7 +176,7 @@ Before every `git commit` — including follow-up commits on the same branch — **Key rules:** - Invoking `queue` is explicit authorization to merge each PR (same as `fullrun`). -- `josh latest` runs only once, before the first issue. +- `josh latest` runs only once, before the first issue. If files were pre-staged when `queue` was invoked, they must be stashed before `josh latest` and restored after. - All `kickoff`/`fullrun` mid-workflow stop rules (confirmation notification, AI review blocker handling, etc.) apply within each issue's execution. #### AI reviewer comment scan (automatic in `pnpm josh followup`) diff --git a/CLAUDE.md b/CLAUDE.md index b42e6c7..f29029f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,6 +61,7 @@ GitHub operations use the `gh` CLI. Authenticate once with `gh auth login`; no a - **NEVER** remove or modify entries in `pnpm.overrides` without explicit user approval. - After running `pnpm update`, `josh latest`, or any dependency-update command, verify that `pnpm.overrides` is unchanged **and** that `devDependencies` versions still respect the overrides. If any entry was removed, modified, or bumped past an override, restore it immediately. +- **NEVER** modify the `devEngines` field in `package.json` without explicit user confirmation. `devEngines` pins the required development toolchain (e.g. pnpm version); silently changing it can break CI or other contributors' environments. After any dependency-update command, verify `devEngines` is unchanged. If it was modified, restore it immediately and ask the user before making any change. ## Code Change Rules @@ -140,6 +141,7 @@ Before every `git commit` — including follow-up commits on the same branch — - **No commits** unless explicitly requested by the user - **No PR merges, branch deletions, force pushes, or other shared-state mutations** unless explicitly requested in the current turn. The default end state is PR still OPEN — do not run `gh pr merge` on your own. **Exception**: invoking `fullrun` or `fullrun new` is explicit authorization to merge; use `pnpm josh followup --merge` in that flow. See `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md` → "指示されていない行動は取らない" for the full rule. - For git operations: use `pnpm josh git` +- **Recovery after failed push**: If `pnpm josh git -y` fails at the push step (e.g. pre-push hook blocked), fix the issue, push manually, then run `pnpm josh pr` (or `pnpm josh git -y --skip-commit --skip-push`) to create the PR. **Never** use `gh pr create` directly — it bypasses `closes #N` generation and the Issue will not auto-close. - **Start-of-conversation git status is a stale snapshot.** The `gitStatus` block in the environment preamble is captured once at session start and never refreshes. Before acting on any assumption about working-tree / index / stash / branch state (including "there are uncommitted changes", "staged files remain", "branch is behind"), run `git status` (and `git stash list` if relevant) live first. Never report state, propose a stash/checkout/reset plan, or ask the user to confirm cleanup based on the snapshot alone. ## Collaboration Workflow @@ -155,8 +157,8 @@ Before every `git commit` — including follow-up commits on the same branch — #### `fullrun` — Full execution (plan → implement → PR → completion notify) -- `fullrun #<N>`: Read Issue #N → **normalize the title**: if the title is not in English or can be phrased more clearly/conventionally, derive a better English title and run `gh issue edit <N> --title "<title>"` → **add `in-progress` label** (create if missing: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`) → post the agreed plan only if the Issue body is blank (use `gh issue edit <N> --body "<plan>"`); if the body already has content, skip the plan-posting step → implement → `pnpm josh bump minor` → `pnpm josh git -y` → run `/review` skill → `pnpm josh followup --merge` (full run from Step 3 onward in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`). Issue plan comments MUST be written in English. Before implementing, run `git switch main && git pull`, then `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding (do NOT remove intentional overrides without user approval).** After committing, run the `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean before proceeding to `followup`. When running `pnpm josh followup --merge`, compose an implementation summary in English and pass it via `--notify-message`. Format: `"Implemented <title>:\n- <change1>\n- <change2>\n..."` (one bullet per meaningful change — what was added, changed, or fixed). **`pnpm josh followup --merge` waits for CI, verifies AI review findings, sends the completion notification, then merges — all in one step. If AI review blockers are found, followup exits non-zero; fix the findings and re-run `pnpm josh followup --merge`.** — see `auto-merge` behavior below. -- `fullrun new` or `fullrun new "<title>"`: Shortcut that combines `kickoff new` + `fullrun #<N>` into a single run. When no Issue exists yet (full run from Step 1 onward in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`). Steps: (1) Derive an English title from the conversation, or use the provided title. (2) Create Issue: `gh issue create --title "<title>" --body "<body>"` — body follows the minimum template in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`, filled from conversation context. Capture the new Issue number `<N>`. (3) Add `in-progress` label: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`. (4) Post the agreed plan in English: if the Issue body is blank, use `gh issue edit <N> --body "<plan>"` to fill the body; otherwise use `gh issue comment <N> --body "<plan>"`. (5) Run `git switch main && git pull`. (6) Run `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding.** (7) Implement. (8) `pnpm josh bump minor`. (9) `pnpm josh git -y "<title> #<N>"`. (10) Run `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean. (11) `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- <change1>\n- <change2>\n..."` (one bullet per meaningful change). **`pnpm josh followup --merge` waits for CI, verifies AI review findings (CodeRabbit, Claude Review, SonarQube, etc.), sends the completion notification, then merges. If blockers are found, followup exits non-zero; fix and re-run `pnpm josh followup --merge`.** — see `auto-merge` behavior below. +- `fullrun #<N>`: Read Issue #N → **normalize the title**: if the title is not in English or can be phrased more clearly/conventionally, derive a better English title and run `gh issue edit <N> --title "<title>"` → **add `in-progress` label** (create if missing: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`) → post the agreed plan only if the Issue body is blank (use `gh issue edit <N> --body "<plan>"`); if the body already has content, skip the plan-posting step → implement → `pnpm josh bump minor` → `pnpm josh git -y` → run `/review` skill → `pnpm josh followup --merge` (full run from Step 3 onward in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`). Issue plan comments MUST be written in English. Before implementing, run `git switch main && git pull`, then `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding (do NOT remove intentional overrides without user approval). Also verify `devEngines` is unchanged — restore it and ask the user before making any change if it was modified.** After committing, run the `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean before proceeding to `followup`. When running `pnpm josh followup --merge`, compose an implementation summary in English and pass it via `--notify-message`. Format: `"Implemented <title>:\n- <change1>\n- <change2>\n..."` (one bullet per meaningful change — what was added, changed, or fixed). **`pnpm josh followup --merge` waits for CI, verifies AI review findings, sends the completion notification, then merges — all in one step. If AI review blockers are found, followup exits non-zero; fix the findings and re-run `pnpm josh followup --merge`.** — see `auto-merge` behavior below. +- `fullrun new` or `fullrun new "<title>"`: Shortcut that combines `kickoff new` + `fullrun #<N>` into a single run. When no Issue exists yet (full run from Step 1 onward in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`). Steps: (1) Derive an English title from the conversation, or use the provided title. (2) Create Issue: `gh issue create --title "<title>" --body "<body>"` — body follows the minimum template in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`, filled from conversation context. Capture the new Issue number `<N>`. (3) Add `in-progress` label: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`. (4) Post the agreed plan in English: if the Issue body is blank, use `gh issue edit <N> --body "<plan>"` to fill the body; otherwise use `gh issue comment <N> --body "<plan>"`. (5) If the working tree already has staged or modified files (e.g., user pre-staged kit/config changes), stash them first: `git stash`. (6) Run `git switch main && git pull`. (7) Run `josh latest` — **mandatory, never skip even if the working tree had modifications** (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding. Also verify `devEngines` is unchanged — restore it and ask the user before making any change if it was modified. If you stashed changes in step 5, restore them now: `git stash pop`.** (8) Implement. (9) `pnpm josh bump minor`. (10) `pnpm josh git -y "<title> #<N>"`. (11) Run `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean. (12) `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- <change1>\n- <change2>\n..."` (one bullet per meaningful change). **`pnpm josh followup --merge` waits for CI, verifies AI review findings (CodeRabbit, Claude Review, SonarQube, etc.), sends the completion notification, then merges. If blockers are found, followup exits non-zero; fix and re-run `pnpm josh followup --merge`.** — see `auto-merge` behavior below. #### `queue` — Sequential multi-issue fullrun @@ -164,7 +166,7 @@ Before every `git commit` — including follow-up commits on the same branch — **Steps:** -1. Run `git switch main && git pull`, then `josh latest` once (before the first issue). Verify `pnpm.overrides` is unchanged after `josh latest`. +1. If the working tree already has staged or modified files, stash them first: `git stash`. Run `git switch main && git pull`, then `josh latest` once (before the first issue) — **mandatory, never skip**. Verify `pnpm.overrides` and `devEngines` are unchanged after `josh latest`. If you stashed changes, restore them: `git stash pop`. 2. For each issue `#<N>` in the supplied order: a. From the 2nd issue onward: run `git switch main && git pull` to incorporate the previous PR's merge. b. Execute the full `fullrun #<N>` flow: normalize title → add `in-progress` label → post plan if body is blank → implement → `pnpm josh bump minor` → `pnpm josh git -y "<title> #<N>"` → run `/review` skill → `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- ..."` (sends per-issue completion notification and merges, exactly as `fullrun` does). @@ -174,7 +176,7 @@ Before every `git commit` — including follow-up commits on the same branch — **Key rules:** - Invoking `queue` is explicit authorization to merge each PR (same as `fullrun`). -- `josh latest` runs only once, before the first issue. +- `josh latest` runs only once, before the first issue. If files were pre-staged when `queue` was invoked, they must be stashed before `josh latest` and restored after. - All `kickoff`/`fullrun` mid-workflow stop rules (confirmation notification, AI review blocker handling, etc.) apply within each issue's execution. #### AI reviewer comment scan (automatic in `pnpm josh followup`) diff --git a/GEMINI.md b/GEMINI.md index f3ca0f6..6e4b832 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -61,6 +61,7 @@ GitHub operations use the `gh` CLI. Authenticate once with `gh auth login`; no a - **NEVER** remove or modify entries in `pnpm.overrides` without explicit user approval. - After running `pnpm update`, `josh latest`, or any dependency-update command, verify that `pnpm.overrides` is unchanged **and** that `devDependencies` versions still respect the overrides. If any entry was removed, modified, or bumped past an override, restore it immediately. +- **NEVER** modify the `devEngines` field in `package.json` without explicit user confirmation. `devEngines` pins the required development toolchain (e.g. pnpm version); silently changing it can break CI or other contributors' environments. After any dependency-update command, verify `devEngines` is unchanged. If it was modified, restore it immediately and ask the user before making any change. ## Code Change Rules @@ -141,6 +142,7 @@ Before every `git commit` — including follow-up commits on the same branch — - **No commits** unless explicitly requested by the user - **No PR merges, branch deletions, force pushes, or other shared-state mutations** unless explicitly requested in the current turn. The default end state is PR still OPEN — do not run `gh pr merge` on your own. **Exception**: invoking `fullrun` or `fullrun new` is explicit authorization to merge; use `pnpm josh followup --merge` in that flow. - For git operations: use `pnpm josh git` +- **Recovery after failed push**: If `pnpm josh git -y` fails at the push step (e.g. pre-push hook blocked), fix the issue, push manually, then run `pnpm josh pr` (or `pnpm josh git -y --skip-commit --skip-push`) to create the PR. **Never** use `gh pr create` directly — it bypasses `closes #N` generation and the Issue will not auto-close. - **Start-of-conversation git status is a stale snapshot.** The `gitStatus` block in the environment preamble is captured once at session start and never refreshes. Before acting on any assumption about working-tree / index / stash / branch state, run `git status` live first. Never report state or propose a plan based on the snapshot alone. ## Collaboration Workflow @@ -156,8 +158,8 @@ Before every `git commit` — including follow-up commits on the same branch — #### `fullrun` — Full execution (plan → implement → PR → completion notify) -- `fullrun #<N>`: Read Issue #N → **normalize the title**: if the title is not in English or can be phrased more clearly/conventionally, derive a better English title and run `gh issue edit <N> --title "<title>"` → **add `in-progress` label** (create if missing: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`) → post the agreed plan only if the Issue body is blank (use `gh issue edit <N> --body "<plan>"`); if the body already has content, skip the plan-posting step → implement → `pnpm josh bump minor` → `pnpm josh git -y` → run `/review` skill → `pnpm josh followup --merge` (full run from Step 3 onward in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`). Issue plan comments MUST be written in English. Before implementing, run `git switch main && git pull`, then `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding (do NOT remove intentional overrides without user approval).** After committing, run the `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean before proceeding to `followup`. When running `pnpm josh followup --merge`, compose an implementation summary in English and pass it via `--notify-message`. Format: `"Implemented <title>:\n- <change1>\n- <change2>\n..."` (one bullet per meaningful change — what was added, changed, or fixed). **`pnpm josh followup --merge` waits for CI, verifies AI review findings, sends the completion notification, then merges — all in one step. If AI review blockers are found, followup exits non-zero; fix the findings and re-run `pnpm josh followup --merge`.** -- `fullrun new` or `fullrun new "<title>"`: Shortcut that combines `kickoff new` + `fullrun #<N>` into a single run. Steps: (1) Derive an English title from the conversation, or use the provided title. (2) Create Issue: `gh issue create --title "<title>" --body "<body>"`. Capture the new Issue number `<N>`. (3) Add `in-progress` label: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`. (4) Post the agreed plan in English. (5) Run `git switch main && git pull`. (6) Run `josh latest`. **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, restore it before proceeding.** (7) Implement. (8) `pnpm josh bump minor`. (9) `pnpm josh git -y "<title> #<N>"`. (10) Run `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean. (11) `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- <change1>\n- <change2>\n..."`. +- `fullrun #<N>`: Read Issue #N → **normalize the title**: if the title is not in English or can be phrased more clearly/conventionally, derive a better English title and run `gh issue edit <N> --title "<title>"` → **add `in-progress` label** (create if missing: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`) → post the agreed plan only if the Issue body is blank (use `gh issue edit <N> --body "<plan>"`); if the body already has content, skip the plan-posting step → implement → `pnpm josh bump minor` → `pnpm josh git -y` → run `/review` skill → `pnpm josh followup --merge` (full run from Step 3 onward in `node_modules/@joshuafolkken/kit/prompts/collaboration-workflow.md`). Issue plan comments MUST be written in English. Before implementing, run `git switch main && git pull`, then `josh latest` (includes `pnpm audit`; fix with `overrides` in `package.json` if vulnerabilities found). **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, investigate why it existed and restore it before proceeding (do NOT remove intentional overrides without user approval). Also verify `devEngines` is unchanged — restore it and ask the user before making any change if it was modified.** After committing, run the `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean before proceeding to `followup`. When running `pnpm josh followup --merge`, compose an implementation summary in English and pass it via `--notify-message`. Format: `"Implemented <title>:\n- <change1>\n- <change2>\n..."` (one bullet per meaningful change — what was added, changed, or fixed). **`pnpm josh followup --merge` waits for CI, verifies AI review findings, sends the completion notification, then merges — all in one step. If AI review blockers are found, followup exits non-zero; fix the findings and re-run `pnpm josh followup --merge`.** +- `fullrun new` or `fullrun new "<title>"`: Shortcut that combines `kickoff new` + `fullrun #<N>` into a single run. Steps: (1) Derive an English title from the conversation, or use the provided title. (2) Create Issue: `gh issue create --title "<title>" --body "<body>"`. Capture the new Issue number `<N>`. (3) Add `in-progress` label: `gh label create "in-progress" --color "#0075ca" --description "Work is actively in progress" 2>/dev/null || true`, then `gh issue edit <N> --add-label "in-progress"`. (4) Post the agreed plan in English. (5) If the working tree already has staged or modified files (e.g., user pre-staged kit/config changes), stash them first: `git stash`. (6) Run `git switch main && git pull`. (7) Run `josh latest` — **mandatory, never skip even if the working tree had modifications**. **After `josh latest`: verify `pnpm.overrides` was not modified — if any override was auto-removed or changed, restore it before proceeding. Also verify `devEngines` is unchanged — restore it and ask the user before making any change if it was modified. If you stashed changes in step 5, restore them now: `git stash pop`.** (8) Implement. (9) `pnpm josh bump minor`. (10) `pnpm josh git -y "<title> #<N>"`. (11) Run `/review` skill on the completed PR diff; fix all high/medium-priority findings and re-run until clean. (12) `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- <change1>\n- <change2>\n..."`. #### `queue` — Sequential multi-issue fullrun @@ -165,7 +167,7 @@ Before every `git commit` — including follow-up commits on the same branch — **Steps:** -1. Run `git switch main && git pull`, then `josh latest` once (before the first issue). Verify `pnpm.overrides` is unchanged after `josh latest`. +1. If the working tree already has staged or modified files, stash them first: `git stash`. Run `git switch main && git pull`, then `josh latest` once (before the first issue) — **mandatory, never skip**. Verify `pnpm.overrides` and `devEngines` are unchanged after `josh latest`. If you stashed changes, restore them: `git stash pop`. 2. For each issue `#<N>` in the supplied order: a. From the 2nd issue onward: run `git switch main && git pull` to incorporate the previous PR's merge. b. Execute the full `fullrun #<N>` flow: normalize title → add `in-progress` label → post plan if body is blank → implement → `pnpm josh bump minor` → `pnpm josh git -y "<title> #<N>"` → run `/review` skill → `pnpm josh followup "<title> #<N>" --merge --notify-message "Implemented <title>:\n- ..."` (sends per-issue completion notification and merges, exactly as `fullrun` does). @@ -175,7 +177,7 @@ Before every `git commit` — including follow-up commits on the same branch — **Key rules:** - Invoking `queue` is explicit authorization to merge each PR (same as `fullrun`). -- `josh latest` runs only once, before the first issue. +- `josh latest` runs only once, before the first issue. If files were pre-staged when `queue` was invoked, they must be stashed before `josh latest` and restored after. - All `kickoff`/`fullrun` mid-workflow stop rules (confirmation notification, AI review blocker handling, etc.) apply within each issue's execution. #### AI reviewer comment scan (automatic in `pnpm josh followup`) diff --git a/package.json b/package.json index e1731a2..8f5dce9 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "gen:pre": "node -e \"const fs=require('node:fs');const d='.svelte-kit/cloudflare';if(fs.existsSync(d))fs.readdirSync(d).forEach(f=>f.startsWith('_worker.')&&fs.rmSync(d+'/'+f,{force:true}))\"", "gen": "pnpm gen:pre && wrangler types", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "postinstall": "lefthook install", + "postinstall": "lefthook install && tsx node_modules/@joshuafolkken/kit/scripts/fix-gh-packages.ts", "josh": "josh", "size-limit": "size-limit" }, @@ -36,7 +36,7 @@ "@eslint/compat": "^2.0.5", "@eslint/js": "^10.0.1", "@ianvs/prettier-plugin-sort-imports": "^4.7.1", - "@joshuafolkken/kit": "0.132.0", + "@joshuafolkken/kit": "0.149.0", "@playwright/test": "^1.59.1", "@size-limit/file": "^12.1.0", "@sveltejs/adapter-cloudflare": "^7.2.8", @@ -82,7 +82,14 @@ "svelte": "^5.0.0", "three": ">=0.180.0" }, - "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8", + "packageManager": "pnpm@11.0.8+sha512.4c4097e1dd2d42372c4e7fa5a791ff28fc75a484c7ac192e64b1df0fdef17594ba982f9b4fed9adfb3c757846f565b799b2763fb3733d1de1bcb82cf46684912", + "devEngines": { + "packageManager": { + "name": "pnpm", + "version": ">=11.0.0-0", + "onFail": "error" + } + }, "size-limit": [ { "path": "./dist/index.js", diff --git a/playwright.config.ts b/playwright.config.ts index bdb1dfa..f48e7af 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,6 +1,56 @@ -import { create_playwright_config } from '@joshuafolkken/kit/playwright/base' +import { defineConfig, devices } from '@playwright/test' -export default create_playwright_config({ - dev_port: 5173, - preview_port: 4173, +const IS_CI = Boolean(process.env['CI']) + +const DEV_PORT = 5173 +const PREVIEW_PORT = 4173 + +const CI_TIMEOUT = 15_000 +const LOCAL_TIMEOUT = 25_000 +const TEST_TIMEOUT = 10_000 +const EXPECT_TIMEOUT = 5_000 +const ACTION_TIMEOUT = 5_000 +const NAVIGATION_TIMEOUT = 10_000 +const CI_WORKERS = 2 +const CI_RETRIES = 2 +const VIEWPORT_WIDTH = 1_280 +const VIEWPORT_HEIGHT = 720 + +const web_server_config = IS_CI + ? { + command: 'pnpm run preview', + port: PREVIEW_PORT, + timeout: CI_TIMEOUT, + reuseExistingServer: false, + } + : { command: 'pnpm run dev', port: DEV_PORT, timeout: LOCAL_TIMEOUT, reuseExistingServer: true } + +export default defineConfig({ + webServer: web_server_config, + testDir: 'e2e', + fullyParallel: true, + ...(IS_CI ? { workers: CI_WORKERS } : {}), + retries: IS_CI ? CI_RETRIES : 0, + timeout: TEST_TIMEOUT, + expect: { timeout: EXPECT_TIMEOUT }, + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }, + launchOptions: { + args: ['--disable-dev-shm-usage', '--disable-gpu', ...(IS_CI ? ['--no-sandbox'] : [])], + }, + }, + }, + ], + reporter: IS_CI ? [['html'], ['github']] : [['html'], ['list']], + use: { + actionTimeout: ACTION_TIMEOUT, + navigationTimeout: NAVIGATION_TIMEOUT, + screenshot: IS_CI ? 'only-on-failure' : 'off', + video: IS_CI ? 'retain-on-failure' : 'off', + trace: IS_CI ? 'retain-on-failure' : 'off', + }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c08fb3..658f0ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - cookie: ^0.7.0 - importers: .: @@ -24,8 +21,8 @@ importers: specifier: ^4.7.1 version: 4.7.1(prettier@3.8.3) '@joshuafolkken/kit': - specifier: 0.132.0 - version: 0.132.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1)) + specifier: 0.149.0 + version: 0.149.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1)) '@playwright/test': specifier: ^1.59.1 version: 1.59.1 @@ -1509,8 +1506,8 @@ packages: resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} engines: {node: '>=18'} - '@joshuafolkken/kit@0.132.0': - resolution: {integrity: sha512-5RVPADY1qfyfMk8XwGitrFUYUWhMmAUD7MMfTzSAGc1DrP2/M1OskVpZSgCvCwD977RU88yDsiUeizZfkaevFQ==, tarball: https://npm.pkg.github.com/download/@joshuafolkken/kit/0.132.0/92af7510b3e33710386fef47b16bb19313f48a95} + '@joshuafolkken/kit@0.149.0': + resolution: {integrity: sha512-UWv3JHVajDxUK1rygpGizmsyDIyC6oJVYRsmsvunv40AAPUpKUdEZ8jQ74SMPCKybN8L4DVjtieumPe/53swPA==, tarball: https://npm.pkg.github.com/download/@joshuafolkken/kit/0.149.0/74b86aa5aa5616b476124c13fcc4a23f7fdf89e3} engines: {node: '>=22.19.0'} hasBin: true peerDependencies: @@ -2249,10 +2246,14 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} @@ -5393,7 +5394,7 @@ snapshots: '@isaacs/cliui@9.0.0': {} - '@joshuafolkken/kit@0.132.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1))': + '@joshuafolkken/kit@0.149.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1))': dependencies: '@playwright/test': 1.59.1 js-yaml: 4.1.1 @@ -5594,7 +5595,7 @@ snapshots: '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) '@types/cookie': 0.6.0 acorn: 8.16.0 - cookie: 0.7.2 + cookie: 0.6.0 devalue: 5.8.0 esm-env: 1.2.2 kleur: 4.1.5 @@ -6138,7 +6139,9 @@ snapshots: convert-source-map@2.0.0: {} - cookie@0.7.2: {} + cookie@0.6.0: {} + + cookie@1.1.1: {} core-js-compat@3.49.0: dependencies: @@ -8133,7 +8136,7 @@ snapshots: '@poppinss/colors': 4.1.6 '@poppinss/dumper': 0.6.5 '@speed-highlight/core': 1.2.15 - cookie: 0.7.2 + cookie: 1.1.1 youch-core: 0.3.3 zimmerframe@1.1.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8dcb096..fae4123 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,10 @@ -onlyBuiltDependencies: - - esbuild - - lefthook - - unrs-resolver +allowBuilds: + esbuild: true + lefthook: true + unrs-resolver: true + '@joshuafolkken/kit': false # project postinstall handles lefthook install + sharp: true + workerd: true # Security fixes often require same-day vite releases; minimum-release-age in .npmrc would block them. minimumReleaseAgeExclude: diff --git a/src/app.html b/src/app.html index bdcb31e..7513437 100644 --- a/src/app.html +++ b/src/app.html @@ -1,6 +1,7 @@ <!doctype html> <html lang="en"> <head> + <title>Game Kit From d2e709b3d2f7802cabd3125dfd3703474ceeec1b Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 21:53:28 +0700 Subject: [PATCH 06/19] Move E2E tests to e2e/ directory to match testDir config #24 Co-Authored-By: Claude Sonnet 4.6 --- .../page.svelte.e2e.ts => e2e/demo/playwright/page.e2e.ts | 0 {src/routes => e2e}/page.e2e.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/routes/demo/playwright/page.svelte.e2e.ts => e2e/demo/playwright/page.e2e.ts (100%) rename {src/routes => e2e}/page.e2e.ts (99%) diff --git a/src/routes/demo/playwright/page.svelte.e2e.ts b/e2e/demo/playwright/page.e2e.ts similarity index 100% rename from src/routes/demo/playwright/page.svelte.e2e.ts rename to e2e/demo/playwright/page.e2e.ts diff --git a/src/routes/page.e2e.ts b/e2e/page.e2e.ts similarity index 99% rename from src/routes/page.e2e.ts rename to e2e/page.e2e.ts index 10e241f..05e8f2f 100644 --- a/src/routes/page.e2e.ts +++ b/e2e/page.e2e.ts @@ -3,7 +3,7 @@ import AxeBuilder from '@axe-core/playwright' import { expect, test, type Page } from '@playwright/test' const { version } = JSON.parse( - readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'), + readFileSync(new URL('../package.json', import.meta.url), 'utf-8'), ) as { version: string } const LOADING_OVERLAY_TIMEOUT_MS = 8000 From ab0a324210328dc3569f71109884e8f9e3ed3c3f Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 21:58:40 +0700 Subject: [PATCH 07/19] Fix playwright testMatch to find .e2e.ts files #24 Co-Authored-By: Claude Sonnet 4.6 --- playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index f48e7af..ab17e13 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -27,7 +27,7 @@ const web_server_config = IS_CI export default defineConfig({ webServer: web_server_config, - testDir: 'e2e', + testMatch: '**/*.e2e.ts', fullyParallel: true, ...(IS_CI ? { workers: CI_WORKERS } : {}), retries: IS_CI ? CI_RETRIES : 0, From 9f64ba0c740237f6278b6e55b8da0fe72e3371f6 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 22:07:00 +0700 Subject: [PATCH 08/19] Fix playwright.config.ts to use build and preview #24 Co-Authored-By: Claude Sonnet 4.6 --- playwright.config.ts | 56 +++----------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index ab17e13..c121c8f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,56 +1,6 @@ -import { defineConfig, devices } from '@playwright/test' - -const IS_CI = Boolean(process.env['CI']) - -const DEV_PORT = 5173 -const PREVIEW_PORT = 4173 - -const CI_TIMEOUT = 15_000 -const LOCAL_TIMEOUT = 25_000 -const TEST_TIMEOUT = 10_000 -const EXPECT_TIMEOUT = 5_000 -const ACTION_TIMEOUT = 5_000 -const NAVIGATION_TIMEOUT = 10_000 -const CI_WORKERS = 2 -const CI_RETRIES = 2 -const VIEWPORT_WIDTH = 1_280 -const VIEWPORT_HEIGHT = 720 - -const web_server_config = IS_CI - ? { - command: 'pnpm run preview', - port: PREVIEW_PORT, - timeout: CI_TIMEOUT, - reuseExistingServer: false, - } - : { command: 'pnpm run dev', port: DEV_PORT, timeout: LOCAL_TIMEOUT, reuseExistingServer: true } +import { defineConfig } from '@playwright/test' export default defineConfig({ - webServer: web_server_config, - testMatch: '**/*.e2e.ts', - fullyParallel: true, - ...(IS_CI ? { workers: CI_WORKERS } : {}), - retries: IS_CI ? CI_RETRIES : 0, - timeout: TEST_TIMEOUT, - expect: { timeout: EXPECT_TIMEOUT }, - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }, - launchOptions: { - args: ['--disable-dev-shm-usage', '--disable-gpu', ...(IS_CI ? ['--no-sandbox'] : [])], - }, - }, - }, - ], - reporter: IS_CI ? [['html'], ['github']] : [['html'], ['list']], - use: { - actionTimeout: ACTION_TIMEOUT, - navigationTimeout: NAVIGATION_TIMEOUT, - screenshot: IS_CI ? 'only-on-failure' : 'off', - video: IS_CI ? 'retain-on-failure' : 'off', - trace: IS_CI ? 'retain-on-failure' : 'off', - }, + webServer: { command: 'npm run build && npm run preview', port: 4173 }, + testMatch: '**/*.e2e.{ts,js}', }) From 0e4dc81f2319e41ce255e2537cd2239b3ed6dd18 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 22:09:36 +0700 Subject: [PATCH 09/19] Fix playwright webServer to use pnpm run #24 Co-Authored-By: Claude Sonnet 4.6 --- playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index c121c8f..5597612 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from '@playwright/test' export default defineConfig({ - webServer: { command: 'npm run build && npm run preview', port: 4173 }, + webServer: { command: 'pnpm run build && pnpm run preview', port: 4173 }, testMatch: '**/*.e2e.{ts,js}', }) From d740cb1da672c3fe87e40ef5555f71edc3e32461 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 22:25:07 +0700 Subject: [PATCH 10/19] Add workers 1 to playwright config to prevent SQLite lock #24 Co-Authored-By: Claude Sonnet 4.6 --- playwright.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/playwright.config.ts b/playwright.config.ts index 5597612..2c13793 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -3,4 +3,5 @@ import { defineConfig } from '@playwright/test' export default defineConfig({ webServer: { command: 'pnpm run build && pnpm run preview', port: 4173 }, testMatch: '**/*.e2e.{ts,js}', + workers: 1, }) From 351903b46adf1990465d16c23d75f0825d3f309e Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 22:39:15 +0700 Subject: [PATCH 11/19] Fix security vulnerabilities by updating vite-plugin-pwa to 1.3.0 #24 Adds serialize-javascript override to resolve high and moderate CVEs via vite-plugin-pwa 1.3.0 which uses workbox-build 7.4.1 and @rollup/plugin-terser 1.0.0 with serialize-javascript ^7.0.3. Co-Authored-By: Claude Sonnet 4.6 --- package.json | 5 ++-- pnpm-lock.yaml | 76 +++++++++++++++++++++++++++----------------------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 8f5dce9..76d2479 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "typescript": "^6.0.3", "typescript-eslint": "^8.59.1", "vite": "^8.0.10", - "vite-plugin-pwa": "^1.2.0", + "vite-plugin-pwa": "^1.3.0", "vitest": "^4.1.5", "vitest-browser-svelte": "^2.1.1", "wrangler": "^4.87.0" @@ -98,7 +98,8 @@ ], "pnpm": { "overrides": { - "cookie": "^0.7.0" + "cookie": "^0.7.0", + "serialize-javascript": ">=7.0.5" }, "onlyBuiltDependencies": [ "@joshuafolkken/kit", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 658f0ea..85b51b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -126,8 +126,8 @@ importers: specifier: ^8.0.10 version: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) vite-plugin-pwa: - specifier: ^1.2.0 - version: 1.2.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + specifier: ^1.3.0 + version: 1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) vitest: specifier: ^4.1.5 version: 4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) @@ -469,8 +469,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.29.0': - resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + '@babel/plugin-transform-modules-systemjs@7.29.4': + resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -625,8 +625,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.29.3': - resolution: {integrity: sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==} + '@babel/preset-env@7.29.5': + resolution: {integrity: sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1513,6 +1513,11 @@ packages: peerDependencies: '@playwright/test': '>=1.0.0' svelte: '>=5.0.0' + peerDependenciesMeta: + '@playwright/test': + optional: true + svelte: + optional: true '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -2197,8 +2202,8 @@ packages: peerDependencies: three: '>=0.126.1' - caniuse-lite@1.0.30001791: - resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2384,8 +2389,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.349: - resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==} + electron-to-chromium@1.5.351: + resolution: {integrity: sha512-9D7Iqx8RImSvCnOsj86rCH6eQjZFQoM04Jn6HnZVM0Nu/G58/gmKYQ1d12MZTbjQbQSTGI8nwEy07ErsA2slLA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2558,8 +2563,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -3057,8 +3062,8 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - lru-cache@11.3.5: - resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + lru-cache@11.3.6: + resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -3827,14 +3832,14 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-plugin-pwa@1.2.0: - resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==} + 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 - workbox-build: ^7.4.0 - workbox-window: ^7.4.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 @@ -4528,7 +4533,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) @@ -4693,7 +4698,7 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.29.3(@babel/core@7.29.0)': + '@babel/preset-env@7.29.5(@babel/core@7.29.0)': dependencies: '@babel/compat-data': 7.29.3 '@babel/core': 7.29.0 @@ -4735,7 +4740,7 @@ snapshots: '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) @@ -5396,12 +5401,13 @@ snapshots: '@joshuafolkken/kit@0.149.0(@playwright/test@1.59.1)(svelte@5.55.5(@typescript-eslint/types@8.59.1))': dependencies: - '@playwright/test': 1.59.1 js-yaml: 4.1.1 strip-json-comments: 5.0.3 - svelte: 5.55.5(@typescript-eslint/types@8.59.1) tsx: 4.21.0 zod: 4.4.3 + optionalDependencies: + '@playwright/test': 1.59.1 + svelte: 5.55.5(@typescript-eslint/types@8.59.1) '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -5880,7 +5886,7 @@ snapshots: '@sveltejs/kit': 2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) kolorist: 1.8.0 tinyglobby: 0.2.16 - vite-plugin-pwa: 1.2.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + vite-plugin-pwa: 1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) transitivePeerDependencies: - supports-color - vite @@ -5974,7 +5980,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 + fast-uri: 3.1.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -6066,8 +6072,8 @@ snapshots: browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.27 - caniuse-lite: 1.0.30001791 - electron-to-chromium: 1.5.349 + caniuse-lite: 1.0.30001792 + electron-to-chromium: 1.5.351 node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -6100,7 +6106,7 @@ snapshots: dependencies: three: 0.184.0 - caniuse-lite@1.0.30001791: {} + caniuse-lite@1.0.30001792: {} chai@6.2.2: {} @@ -6314,7 +6320,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.349: {} + electron-to-chromium@1.5.351: {} emoji-regex@10.6.0: {} @@ -6600,7 +6606,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.0: {} + fast-uri@3.1.2: {} fdir@6.5.0(picomatch@4.0.4): optionalDependencies: @@ -7029,7 +7035,7 @@ snapshots: lodash@4.18.1: {} - lru-cache@11.3.5: {} + lru-cache@11.3.6: {} lru-cache@5.1.1: dependencies: @@ -7151,7 +7157,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.3.5 + lru-cache: 11.3.6 minipass: 7.1.3 path-to-regexp@6.3.0: {} @@ -7814,7 +7820,7 @@ snapshots: util-deprecate@1.0.2: {} - vite-plugin-pwa@1.2.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0): + vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 @@ -7958,7 +7964,7 @@ snapshots: dependencies: '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) '@babel/core': 7.29.0 - '@babel/preset-env': 7.29.3(@babel/core@7.29.0) + '@babel/preset-env': 7.29.5(@babel/core@7.29.0) '@babel/runtime': 7.29.2 '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(rollup@2.80.0) '@rollup/plugin-node-resolve': 15.3.1(rollup@2.80.0) From 1a503a87856871a98016a8d5341354ace712b8fa Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Thu, 7 May 2026 23:06:18 +0700 Subject: [PATCH 12/19] Fix security vulnerabilities by pinning safe dependency versions #24 Co-Authored-By: Claude Sonnet 4.6 --- package.json | 3 + pnpm-lock.yaml | 630 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 420 insertions(+), 213 deletions(-) diff --git a/package.json b/package.json index 76d2479..29fe58b 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@types/three": "^0.184.0", "@vite-pwa/sveltekit": "^1.1.0", "@vitest/browser-playwright": "^4.1.5", + "cookie": "^0.7.2", "cspell": "^10.0.0", "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", @@ -74,6 +75,8 @@ "vite-plugin-pwa": "^1.3.0", "vitest": "^4.1.5", "vitest-browser-svelte": "^2.1.1", + "workbox-build": "^7.4.1", + "workbox-window": "^7.4.1", "wrangler": "^4.87.0" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85b51b8..2cabf12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,10 +64,13 @@ importers: version: 0.184.0 '@vite-pwa/sveltekit': specifier: ^1.1.0 - version: 1.1.0(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + version: 1.1.0(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.1)(workbox-window@7.4.1) '@vitest/browser-playwright': specifier: ^4.1.5 version: 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) + cookie: + specifier: ^0.7.2 + version: 0.7.2 cspell: specifier: ^10.0.0 version: 10.0.0 @@ -100,7 +103,7 @@ importers: version: 0.3.18 rollup-plugin-visualizer: specifier: ^7.0.1 - version: 7.0.1(rolldown@1.0.0-rc.17)(rollup@2.80.0) + version: 7.0.1(rolldown@1.0.0-rc.17)(rollup@4.60.3) size-limit: specifier: ^12.1.0 version: 12.1.0(jiti@2.6.1) @@ -127,13 +130,19 @@ importers: version: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) vite-plugin-pwa: specifier: ^1.3.0 - version: 1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + version: 1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.1)(workbox-window@7.4.1) vitest: specifier: ^4.1.5 version: 4.1.5(@types/node@25.6.0)(@vitest/browser-playwright@4.1.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) vitest-browser-svelte: specifier: ^2.1.1 version: 2.1.1(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vitest@4.1.5) + workbox-build: + specifier: ^7.4.1 + version: 7.4.1 + workbox-window: + specifier: ^7.4.1 + version: 7.4.1 wrangler: specifier: ^4.87.0 version: 4.87.0(@cloudflare/workers-types@4.20260504.1) @@ -1669,19 +1678,21 @@ packages: '@rolldown/pluginutils@1.0.0-rc.17': resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} - '@rollup/plugin-babel@5.3.1': - resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} - engines: {node: '>= 10.0.0'} + '@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 + 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@15.3.1': - resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} + '@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 @@ -1689,25 +1700,23 @@ packages: rollup: optional: true - '@rollup/plugin-replace@2.4.2': - resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} - peerDependencies: - rollup: ^1.20.0 || ^2.0.0 - - '@rollup/plugin-terser@0.4.4': - resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + '@rollup/plugin-replace@6.0.3': + resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^2.0.0||^3.0.0||^4.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - '@rollup/pluginutils@3.1.0': - resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} - engines: {node: '>= 8.0.0'} + '@rollup/plugin-terser@1.0.0': + resolution: {integrity: sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==} + engines: {node: '>=20.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0 + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} @@ -1718,6 +1727,144 @@ packages: rollup: optional: true + '@rollup/rollup-android-arm-eabi@4.60.3': + resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.3': + resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.3': + resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.3': + resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.3': + resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.3': + resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.3': + resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.3': + resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.3': + resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.3': + resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.3': + resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.3': + resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.3': + resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.3': + resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} + cpu: [x64] + os: [win32] + '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} @@ -1734,9 +1881,6 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@surma/rollup-plugin-off-main-thread@2.2.3': - resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - '@sveltejs/acorn-typescript@1.0.9': resolution: {integrity: sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==} peerDependencies: @@ -1905,6 +2049,10 @@ packages: svelte: '>=5' three: '>=0.160' + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} + engines: {node: '>=12'} + '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} @@ -1923,9 +2071,6 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@0.0.39': - resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2251,8 +2396,8 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} cookie@1.1.1: @@ -2533,9 +2678,6 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - estree-walker@1.0.1: - resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -2546,6 +2688,10 @@ packages: 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'} @@ -3059,9 +3205,6 @@ packages: lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - lru-cache@11.3.6: resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} engines: {node: 20 || >=22} @@ -3075,9 +3218,6 @@ packages: '@types/three': '>=0.134.0' three: '>=0.134.0' - magic-string@0.25.9: - resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -3199,10 +3339,6 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -3352,9 +3488,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3427,9 +3560,9 @@ packages: rollup: optional: true - rollup@2.80.0: - resolution: {integrity: sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==} - engines: {node: '>=10.0.0'} + rollup@4.60.3: + resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-applescript@7.1.0: @@ -3444,9 +3577,6 @@ packages: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -3467,8 +3597,9 @@ packages: engines: {node: '>=10'} hasBin: true - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serialize-javascript@7.0.5: + resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + engines: {node: '>=20.0.0'} set-cookie-parser@3.1.0: resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} @@ -3562,10 +3693,6 @@ packages: engines: {node: '>= 8'} deprecated: The work that was done in this beta branch won't be included in future versions - sourcemap-codec@1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -3987,54 +4114,54 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workbox-background-sync@7.4.0: - resolution: {integrity: sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w==} + workbox-background-sync@7.4.1: + resolution: {integrity: sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==} - workbox-broadcast-update@7.4.0: - resolution: {integrity: sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA==} + workbox-broadcast-update@7.4.1: + resolution: {integrity: sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==} - workbox-build@7.4.0: - resolution: {integrity: sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA==} + workbox-build@7.4.1: + resolution: {integrity: sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==} engines: {node: '>=20.0.0'} - workbox-cacheable-response@7.4.0: - resolution: {integrity: sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ==} + workbox-cacheable-response@7.4.1: + resolution: {integrity: sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==} - workbox-core@7.4.0: - resolution: {integrity: sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==} + workbox-core@7.4.1: + resolution: {integrity: sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==} - workbox-expiration@7.4.0: - resolution: {integrity: sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw==} + workbox-expiration@7.4.1: + resolution: {integrity: sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==} - workbox-google-analytics@7.4.0: - resolution: {integrity: sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ==} + workbox-google-analytics@7.4.1: + resolution: {integrity: sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==} - workbox-navigation-preload@7.4.0: - resolution: {integrity: sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==} + workbox-navigation-preload@7.4.1: + resolution: {integrity: sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==} - workbox-precaching@7.4.0: - resolution: {integrity: sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==} + workbox-precaching@7.4.1: + resolution: {integrity: sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==} - workbox-range-requests@7.4.0: - resolution: {integrity: sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw==} + workbox-range-requests@7.4.1: + resolution: {integrity: sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==} - workbox-recipes@7.4.0: - resolution: {integrity: sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==} + workbox-recipes@7.4.1: + resolution: {integrity: sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==} - workbox-routing@7.4.0: - resolution: {integrity: sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==} + workbox-routing@7.4.1: + resolution: {integrity: sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==} - workbox-strategies@7.4.0: - resolution: {integrity: sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==} + workbox-strategies@7.4.1: + resolution: {integrity: sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==} - workbox-streams@7.4.0: - resolution: {integrity: sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg==} + workbox-streams@7.4.1: + resolution: {integrity: sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==} - workbox-sw@7.4.0: - resolution: {integrity: sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw==} + workbox-sw@7.4.1: + resolution: {integrity: sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==} - workbox-window@7.4.0: - resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==} + workbox-window@7.4.1: + resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} workerd@1.20260430.1: resolution: {integrity: sha512-KEgIWyiw3Jmn+DCd/L3ePo5fmiiYb/UcwKvDWPf/nLLOiwShDFzDSsegU5NY/JcwgvO/QsLHVi2FYrbkcXNY5Q==} @@ -5518,53 +5645,123 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.17': {} - '@rollup/plugin-babel@5.3.1(@babel/core@7.29.0)(rollup@2.80.0)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.0)(rollup@4.60.3)': dependencies: '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.28.6 - '@rollup/pluginutils': 3.1.0(rollup@2.80.0) - rollup: 2.80.0 + '@rollup/pluginutils': 5.3.0(rollup@4.60.3) + optionalDependencies: + rollup: 4.60.3 transitivePeerDependencies: - supports-color - '@rollup/plugin-node-resolve@15.3.1(rollup@2.80.0)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.60.3)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@2.80.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.3) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.12 optionalDependencies: - rollup: 2.80.0 + rollup: 4.60.3 - '@rollup/plugin-replace@2.4.2(rollup@2.80.0)': + '@rollup/plugin-replace@6.0.3(rollup@4.60.3)': dependencies: - '@rollup/pluginutils': 3.1.0(rollup@2.80.0) - magic-string: 0.25.9 - rollup: 2.80.0 + '@rollup/pluginutils': 5.3.0(rollup@4.60.3) + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.60.3 - '@rollup/plugin-terser@0.4.4(rollup@2.80.0)': + '@rollup/plugin-terser@1.0.0(rollup@4.60.3)': dependencies: - serialize-javascript: 6.0.2 + serialize-javascript: 7.0.5 smob: 1.6.1 terser: 5.46.2 optionalDependencies: - rollup: 2.80.0 - - '@rollup/pluginutils@3.1.0(rollup@2.80.0)': - dependencies: - '@types/estree': 0.0.39 - estree-walker: 1.0.1 - picomatch: 2.3.2 - rollup: 2.80.0 + rollup: 4.60.3 - '@rollup/pluginutils@5.3.0(rollup@2.80.0)': + '@rollup/pluginutils@5.3.0(rollup@4.60.3)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.4 optionalDependencies: - rollup: 2.80.0 + rollup: 4.60.3 + + '@rollup/rollup-android-arm-eabi@4.60.3': + optional: true + + '@rollup/rollup-android-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-x64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.3': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.3': + optional: true '@sindresorhus/is@7.2.0': {} @@ -5576,13 +5773,6 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@surma/rollup-plugin-off-main-thread@2.2.3': - dependencies: - ejs: 3.1.10 - json5: 2.2.3 - magic-string: 0.25.9 - string.prototype.matchall: 4.0.12 - '@sveltejs/acorn-typescript@1.0.9(acorn@8.16.0)': dependencies: acorn: 8.16.0 @@ -5601,7 +5791,7 @@ snapshots: '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) '@types/cookie': 0.6.0 acorn: 8.16.0 - cookie: 0.6.0 + cookie: 0.7.2 devalue: 5.8.0 esm-env: 1.2.2 kleur: 4.1.5 @@ -5745,6 +5935,13 @@ snapshots: transitivePeerDependencies: - '@types/three' + '@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 + '@tweenjs/tween.js@23.1.3': {} '@tybys/wasm-util@0.10.1': @@ -5763,8 +5960,6 @@ snapshots: '@types/esrecurse@4.3.1': {} - '@types/estree@0.0.39': {} - '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -5881,12 +6076,12 @@ snapshots: '@typescript-eslint/types': 8.59.1 eslint-visitor-keys: 5.0.1 - '@vite-pwa/sveltekit@1.1.0(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0)': + '@vite-pwa/sveltekit@1.1.0(@sveltejs/kit@2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.1)(workbox-window@7.4.1)': dependencies: '@sveltejs/kit': 2.59.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5(@typescript-eslint/types@8.59.1))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.5(@typescript-eslint/types@8.59.1))(typescript@6.0.3)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) kolorist: 1.8.0 tinyglobby: 0.2.16 - vite-plugin-pwa: 1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0) + vite-plugin-pwa: 1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.1)(workbox-window@7.4.1) transitivePeerDependencies: - supports-color - vite @@ -6145,7 +6340,7 @@ snapshots: convert-source-map@2.0.0: {} - cookie@0.6.0: {} + cookie@0.7.2: {} cookie@1.1.1: {} @@ -6586,8 +6781,6 @@ snapshots: estraverse@5.3.0: {} - estree-walker@1.0.1: {} - estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -6596,6 +6789,8 @@ snapshots: esutils@2.0.3: {} + eta@4.6.0: {} + expect-type@1.3.0: {} fast-deep-equal@3.1.3: {} @@ -7033,8 +7228,6 @@ snapshots: lodash.sortby@4.7.0: {} - lodash@4.18.1: {} - lru-cache@11.3.6: {} lru-cache@5.1.1: @@ -7046,10 +7239,6 @@ snapshots: '@types/three': 0.184.0 three: 0.184.0 - magic-string@0.25.9: - dependencies: - sourcemap-codec: 1.4.8 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -7166,8 +7355,6 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.4: {} playwright-core@1.59.1: {} @@ -7244,10 +7431,6 @@ snapshots: punycode@2.3.1: {} - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - readdirp@4.1.2: {} readdirp@5.0.0: {} @@ -7329,7 +7512,7 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 - rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.17)(rollup@2.80.0): + rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.17)(rollup@4.60.3): dependencies: open: 11.0.0 picomatch: 4.0.4 @@ -7337,10 +7520,37 @@ snapshots: yargs: 18.0.0 optionalDependencies: rolldown: 1.0.0-rc.17 - rollup: 2.80.0 + rollup: 4.60.3 - rollup@2.80.0: + rollup@4.60.3: + dependencies: + '@types/estree': 1.0.8 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.3 + '@rollup/rollup-android-arm64': 4.60.3 + '@rollup/rollup-darwin-arm64': 4.60.3 + '@rollup/rollup-darwin-x64': 4.60.3 + '@rollup/rollup-freebsd-arm64': 4.60.3 + '@rollup/rollup-freebsd-x64': 4.60.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 + '@rollup/rollup-linux-arm-musleabihf': 4.60.3 + '@rollup/rollup-linux-arm64-gnu': 4.60.3 + '@rollup/rollup-linux-arm64-musl': 4.60.3 + '@rollup/rollup-linux-loong64-gnu': 4.60.3 + '@rollup/rollup-linux-loong64-musl': 4.60.3 + '@rollup/rollup-linux-ppc64-gnu': 4.60.3 + '@rollup/rollup-linux-ppc64-musl': 4.60.3 + '@rollup/rollup-linux-riscv64-gnu': 4.60.3 + '@rollup/rollup-linux-riscv64-musl': 4.60.3 + '@rollup/rollup-linux-s390x-gnu': 4.60.3 + '@rollup/rollup-linux-x64-gnu': 4.60.3 + '@rollup/rollup-linux-x64-musl': 4.60.3 + '@rollup/rollup-openbsd-x64': 4.60.3 + '@rollup/rollup-openharmony-arm64': 4.60.3 + '@rollup/rollup-win32-arm64-msvc': 4.60.3 + '@rollup/rollup-win32-ia32-msvc': 4.60.3 + '@rollup/rollup-win32-x64-gnu': 4.60.3 + '@rollup/rollup-win32-x64-msvc': 4.60.3 fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -7357,8 +7567,6 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 - safe-buffer@5.2.1: {} - safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -7376,9 +7584,7 @@ snapshots: semver@7.7.4: {} - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 + serialize-javascript@7.0.5: {} set-cookie-parser@3.1.0: {} @@ -7508,8 +7714,6 @@ snapshots: dependencies: whatwg-url: 7.1.0 - sourcemap-codec@1.4.8: {} - stackback@0.0.2: {} std-env@4.1.0: {} @@ -7820,14 +8024,14 @@ snapshots: util-deprecate@1.0.2: {} - vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.0)(workbox-window@7.4.0): + vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(workbox-build@7.4.1)(workbox-window@7.4.1): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.16 vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) - workbox-build: 7.4.0 - workbox-window: 7.4.0 + workbox-build: 7.4.1 + workbox-window: 7.4.1 transitivePeerDependencies: - supports-color @@ -7951,118 +8155,118 @@ snapshots: word-wrap@1.2.5: {} - workbox-background-sync@7.4.0: + workbox-background-sync@7.4.1: dependencies: idb: 7.1.1 - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-broadcast-update@7.4.0: + workbox-broadcast-update@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-build@7.4.0: + workbox-build@7.4.1: dependencies: '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) '@babel/core': 7.29.0 '@babel/preset-env': 7.29.5(@babel/core@7.29.0) '@babel/runtime': 7.29.2 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(rollup@2.80.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@2.80.0) - '@rollup/plugin-replace': 2.4.2(rollup@2.80.0) - '@rollup/plugin-terser': 0.4.4(rollup@2.80.0) - '@surma/rollup-plugin-off-main-thread': 2.2.3 + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.0)(rollup@4.60.3) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.3) + '@rollup/plugin-replace': 6.0.3(rollup@4.60.3) + '@rollup/plugin-terser': 1.0.0(rollup@4.60.3) + '@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 - lodash: 4.18.1 pretty-bytes: 5.6.0 - rollup: 2.80.0 + rollup: 4.60.3 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.0 - workbox-broadcast-update: 7.4.0 - workbox-cacheable-response: 7.4.0 - workbox-core: 7.4.0 - workbox-expiration: 7.4.0 - workbox-google-analytics: 7.4.0 - workbox-navigation-preload: 7.4.0 - workbox-precaching: 7.4.0 - workbox-range-requests: 7.4.0 - workbox-recipes: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 - workbox-streams: 7.4.0 - workbox-sw: 7.4.0 - workbox-window: 7.4.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.0: + workbox-cacheable-response@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-core@7.4.0: {} + workbox-core@7.4.1: {} - workbox-expiration@7.4.0: + workbox-expiration@7.4.1: dependencies: idb: 7.1.1 - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-google-analytics@7.4.0: + workbox-google-analytics@7.4.1: dependencies: - workbox-background-sync: 7.4.0 - workbox-core: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 + 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.0: + workbox-navigation-preload@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-precaching@7.4.0: + workbox-precaching@7.4.1: dependencies: - workbox-core: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 - workbox-range-requests@7.4.0: + workbox-range-requests@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-recipes@7.4.0: + workbox-recipes@7.4.1: dependencies: - workbox-cacheable-response: 7.4.0 - workbox-core: 7.4.0 - workbox-expiration: 7.4.0 - workbox-precaching: 7.4.0 - workbox-routing: 7.4.0 - workbox-strategies: 7.4.0 + 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.0: + workbox-routing@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-strategies@7.4.0: + workbox-strategies@7.4.1: dependencies: - workbox-core: 7.4.0 + workbox-core: 7.4.1 - workbox-streams@7.4.0: + workbox-streams@7.4.1: dependencies: - workbox-core: 7.4.0 - workbox-routing: 7.4.0 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 - workbox-sw@7.4.0: {} + workbox-sw@7.4.1: {} - workbox-window@7.4.0: + workbox-window@7.4.1: dependencies: '@types/trusted-types': 2.0.7 - workbox-core: 7.4.0 + workbox-core: 7.4.1 workerd@1.20260430.1: optionalDependencies: From 6e4ac9a66a7260191527238de6ee8dfa7aaffc29 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 02:54:44 +0700 Subject: [PATCH 13/19] Use production build for pre-push E2E tests (Vite 8 dev server CSS regression) #24 Co-Authored-By: Claude Sonnet 4.6 --- lefthook.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lefthook.yml b/lefthook.yml index 058fdda..476fa21 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,2 +1,8 @@ extends: - node_modules/@joshuafolkken/kit/lefthook/sveltekit.yml + +pre-push: + commands: + test-e2e: + glob: '{*.{svelte,ts,js,mjs,cjs},package.json}' + run: pnpm build && CI=1 PLAYWRIGHT_HTML_OPEN=never pnpm exec playwright test From 685f0c1f2c7881b37fbb651a1d806fba9c58de38 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 03:24:10 +0700 Subject: [PATCH 14/19] Switch pre-push E2E to PLAYWRIGHT_PREVIEW var to reliably trigger preview mode #24 Co-Authored-By: Claude Sonnet 4.6 --- lefthook.yml | 2 +- playwright.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 476fa21..1a45a18 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -5,4 +5,4 @@ pre-push: commands: test-e2e: glob: '{*.{svelte,ts,js,mjs,cjs},package.json}' - run: pnpm build && CI=1 PLAYWRIGHT_HTML_OPEN=never pnpm exec playwright test + run: pnpm build && PLAYWRIGHT_PREVIEW=1 PLAYWRIGHT_HTML_OPEN=never pnpm exec playwright test diff --git a/playwright.config.ts b/playwright.config.ts index 5ca5a39..3cac690 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,6 +1,6 @@ import { defineConfig, devices, type ReporterDescription } from '@playwright/test' -const IS_CI = Boolean(process.env['CI']) +const IS_CI = Boolean(process.env['CI']) || process.env['PLAYWRIGHT_PREVIEW'] === '1' const DEV_PORT = 5173 const PREVIEW_PORT = 4173 From 84b6764bd3be9becfa6b82f8bb4ab92d79011768 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 03:26:01 +0700 Subject: [PATCH 15/19] Use lefthook env: key for PLAYWRIGHT_PREVIEW to correctly set env #24 Co-Authored-By: Claude Sonnet 4.6 --- lefthook.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lefthook.yml b/lefthook.yml index 1a45a18..e7d7fc4 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -5,4 +5,7 @@ pre-push: commands: test-e2e: glob: '{*.{svelte,ts,js,mjs,cjs},package.json}' - run: pnpm build && PLAYWRIGHT_PREVIEW=1 PLAYWRIGHT_HTML_OPEN=never pnpm exec playwright test + env: + PLAYWRIGHT_PREVIEW: '1' + PLAYWRIGHT_HTML_OPEN: never + run: pnpm build && pnpm exec playwright test From b2ba2fb2374535671310d382d2fb6059badeb922 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 03:28:21 +0700 Subject: [PATCH 16/19] Clean wrangler state before pre-push E2E to avoid SQLite locking #24 Co-Authored-By: Claude Sonnet 4.6 --- lefthook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lefthook.yml b/lefthook.yml index e7d7fc4..dd80262 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -8,4 +8,4 @@ pre-push: env: PLAYWRIGHT_PREVIEW: '1' PLAYWRIGHT_HTML_OPEN: never - run: pnpm build && pnpm exec playwright test + run: rm -rf .wrangler/state && pnpm build && pnpm exec playwright test From e6f8ca2d69de341ab4389bd41631afe5610bfc19 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 03:33:08 +0700 Subject: [PATCH 17/19] Fix pre-push E2E hook to use --workers=1 for preview server stability Co-Authored-By: Claude Sonnet 4.6 --- lefthook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lefthook.yml b/lefthook.yml index dd80262..8d34312 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -8,4 +8,4 @@ pre-push: env: PLAYWRIGHT_PREVIEW: '1' PLAYWRIGHT_HTML_OPEN: never - run: rm -rf .wrangler/state && pnpm build && pnpm exec playwright test + run: rm -rf .wrangler/state && pnpm build && pnpm exec playwright test --workers=1 From 49dff58c5d349e0fe797861afee070ab7e95b8d6 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 03:37:35 +0700 Subject: [PATCH 18/19] Fix preview mode E2E to run with single worker via config Sets PREVIEW_WORKERS=1 in playwright.config.ts when PLAYWRIGHT_PREVIEW=1 so workerd SQLite lock conflicts are avoided during pre-push hook. Co-Authored-By: Claude Sonnet 4.6 --- lefthook.yml | 2 +- playwright.config.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 8d34312..dd80262 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -8,4 +8,4 @@ pre-push: env: PLAYWRIGHT_PREVIEW: '1' PLAYWRIGHT_HTML_OPEN: never - run: rm -rf .wrangler/state && pnpm build && pnpm exec playwright test --workers=1 + run: rm -rf .wrangler/state && pnpm build && pnpm exec playwright test diff --git a/playwright.config.ts b/playwright.config.ts index 3cac690..976f8d7 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,6 +1,7 @@ import { defineConfig, devices, type ReporterDescription } from '@playwright/test' const IS_CI = Boolean(process.env['CI']) || process.env['PLAYWRIGHT_PREVIEW'] === '1' +const IS_PREVIEW = process.env['PLAYWRIGHT_PREVIEW'] === '1' const DEV_PORT = 5173 const PREVIEW_PORT = 4173 @@ -11,6 +12,7 @@ const CI_TEST_TIMEOUT = 30_000 const ACTION_TIMEOUT = 10_000 const NAV_TIMEOUT = 30_000 const CI_WORKERS = 2 +const PREVIEW_WORKERS = 1 const CI_RETRIES = 2 type EnvConfig = { @@ -56,7 +58,7 @@ export default defineConfig({ webServer: web_server_config, testMatch: '**/*.e2e.{ts,js}', fullyParallel: true, - ...(IS_CI ? { workers: CI_WORKERS } : {}), + ...(IS_CI ? { workers: IS_PREVIEW ? PREVIEW_WORKERS : CI_WORKERS } : {}), retries: env_config.retries, timeout: env_config.timeout, projects: [ From 96cfcd82f41755ab1613e19e6c0f33bb52062148 Mon Sep 17 00:00:00 2001 From: joshuafolkken Date: Sun, 10 May 2026 03:43:06 +0700 Subject: [PATCH 19/19] Convert arrow function methods to method shorthand per project convention Co-Authored-By: Claude Sonnet 4.6 --- src/lib/simon/score.svelte.ts | 9 ++++++--- src/lib/simon/simon.svelte.ts | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/lib/simon/score.svelte.ts b/src/lib/simon/score.svelte.ts index 9ee02f6..6accf88 100644 --- a/src/lib/simon/score.svelte.ts +++ b/src/lib/simon/score.svelte.ts @@ -100,9 +100,12 @@ function make_score_api(s: ScoreState, keys: StorageKeys) { get last_cleared_round(): number { return s.last_cleared_round }, - add_round_score: (elapsed_ms: number, sequence_length: number, round: number): void => - add_round_score_impl(s, { elapsed_ms, sequence_length, round }, keys), - reset: (): void => reset_score_impl(s), + add_round_score(elapsed_ms: number, sequence_length: number, round: number): void { + add_round_score_impl(s, { elapsed_ms, sequence_length, round }, keys) + }, + reset(): void { + reset_score_impl(s) + }, format_score, calculate_time_coefficient, calculate_round_score, diff --git a/src/lib/simon/simon.svelte.ts b/src/lib/simon/simon.svelte.ts index dbc02ab..6a8a4e3 100644 --- a/src/lib/simon/simon.svelte.ts +++ b/src/lib/simon/simon.svelte.ts @@ -190,10 +190,18 @@ function make_simon_api( get flash_intensity() { return s.flash_intensity }, - start: (): void => start_simon(s, t, score, colors), - press: (color: ButtonColor): void => press_simon(s, color), - release: (): void => release_simon(s, t, score, colors), - reset: (): void => reset_simon(s, t, score), + start(): void { + start_simon(s, t, score, colors) + }, + press(color: ButtonColor): void { + press_simon(s, color) + }, + release(): void { + release_simon(s, t, score, colors) + }, + reset(): void { + reset_simon(s, t, score) + }, } }