5.9 KiB
bgm Loader — Status
WIP. What's built, what works, what's missing, and the known issues. Spec:
bgm-format.md.
What's built
packages/bgm — the loader core (new)
| File | Purpose |
|---|---|
src/types.ts |
Roles (Package, Part, Surface, Setup, Route, Stacking, SurfaceMount, …), SerializedPackage (the JSON the plugin emits), BgmError, DefFile, ParsedDef |
src/schemas.ts |
zod schemas + validate* for each role |
src/markdown.ts |
Virtual def files from markdown code blocks, via marked. file= naming + content-hash auto-naming (./<hash>.yaml); multiple blocks may share a file= name |
src/parse.ts |
yaml/json/toml → def objects (yaml, smol-toml); real-file walker (incl. .csv) |
src/variants.ts |
$variants expansion via typed-csv (typed-csv/csv-loader). Inline (newline) vs path; paths resolve against the virtual def map |
src/collect.ts |
loadDefs (real + virtual, virtual wins), collectPackages (package decl → include globs via picomatch → parts/surfaces/setups, type#id uniqueness, $variants on defs and route candidates). Sets each part's baseUrl to its source file's directory for resolving relative asset paths |
src/vite.ts |
The vite plugin (bgm()): resolves virtual:bgm/packages (all packages) and virtual:bgm/package/<id> (one package) imports to export default <json>, watches source files for reload. Serializes the package's Maps to objects (SerializedPackage); throws on unknown packages; re-collects per load (no stale cache). |
src/*.test.ts |
23 tests, all passing — markdown extractor, typed-csv parsing (incl. the spec's empty-array + crop tuple cases), full harbor collection, plugin unit tests (resolve/load/shape/watch), and a real vite build integration test covering two packages |
Deps: marked, typed-csv, yaml, smol-toml, picomatch, zod, vite, @types/picomatch.
games/harbor/harbor.md — example game (new)
Exercises the format end-to-end: package decl, two file=parts/tokens.yaml blocks, a table surface with mount/children and candidates: $variants against a virtual csv block, a child player surface, and a setup declaring its enabled surfaces. Same content duplicated as the vitest fixture under packages/bgm/src/__fixtures__/harbor/.
games/poker/poker.md — example game (new)
A real 52-card deck: a single card part expanded by $variants into 52 cards, each picking a cell from a 13×4 face sheet (cards-13x4.jpg) and a shared 4×1 back sheet (back-4x1.png). Assets are stored in Git LFS (.gitattributes).
packages/tabletop — rendering library (new)
A standalone r3f library that renders bgm parts. PartView/PartMesh build a mesh from a Part definition via @tts/mesh (size/fillet, face/back sprite UVs, traced shape or rect fallback). Proxy calls (/asset, /trace) are injectable via TabletopProvider, so the host app supplies its own handlers. Plan: bgm-tabletop-plan.md.
apps/proxy — local game assets (new)
The proxy serves local game assets from the games root: GAMES_ROOT (env or default) is set at startup (config.ts) and read by the /asset and /trace routes to serve relative paths (e.g. poker/parts/assets/cards.png) from disk, alongside the existing http(s) proxy path.
apps/web — consumer (new)
vite.config.ts— wired withbgm({ root: <repo>/games }), importing the plugin from@tts/bgm.src/vite-env.d.ts— ambientdeclare module 'virtual:bgm/packages'(all packages) and'virtual:bgm/package/*'(one package).src/pages/BgmPage.tsx— list page: importsvirtual:bgm/packagesand shows every discovered package. Routed at/bgm.src/pages/BgmPackagePage.tsx— detail page: looks up a package by id frombgmand renders its parts/surfaces/setups. Routed at/bgm/:id.
The vite plugin itself lives in packages/bgm/src/vite.ts (exported from @tts/bgm), not in the web app — so the loader's plugin is tested in isolation from the web project.
Works
pnpm --filter @tts/bgm buildandtypecheckpass.- Root
pnpm test: 191 pass. pnpm --filter @tts/web buildsucceeds; config warnings fixed.src/vite.test.tsruns a realvite buildagainst a self-contained fixture (src/__fixtures__/vite-build/) and asserts the bundled output contains the package data — the plugin is proven end-to-end without touching the web app.pnpm --filter @tts/tabletop build/testpass;pnpm --filter @tts/proxy typecheckpasses.
Known issues
Plugin emits empty maps— fixed:loadserializesMaps viaObject.fromEntries.Plugin— fixed: throws instead.loadusesthis.errorHMR cache not invalidated— fixed: dropped the closure cache; re-collects perload.— removed.tinyglobbyleftover depPackage-level test script finds no files— addedpackages/bgm/vitest.config.ts.
Not yet done
No consumer yet—apps/web/src/pages/BgmPage.tsxlists packages fromvirtual:bgm/packages;BgmPackagePage.tsxshows one at/bgm/:id.$variantsURL paths — spec mentions file/URL; URLs deferred.- zod
SerializedPackageshape for the emitted JSON — the plugin emitsSerializedPackageobjects; a zod schema for the emitted module would give runtime validation beyond the ambientdeclare module. setupvalue expansion —typewithoutid→ all parts of that type is documented but not implemented in the loader (it's a game-state init concern; noted as future).- Surface mounting is validated but not resolved —
mount/children/surfacesare parsed and validated, but the loader doesn't resolve child→parent relationships or enforce that a setup'ssurfaces/a surface'schildrenreference existing surfaces. That's a game-state/rendering concern (seedocs/bgm-tabletop.md). - Docs for the loader itself (this file is the start).