From ba16f17d8ada0664be3bcf3652ee0f99db7a41e1 Mon Sep 17 00:00:00 2001 From: hypercross Date: Sun, 9 Aug 2026 19:07:14 +0800 Subject: [PATCH] feat(bgm): add vite plugin and build integration test Move the bgm vite plugin into the package so it can be tested in isolation from the web app. Serve each package as a JSON module, serializing its maps, and verify resolution through a real vite build against a self-contained fixture. --- packages/bgm/package.json | 1 + .../vite-build/games/harbor/harbor.md | 57 +++++++++ .../src/__fixtures__/vite-build/index.html | 10 ++ .../src/__fixtures__/vite-build/src/main.ts | 9 ++ packages/bgm/src/collect.test.ts | 2 +- packages/bgm/src/index.ts | 3 +- packages/bgm/src/vite.test.ts | 59 +++++++++ packages/bgm/src/vite.ts | 64 ++++++++++ packages/bgm/src/vite.unit.test.ts | 49 ++++++++ packages/bgm/tsconfig.json | 3 +- pnpm-lock.yaml | 118 +++++++++++++++--- 11 files changed, 357 insertions(+), 18 deletions(-) create mode 100644 packages/bgm/src/__fixtures__/vite-build/games/harbor/harbor.md create mode 100644 packages/bgm/src/__fixtures__/vite-build/index.html create mode 100644 packages/bgm/src/__fixtures__/vite-build/src/main.ts create mode 100644 packages/bgm/src/vite.test.ts create mode 100644 packages/bgm/src/vite.ts create mode 100644 packages/bgm/src/vite.unit.test.ts diff --git a/packages/bgm/package.json b/packages/bgm/package.json index 7f80927..7c6003d 100644 --- a/packages/bgm/package.json +++ b/packages/bgm/package.json @@ -18,6 +18,7 @@ "lint": "echo \"no lint configured\"" }, "dependencies": { + "vite": "^8.2.1", "marked": "^16.0.0", "picomatch": "^4.0.5", "smol-toml": "^1.4.0", diff --git a/packages/bgm/src/__fixtures__/vite-build/games/harbor/harbor.md b/packages/bgm/src/__fixtures__/vite-build/games/harbor/harbor.md new file mode 100644 index 0000000..7ed9319 --- /dev/null +++ b/packages/bgm/src/__fixtures__/vite-build/games/harbor/harbor.md @@ -0,0 +1,57 @@ +# Harbor + +A tiny example game used to exercise the bgm loader end-to-end through a real +vite build. + +```yaml file=harbor.yaml +role: package +id: harbor +title: Harbor +designer: Jane Doe +players: 2 +language: en +``` + +```yaml file=parts/tokens.yaml +role: part +type: token +id: wood +face: ./assets/tokens.png +faceCrop: [1, 0, 5, 2] +back: ./assets/tokens.png +backCrop: [3, 0, 5, 2] +shape: ./assets/token-shape.png +size: [20, 20, 3] +fillet: 2 +``` + +```yaml file=parts/board.yaml +type: board +id: harbor +role: surface +size: [300, 200] +layout: + - route: /dock/:seat + candidates: + $variants: ./seats.csv + - route: /deck + x: -100 + y: 0 + rotation: 0 +``` + +```csv file=parts/seats.csv +seat,x,y,rotation +string,number,number,number +0,40,0,0 +1,40,20,0 +``` + +```yaml file=setup/main.yaml +role: setup +type: game +id: main +setup: + /dock/0: harbor:token#wood + /deck: harbor:token#grain +``` diff --git a/packages/bgm/src/__fixtures__/vite-build/index.html b/packages/bgm/src/__fixtures__/vite-build/index.html new file mode 100644 index 0000000..4dc75ba --- /dev/null +++ b/packages/bgm/src/__fixtures__/vite-build/index.html @@ -0,0 +1,10 @@ + + + + + bgm build fixture + + + + + diff --git a/packages/bgm/src/__fixtures__/vite-build/src/main.ts b/packages/bgm/src/__fixtures__/vite-build/src/main.ts new file mode 100644 index 0000000..3eab879 --- /dev/null +++ b/packages/bgm/src/__fixtures__/vite-build/src/main.ts @@ -0,0 +1,9 @@ +import harbor from 'bgm/harbor'; + +// Re-export the package data so the test can assert the bundled output. +export const parts = [...harbor.parts.keys()]; +export const surfaces = [...harbor.surfaces.keys()]; +export const setups = [...harbor.setups.keys()]; +export const title = harbor.meta.title; + +console.log(title, parts, surfaces, setups); diff --git a/packages/bgm/src/collect.test.ts b/packages/bgm/src/collect.test.ts index 5a5b160..2685cdd 100644 --- a/packages/bgm/src/collect.test.ts +++ b/packages/bgm/src/collect.test.ts @@ -3,7 +3,7 @@ import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; import { loadDefs, collectPackages } from './collect.js'; -const fixtureRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '__fixtures__'); +const fixtureRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '__fixtures__', 'harbor'); describe('collectPackages', () => { it('collects the harbor package from markdown code blocks', () => { diff --git a/packages/bgm/src/index.ts b/packages/bgm/src/index.ts index 60860d5..401d72f 100644 --- a/packages/bgm/src/index.ts +++ b/packages/bgm/src/index.ts @@ -3,4 +3,5 @@ export * from './schemas.js'; export * from './markdown.js'; export * from './parse.js'; export * from './variants.js'; -export * from './collect.js'; \ No newline at end of file +export * from './collect.js'; +export * from './vite.js'; \ No newline at end of file diff --git a/packages/bgm/src/vite.test.ts b/packages/bgm/src/vite.test.ts new file mode 100644 index 0000000..fc253c7 --- /dev/null +++ b/packages/bgm/src/vite.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { build } from 'vite'; +import { bgm } from './vite.js'; + +const fixtureRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '__fixtures__', + 'vite-build', +); +const gamesRoot = path.join(fixtureRoot, 'games'); + +describe('bgm vite plugin (integration)', () => { + it('resolves bgm/ imports through a real vite build', async () => { + const outDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bgm-build-')); + + try { + await build({ + root: fixtureRoot, + logLevel: 'silent', + build: { + outDir, + write: true, + emptyOutDir: true, + }, + plugins: [bgm({ root: gamesRoot })], + }); + + // The fixture's entry re-exports the package data; find the bundle chunk. + const chunk = walk(outDir).find((f) => f.endsWith('.js'))!; + const code = fs.readFileSync(path.join(outDir, chunk), 'utf8'); + + // The plugin serialized the package's maps into the emitted module. + expect(code).toContain('token#wood'); + expect(code).toContain('board#harbor'); + expect(code).toContain('game#main'); + expect(code).toContain('Harbor'); + } finally { + await fs.promises.rm(outDir, { recursive: true, force: true }); + } + }); +}); + +/** Recursively list files under a directory, as paths relative to it. */ +function walk(dir: string): string[] { + const out: string[] = []; + const visit = (current: string) => { + for (const entry of fs.readdirSync(current, { withFileTypes: true })) { + const full = path.join(current, entry.name); + if (entry.isDirectory()) visit(full); + else out.push(path.relative(dir, full)); + } + }; + visit(dir); + return out; +} diff --git a/packages/bgm/src/vite.ts b/packages/bgm/src/vite.ts new file mode 100644 index 0000000..b57e234 --- /dev/null +++ b/packages/bgm/src/vite.ts @@ -0,0 +1,64 @@ +/** + * Vite plugin: resolve `bgm/` imports to the package's JSON. + * + * The loader reads a games root (markdown code blocks + real + * yaml/json/toml/csv files), collects packages, and this plugin serves each + * package as a module whose default export is the assembled JSON. Editing a + * game definition hot-reloads the app via `addWatchFile`. + */ +import * as path from 'node:path'; +import type { Plugin } from 'vite'; +import { collectPackages, loadDefs } from './collect.js'; +import type { Package } from './types.js'; + +const VIRTUAL_PREFIX = '\0bgm:'; + +export interface BgmOptions { + /** Absolute path to the games root (e.g. `/games`). */ + root: string; +} + +export function bgm(options: BgmOptions): Plugin { + const root = options.root; + + const collect = (): Package[] => { + const defMap = loadDefs('', root); + return collectPackages(defMap, root); + }; + + return { + name: 'bgm', + buildStart() { + // Watch every source file so edits trigger a reload/re-collect. + const defMap = loadDefs('', root); + for (const name of defMap.files.keys()) { + this.addWatchFile(path.join(root, name)); + } + }, + resolveId(id) { + if (id.startsWith('bgm/')) return VIRTUAL_PREFIX + id; + }, + load(id) { + if (!id.startsWith(VIRTUAL_PREFIX)) return; + const name = id.slice(VIRTUAL_PREFIX.length + 'bgm/'.length); + const pkg = collect().find((p) => p.meta.id === name); + if (!pkg) { + throw new Error(`bgm package "${name}" not found`); + } + return `export default ${JSON.stringify(toJson(pkg))}`; + }, + }; +} + +/** + * Serialize a `Package` for JSON emission. The parts/surfaces/setups are + * `Map`s, which `JSON.stringify` would otherwise turn into `{}`. + */ +function toJson(pkg: Package): Record { + return { + meta: pkg.meta, + parts: Object.fromEntries(pkg.parts), + surfaces: Object.fromEntries(pkg.surfaces), + setups: Object.fromEntries(pkg.setups), + }; +} diff --git a/packages/bgm/src/vite.unit.test.ts b/packages/bgm/src/vite.unit.test.ts new file mode 100644 index 0000000..3c6573a --- /dev/null +++ b/packages/bgm/src/vite.unit.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from 'vitest'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { bgm } from './vite.js'; + +const fixtureRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '__fixtures__'); +const gamesRoot = path.join(fixtureRoot, 'harbor'); + +/** + * Vite 8 types plugin hooks as `ObjectHook`, which may be a plain function or + * a `{ handler, order }` object. Our plugin uses plain functions, so cast the + * hook to a callable for direct invocation in tests. + */ +type Callable = T extends (...args: infer A) => infer R ? (...args: A) => R : never; + +function resolveId(plugin: ReturnType, id: string): unknown { + return (plugin.resolveId as Callable)(id, undefined, { + isEntry: false, + }); +} + +function load(plugin: ReturnType, id: string): unknown { + return (plugin.load as Callable)(id, { ssr: false }); +} + +describe('bgm vite plugin', () => { + it('resolves bgm/ imports to the virtual module', () => { + const plugin = bgm({ root: gamesRoot }); + expect(resolveId(plugin, 'bgm/harbor')).toBe('\0bgm:bgm/harbor'); + expect(resolveId(plugin, 'bgm/nope')).toBe('\0bgm:bgm/nope'); + expect(resolveId(plugin, 'other')).toBeUndefined(); + }); + + it('loads a package as a JSON module', () => { + const plugin = bgm({ root: gamesRoot }); + const code = load(plugin, '\0bgm:bgm/harbor'); + expect(code).toBeDefined(); + expect(String(code).startsWith('export default ')).toBe(true); + const pkg = JSON.parse(String(code).slice('export default '.length)); + expect(pkg.meta.id).toBe('harbor'); + expect(pkg.parts).toHaveProperty('token#wood'); + expect(pkg.surfaces).toHaveProperty('board#harbor'); + }); + + it('errors on an unknown package', () => { + const plugin = bgm({ root: gamesRoot }); + expect(() => load(plugin, '\0bgm:bgm/unknown')).toThrow(/not found/); + }); +}); diff --git a/packages/bgm/tsconfig.json b/packages/bgm/tsconfig.json index 8eed416..c3b233a 100644 --- a/packages/bgm/tsconfig.json +++ b/packages/bgm/tsconfig.json @@ -5,5 +5,6 @@ "rootDir": "./src", "lib": ["ES2022", "DOM"] }, - "include": ["src"] + "include": ["src"], + "exclude": ["src/__fixtures__", "src/**/*.test.ts"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d117cd..664df80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.10 - version: 4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)) + version: 4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)) apps/proxy: dependencies: @@ -81,6 +81,9 @@ importers: '@react-three/postprocessing': specifier: ^3.0.4 version: 3.0.4(@react-three/fiber@9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1))(@types/three@0.185.4)(react@19.2.8)(three@0.185.1) + '@tts/bgm': + specifier: workspace:* + version: link:../../packages/bgm '@tts/extract': specifier: workspace:* version: link:../../packages/extract @@ -111,7 +114,7 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.3.3 - version: 4.3.3(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)) + version: 4.3.3(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)) '@types/react': specifier: ^19.2.18 version: 19.2.18 @@ -123,7 +126,7 @@ importers: version: 0.185.4 '@vitejs/plugin-react': specifier: ^6.0.5 - version: 6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)) + version: 6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)) tailwindcss: specifier: ^4.3.3 version: 4.3.3 @@ -132,10 +135,47 @@ importers: version: 5.9.3 vite: specifier: ^8.2.1 - version: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11) + version: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0) vitest: specifier: ^4.1.10 - version: 4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)) + version: 4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)) + + packages/bgm: + dependencies: + marked: + specifier: ^16.0.0 + version: 16.4.2 + picomatch: + specifier: ^4.0.5 + version: 4.0.5 + smol-toml: + specifier: ^1.4.0 + version: 1.7.1 + typed-csv: + specifier: ^2.0.0 + version: 2.0.0(esbuild@0.28.1) + vite: + specifier: ^8.2.1 + version: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0) + yaml: + specifier: ^2.4.2 + version: 2.9.0 + zod: + specifier: ^3.24.1 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^22.12.0 + version: 22.20.1 + '@types/picomatch': + specifier: ^4.0.0 + version: 4.0.3 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + vitest: + specifier: ^4.1.10 + version: 4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)) packages/extract: dependencies: @@ -829,6 +869,9 @@ packages: '@types/offscreencanvas@2019.7.3': resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} + '@types/picomatch@4.0.3': + resolution: {integrity: sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==} + '@types/react-dom@19.2.4': resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} peerDependencies: @@ -958,6 +1001,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + csv-parse@5.6.0: + resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + detect-gpu@5.0.70: resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==} @@ -1213,6 +1259,11 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + meshline@3.3.1: resolution: {integrity: sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==} peerDependencies: @@ -1343,6 +1394,10 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + smol-toml@1.7.1: + resolution: {integrity: sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==} + engines: {node: '>= 18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1429,6 +1484,17 @@ packages: tunnel-rat@0.1.2: resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==} + typed-csv@2.0.0: + resolution: {integrity: sha512-iwDT4D2SjLJswiwbKUl21ikzRWfXxy6R7pylfKk418ylMPJm3kJk4n87eJN/1bMYszLZm7ISo99IoyE2DhqWFA==} + peerDependencies: + '@rspack/core': ^1.x + esbuild: '*' + peerDependenciesMeta: + '@rspack/core': + optional: true + esbuild: + optional: true + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -1546,6 +1612,11 @@ packages: engines: {node: '>=8'} hasBin: true + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -2002,12 +2073,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 - '@tailwindcss/vite@4.3.3(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11))': + '@tailwindcss/vite@4.3.3(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11) + vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0) '@tweenjs/tween.js@23.1.3': {} @@ -2032,6 +2103,8 @@ snapshots: '@types/offscreencanvas@2019.7.3': {} + '@types/picomatch@4.0.3': {} + '@types/react-dom@19.2.4(@types/react@19.2.18)': dependencies: '@types/react': 19.2.18 @@ -2066,10 +2139,10 @@ snapshots: '@visioncortex/vtracer@1.0.0-alpha.3': {} - '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11))': + '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11) + vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0) '@vitest/expect@4.1.10': dependencies: @@ -2080,13 +2153,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11))': + '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11) + vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -2153,6 +2226,8 @@ snapshots: csstype@3.2.3: {} + csv-parse@5.6.0: {} + detect-gpu@5.0.70: dependencies: webgl-constants: 1.1.1 @@ -2359,6 +2434,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + marked@16.4.2: {} + meshline@3.3.1(three@0.185.1): dependencies: three: 0.185.1 @@ -2497,6 +2574,8 @@ snapshots: siginfo@2.0.0: {} + smol-toml@1.7.1: {} + source-map-js@1.2.1: {} stackback@0.0.2: {} @@ -2578,6 +2657,12 @@ snapshots: - immer - react + typed-csv@2.0.0(esbuild@0.28.1): + dependencies: + csv-parse: 5.6.0 + optionalDependencies: + esbuild: 0.28.1 + typescript@5.9.3: {} undici-types@6.21.0: {} @@ -2588,7 +2673,7 @@ snapshots: utility-types@3.11.0: {} - vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11): + vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -2601,11 +2686,12 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 tsx: 4.23.11 + yaml: 2.9.0 - vitest@4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)): + vitest@4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)) + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -2622,7 +2708,7 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11) + vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.20.1 @@ -2642,6 +2728,8 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + yaml@2.9.0: {} + zod@3.25.76: {} zustand@4.5.7(@types/react@19.2.18)(react@19.2.8):