Audio with range requests, library.json, Homer books; LRC parser. Content stays out of git and the image. Co-Authored-By: Claude Opus 5.5 <[email protected]>
4.2 KiB
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, { contentDir? }): routes /, /mockups/NN-slug/ (+ 302 from the no-slash form), /content/* via src/content/routes.ts, 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. |
src/content/routes.ts |
contentResponse(req, dir): /content/library.json, /content/files/<path> (traversal-guarded, HTTP ranges), /content/homer/<odyssey|iliad>/<book>.json. All 404 when no folder is mounted. |
src/content/library.ts |
scanLibrary(dir): sagas (story order) and songs from Songs and Lyrics/EPIC - <Saga> (<year>)/; .lrc preferred over .txt. |
src/content/homer.ts |
splitBooks(text): Gutenberg Butler text → books (heading, summary, paragraphs). |
src/shared/lrc.ts |
parseLrc(text): timed lyric lines, sorted. |
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), content.test.ts (LRC, library scan, Homer split, /content/* routes on a temp fixture). |
Dockerfile |
Two-stage oven/bun:1.3: install + build, then copy dist/ and run from WORKDIR /app/dist. |
Data flow
- Request hits
Bun.serve; an HTML-import route returns the bundled page (dev: bundled on demand with HMR viabun --hot; production: prebuilt assets fromdist/). - The page's
main.tsimportssrc/data/*andsrc/shared/*(bundled in; no runtime API). main.tsrenders the direction and callsmountCredits()into<footer data-fan-credits>.
External dependencies
Optional at runtime: the content folder named by CONTENT_DIR (real audio, lyrics, cover art, Butler's
Odyssey and Iliad). It is mounted read-only, never committed or baked into the image (spec 003). Without it
the site works and /content/* answers 404. Otherwise none. 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_NOTICEviamountCredits(). src/server.tsroutes,src/mockups/registry.tsand the cards insrc/index.htmllist 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, DockerWORKDIR /app/dist);bun dist/server.jsfrom the repo root crashes. Seedocs/TODO.md. - Routes, registry and index cards are three hand-maintained lists. See
docs/TODO.md.