Add apps/web (Vite + React Router + Tailwind v4 + Zustand) with a search page and a mod page that analyzes saves via @tts/extract. Document the frontend in the README and docs.
4.4 KiB
Decisions
Scope: The rationale behind key design decisions. For the system's architecture, see
architecture.md. For the concrete build plan, seeimplementation-plan.md.Each entry records the decision, the context, and the alternatives considered. New entries are appended; existing entries are updated only to correct facts, not to rewrite history.
D1 — Client-only, no caching
Decision: The system runs on the client only and has no caching layer.
Context: The tool is a personal, single-user utility. There is no shared state to protect and no multi-instance deployment.
Alternatives considered: In-memory TTL caching; Redis-backed caching. Both rejected as unnecessary complexity for a single client.
D2 — Separate packages/extract from packages/tts
Decision: Analysis lives in packages/extract; fetching lives in
packages/tts. They are separate packages with a clean fetch-vs-analyze
boundary.
Context: The user wants to analyze saves and extract objects for a future frontend. Keeping analysis separate lets it be reused independently of the fetcher.
Alternatives considered: Folding analysis into packages/tts. Rejected —
couples fetch and analysis and would force the frontend to depend on the
fetcher.
D3 — Traversal lives in packages/extract
Decision: traverseMod / markParent move from the fetcher into
packages/extract.
Context: Traversal is analysis, not fetching. packages/tts should be
fetch-only.
Alternatives considered: Keeping traversal in packages/tts. Rejected —
blurs the fetch/analyze boundary.
D4 — packages/extract is isomorphic with zero runtime deps
Decision: packages/extract runs in browser and Node, using only fetch,
Blob, and typed arrays. It has no external runtime dependencies.
Context: The frontend (deferred) will consume it directly, so it must be
portable. Node 18+ provides global fetch and Blob.
Alternatives considered: Using Buffer and Node-only packages. Rejected —
breaks browser use.
D5 — No backend traversal endpoints
Decision: The proxy exposes search and fetch only. Traversal is not exposed as HTTP endpoints.
Context: The frontend will use packages/extract directly. Exposing
traversal on the backend would duplicate that logic and add endpoints with no
current consumer.
Alternatives considered: Adding /items/:id/objects and similar endpoints.
Deferred until a consumer exists.
D6 — Search scrapes the Workshop browse page
Decision: Search is implemented by scraping
steamcommunity.com/workshop/browse/?appid=286160 with cheerio.
Context: Steam has no official Web API for searching the Workshop; the API only fetches details for known IDs.
Alternatives considered: None viable — there is no official search API. Accepted risk: scraping is fragile and may break if Steam changes its HTML.
D7 — bson npm package replaces the browser BSON global
Decision: The existing scraper's BSON.deserialize global is replaced with
the bson npm package.
Context: The scraper was written for the browser; the proxy runs on Node.
Alternatives considered: Keeping a browser-only global. Rejected — not available in Node.
D8 — Download output type is Blob
Decision: packages/extract's downloadAsset / downloadAll return Blob.
Context: The primary consumer is a frontend that renders assets in
<img> / <object> elements.
Alternatives considered: Raw ArrayBuffer. Rejected — less convenient for
frontend rendering.
D9 — Frontend stack: React + React Router + Tailwind v4 + Zustand
Decision: The frontend (apps/web) uses React with React Router for
routing, Tailwind CSS v4 for styling, and Zustand for state, built with Vite.
Context: The frontend consumes the proxy API for search/fetch and
packages/extract directly for analysis. React is the de-facto standard for
this kind of tool; React Router provides declarative routes (/ and
/mod/:id); Tailwind v4 is the current major version and integrates via
@tailwindcss/vite; Zustand is a minimal, unopinionated store that fits the
small amount of client state (search + mod).
Alternatives considered: Next.js (heavier than needed for a client-only tool); Redux Toolkit (more boilerplate than warranted); CSS Modules (no utility styling).