49 lines
3.3 KiB
Markdown
49 lines
3.3 KiB
Markdown
# Architecture
|
|
|
|
How `epic-the-musical-timeline` is built today. Update in the same commit as any structural change.
|
|
|
|
## Overview
|
|
A Bun server (`Bun.serve`) with HTML-import routes serves an index page and seven mockup pages. Bun's bundler
|
|
turns each HTML page and its imported TS/CSS into hashed assets. `bun run build` bundles the server and every
|
|
page into `dist/`; the Docker image runs that bundle. All pages read one typed sample dataset and mount one
|
|
shared credits snippet.
|
|
|
|
## Map
|
|
| Path | Responsibility |
|
|
|---|---|
|
|
| `src/server.ts` | `startServer(port)`: routes `/`, `/mockups/NN-slug/` (+ 302 from the no-slash form), 404 otherwise. Runs when `import.meta.main`; `PORT` env, default 3000. |
|
|
| `src/index.html`, `src/index.ts`, `src/index.css` | Index page: static cards linking each mockup, then `mountCredits()`. |
|
|
| `src/mockups/registry.ts` | `mockups: MockupInfo[]` — slug, number, name, pitch, what to try. |
|
|
| `src/mockups/NN-slug/` | One design direction: `index.html` (links `../../shared/credits.css`, has `<footer data-fan-credits>`), `main.ts`, `style.css`, plus optional helper modules (e.g. `scene.ts`, `layout.ts`). |
|
|
| `src/data/types.ts` | `Saga`, `Song`, `Part`, `Motif`, `StoryTime`, `OdysseyRef`, `Video`. |
|
|
| `src/data/sample.ts` | `sagas`, `motifs`, `songs`. Placeholder lyrics; unverified fields `// TODO(verify)`. |
|
|
| `src/data/query.ts` | `songsWithMotif`, `motifsInSong`, `gapYears`, `sagaOf`. |
|
|
| `src/shared/credits.ts` | `FAN_NOTICE`, `mountCredits(target?)`. |
|
|
| `src/shared/audio-slot.ts` | `createAudioSlot(song)`: `<audio>` for `song.audio`, else an empty-state slot. |
|
|
| `src/shared/video-cards.ts` | `createVideoCards(song)`: YouTube link cards, no embeds. |
|
|
| `src/shared/credits.css` | Styles for credits, audio slot, video cards. |
|
|
| `tests/` | `data.test.ts` (dataset invariants), `shared.test.ts` (DOM modules under happy-dom), `server.test.ts` (every route 200s, index links every registry slug, each mockup has the credits hook). |
|
|
| `Dockerfile` | Two-stage `oven/bun:1.3`: install + build, then copy `dist/` and run from `WORKDIR /app/dist`. |
|
|
|
|
## Data flow
|
|
1. Request hits `Bun.serve`; an HTML-import route returns the bundled page (dev: bundled on demand with HMR
|
|
via `bun --hot`; production: prebuilt assets from `dist/`).
|
|
2. The page's `main.ts` imports `src/data/*` and `src/shared/*` (bundled in; no runtime API).
|
|
3. `main.ts` renders the direction and calls `mountCredits()` into `<footer data-fan-credits>`.
|
|
|
|
## External dependencies
|
|
None at runtime. Fonts (`@fontsource/*`) and three.js are bundled from npm. Outbound links only: YouTube
|
|
(video cards) and Project Gutenberg (Odyssey references) — the site works without them.
|
|
|
|
## Invariants
|
|
- Every page renders `FAN_NOTICE` via `mountCredits()`.
|
|
- `src/server.ts` routes, `src/mockups/registry.ts` and the cards in `src/index.html` list the same slugs
|
|
(`tests/server.test.ts`).
|
|
- Every motif id used by a part exists; every motif appears in at least one song (`tests/data.test.ts`).
|
|
- No two mockups ship byte-identical `style.css` (the build fails otherwise).
|
|
|
|
## Known rough edges
|
|
- The production bundle only runs with `dist/` as the cwd (`bun run start`, Docker `WORKDIR /app/dist`);
|
|
`bun dist/server.js` from the repo root crashes. See `docs/TODO.md`.
|
|
- Routes, registry and index cards are three hand-maintained lists. See `docs/TODO.md`.
|