Document the separated front/back/walls extrusion, material-level back flips, CardID sprite selection from the parent deck, index-path tree selection, and the Bounds-inside-Suspense camera fit.
6.5 KiB
Architecture & Dependencies
Scope: The system's architecture and dependency graph. For implementation details (files, endpoints, build order), see
implementation-plan.md. For the rationale behind key decisions, seedecisions.md.
Overview
A lightweight, client-only pnpm monorepo that lets a user search the Tabletop Simulator Steam Workshop, fetch full TTS save files, and analyze their contents. It is split into four packages with a strict layering: a thin HTTP proxy on top, a low-level fetcher, an isomorphic analysis layer, and a shared types/validation package.
Design principles
- Client-only & lightweight — no caching layer, no shared server state.
- Fetch vs analyze separation —
packages/ttsonly fetches and parses;packages/extractonly analyzes. Neither depends on the other's concerns. - Isomorphic analysis —
packages/extractruns in browser and Node, using onlyfetch,Blob, and typed arrays (noBuffer, no Node-only packages). - Thin proxy — the HTTP layer exposes search and fetch only; traversal is intentionally not exposed as endpoints.
Package responsibilities
| Package | Role | Runtime |
|---|---|---|
apps/web |
React frontend: search + mod pages | Browser |
apps/proxy |
Hono HTTP server: search + fetch endpoints | Node |
packages/tts |
Fetch save from Steam, BSON-parse to TTSMod |
Node |
packages/extract |
Analyze a TTSMod: objects, refs, assets |
Isomorphic |
packages/mesh |
2D shapes + extrusion into 3D mesh geometry | Isomorphic |
packages/shared |
Shared types + zod schemas | Isomorphic |
Dependency graph
apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
│ │ │
│ │ └──► (fetchMod → TTSMod)
│ ▼
├──► packages/mesh ──► packages/shared (types)
└──► packages/extract ──► packages/shared (types)
Edges
apps/web→apps/proxy— calls/search,/items/:id,/items/:id/file,/asset(CORS-safe asset proxy), and/trace(image → vector shape) over HTTP.apps/web→packages/extract— usesbuildTree/collectRefsto analyze a loadedTTSModin the browser (tree sidebar + asset refs).apps/web→packages/mesh— extrudes 2D shapes into 3D geometry ({ front, back, walls }) for the tile, token, and card viewers.apps/proxy→packages/tts— callsfetchMod/getFileNameto serve item requests.apps/proxy→packages/shared— uses shared types and zod schemas for request/response validation.packages/tts→packages/shared— consumesTTSMod/TTSObjecttypes.packages/extract→packages/tts— reusestraverseMod(traversal logic lives inextract; see note below).packages/extract→packages/shared— consumes shared types.
Note on
traverseMod: traversal is analysis, so it lives inpackages/extract.packages/ttsis fetch-only. The graph edgeextract → ttsreflects thatextractimports the traversal helper that was originally authored alongside the fetcher;ttsdoes not depend onextract.
Layering rules
- No upward dependencies —
packages/*never importapps/*. - No sibling coupling beyond the graph above —
extractandttsdo not depend on each other's analysis/fetch concerns. packages/sharedis the leaf — everything depends on it; it depends on nothing internal.
External dependencies
| Package | Purpose | Used by |
|---|---|---|
hono |
HTTP framework | apps/proxy |
@hono/node-server |
Node adapter for Hono | apps/proxy |
@hono/cors |
CORS middleware | apps/proxy |
bson |
BSON deserialization of TTS save files | packages/tts |
@visioncortex/vtracer |
Raster-to-SVG vectorization (wasm) | apps/proxy |
clipper-lib |
Polygon offsetting (inset/outset) for traced shapes | apps/proxy |
sharp |
Image decoding to RGBA | apps/proxy |
svgpath |
SVG path parsing for traced shapes | apps/proxy |
cheerio |
Workshop browse page scraping | apps/proxy |
zod |
Runtime validation | apps/proxy, packages/shared |
three |
3D rendering | apps/web |
@react-three/fiber |
React renderer for three.js | apps/web |
@react-three/drei |
three.js helpers (controls, textures, bounds) | apps/web |
@react-three/postprocessing |
Post-processing effects | apps/web |
Runtime constraints
cheeriois backend-only — it never appears inpackages/extract, which must stay isomorphic.bsonis Node-only — used by the fetcher and the trace route, not the analysis layer.@visioncortex/vtracer,sharp,svgpath,clipper-libare backend-only — the trace route lives inapps/proxy; they never appear inpackages/extract, which must stay isomorphic.packages/extracthas zero external runtime deps — it relies only on platformfetch/Blob, keeping it portable to a future frontend.
Tooling dependencies
typescript(strict),tsx(dev runner),eslint,prettier.vitest(unit tests), colocated as*.test.tsnext to sources.vite,@vitejs/plugin-react,tailwindcss,@tailwindcss/vite— frontend build/dev tooling.pnpmworkspaces for package management.
Deployment / runtime shape
- The proxy runs as a single Node process via
@hono/node-server. apps/webis a static Vite build served separately; during development it proxies/search,/items,/health,/asset, and/traceto the proxy.packages/extractis published/consumed as a plain ESM module usable from a browser bundle or Node.- No shared state between requests; each request fetches fresh.