Cover shared zod schemas, extract traversal/refs/downloads, and tts filename/error handling. Document the test setup in the README and docs.
4.6 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/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/shared |
Shared types + zod schemas | Isomorphic |
Dependency graph
apps/proxy ──► packages/tts ──► packages/shared
│ │
│ └──► (fetchMod → TTSMod)
▼
packages/extract ──► packages/tts (traverseMod) ──► packages/shared (types)
Edges
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 |
cheerio |
Workshop browse page scraping | apps/proxy |
zod |
Runtime validation | apps/proxy, packages/shared |
Runtime constraints
cheeriois backend-only — it never appears inpackages/extract, which must stay isomorphic.bsonis Node-only — used by the fetcher, not the analysis layer.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.pnpmworkspaces for package management.
Deployment / runtime shape
- The proxy runs as a single Node process via
@hono/node-server. 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.