126 lines
6.5 KiB
Markdown
126 lines
6.5 KiB
Markdown
# Architecture & Dependencies
|
|
|
|
> **Scope:** The system's architecture and dependency graph. For implementation
|
|
> details (files, endpoints, build order), see
|
|
> [`implementation-plan.md`](./implementation-plan.md). For the rationale behind
|
|
> key decisions, see [`decisions.md`](./decisions.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/tts` only fetches and parses;
|
|
`packages/extract` only analyzes. Neither depends on the other's concerns.
|
|
- **Isomorphic analysis** — `packages/extract` runs in browser and Node, using
|
|
only `fetch`, `Blob`, and typed arrays (no `Buffer`, 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`** — uses `buildTree` / `collectRefs`
|
|
to analyze a loaded `TTSMod` in 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`** — calls `fetchMod` / `getFileName` to serve
|
|
item requests (the filename is derived from the save URL path).
|
|
- **`apps/proxy` → `packages/shared`** — uses shared types and zod schemas for
|
|
request/response validation.
|
|
- **`packages/tts` → `packages/shared`** — consumes `TTSMod` / `TTSObject`
|
|
types.
|
|
- **`packages/extract` → `packages/tts`** — reuses `traverseMod` (traversal
|
|
logic lives in `extract`; see note below).
|
|
- **`packages/extract` → `packages/shared`** — consumes shared types.
|
|
|
|
> **Note on `traverseMod`:** traversal is analysis, so it lives in
|
|
> `packages/extract`. `packages/tts` is fetch-only. The graph edge
|
|
> `extract → tts` reflects that `extract` imports the traversal helper that was
|
|
> originally authored alongside the fetcher; `tts` does not depend on `extract`.
|
|
|
|
### Layering rules
|
|
|
|
- **No upward dependencies** — `packages/*` never import `apps/*`.
|
|
- **No sibling coupling beyond the graph above** — `extract` and `tts` do not
|
|
depend on each other's analysis/fetch concerns.
|
|
- **`packages/shared` is 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
|
|
|
|
- **`cheerio` is backend-only** — it never appears in `packages/extract`, which
|
|
must stay isomorphic.
|
|
- **`bson` is Node-only** — used by the fetcher and the trace route, not the
|
|
analysis layer.
|
|
- **`@visioncortex/vtracer`, `sharp`, `svgpath`, `clipper-lib` are
|
|
backend-only** — the trace route lives in `apps/proxy`; they never appear in
|
|
`packages/extract`, which must stay isomorphic.
|
|
- **`packages/extract` has zero external runtime deps** — it relies only on
|
|
platform `fetch` / `Blob`, keeping it portable to a future frontend.
|
|
|
|
## Tooling dependencies
|
|
|
|
- `typescript` (strict), `tsx` (dev runner), `eslint`, `prettier`.
|
|
- `vitest` (unit tests), colocated as `*.test.ts` next to sources.
|
|
- `vite`, `@vitejs/plugin-react`, `tailwindcss`, `@tailwindcss/vite` — frontend
|
|
build/dev tooling.
|
|
- `pnpm` workspaces for package management.
|
|
|
|
## Deployment / runtime shape
|
|
|
|
- The proxy runs as a single Node process via `@hono/node-server`.
|
|
- `apps/web` is a static Vite build served separately; during development it
|
|
proxies `/search`, `/items`, `/health`, `/asset`, and `/trace` to the proxy.
|
|
- `packages/extract` is published/consumed as a plain ESM module usable from a
|
|
browser bundle or Node.
|
|
- No shared state between requests; each request fetches fresh. |