Skip to content

Development

Austin Stanfield edited this page Oct 5, 2026 · 9 revisions

Development

Local setup, the check scripts and the seeds.

Running it locally

npm install
# A Postgres to point at — anything will do
docker run -d --name wed-db -e POSTGRES_USER=wed -e POSTGRES_PASSWORD=wed \
  -e POSTGRES_DB=wed -p 5432:5432 postgres:15-alpine

DATABASE_URL=postgresql://wed:wed@localhost:5432/wed ADMIN_PASSWORD=dev npm run dev

Then http://localhost:3000, and /admin with ADMIN_PASSWORD.

The schema creates itself: database/init.sql is idempotent and the container runs it on every boot. Running locally with npm run dev skips that, so apply it once by hand — psql … < database/init.sql.

Checks

None of these need a network, and only check:finance:db needs a database, so they are cheap to run before a commit.

Command What it covers
npm run check:types tsc --noEmit over the app and the scripts
npm run lint ESLint
npm run check:honeymoon Distances, date maths, URL parsing, the calendar grid, .ics output, travel arcs and overnight legs, stay ranking, search ranking, seed integrity, journeys, time zones, the budget, opening hours, sunrise/sunset, the timeline with travel, the place panel's sections and travel documents — 743 assertions
npm run check:offline The offline worker's rules and the offline page lists — 38 checks, no browser, in CI
npm run check:offline:ui The whole site offline in a real browser against a production build, with the server cut off through a proxy — 49 checks. Manual
npm run check:honeymoon:ui The honeymoon portal in a real browser (53 checks, none of which write): one place panel from every entry point, the itinerary toolbar, travel on the timeline, and the phone layout — no sideways scroll, nothing under 44px. Needs a browser and a server with honeymoon data: BASE=… ADMIN_PASSWORD=…, or DEMO=1 against the demo. Manual, not CI
npm run check:seating Who takes a chair (a party member who declined takes none), seat-index allocation, moves, swaps, gathering a split party, auto-seating, the plan's warnings, and the export: tallies, vendor plates, the grand total, the spreadsheet and the one-page fit maths — 199 assertions
npm run check:changelog The changelog parser against fixtures and the real CHANGELOG.md — 56 assertions
npm run check:photos The photo route: thumbs, resizing, 404s, the traversal guard
npm run check:finance Budget arithmetic
npm run check:finance:db The same against a live database, plus the real route handlers — CRUD, validation, injection safety, and the settings singleton on every verb the UI sends
npm run check:finance:ui The finance UI's contracts
npm run audit:finance A deeper sweep over the finance logic

check:changelog is the one that catches a hand-editing mistake in CHANGELOG.md — most usefully, nested backticks inside a code span, which cannot parse and render as raw ** in the in-app viewer.

Seeds

Command What it does
npm run seed:honeymoon Loads the bundled Bali/Singapore travel guide into the honeymoon portal — 7 regions, 224 places, 12 notes. Idempotent: it matches on place name and skips anything already there, so it never reverts an edit.
npm run seed:demo -- --yes-wipe Fills an instance with a completely fictional wedding. Destructive. See The demo instance.

Running it as the demo

DEMO_MODE=true makes an instance read-only and login-free. The flag is refused unless the database in DATABASE_URL is named demo, so to try it locally the database has to be called that:

docker run -d --name demo-db -e POSTGRES_USER=demo -e POSTGRES_PASSWORD=demo \
  -e POSTGRES_DB=demo -p 5433:5432 postgres:15-alpine

DATABASE_URL=postgresql://demo:demo@localhost:5433/demo \
  DEMO_MODE=true ADMIN_PASSWORD=demo npm run dev

Writes under /api/ are then answered but never stored, /admin needs no password, and a banner says so. Details in The demo instance; the flag itself is src/lib/demo.ts (server) and src/lib/demoClient.ts (browser, via /api/demo-status).

Conventions worth knowing

  • CHANGELOG.md is the source of truth for the version. There is no number in package.json. See Versions and the changelog.
  • database/init.sql is the only copy of the schema. Some tables are also created at runtime by the API route that owns them; if you add one of those, add it here too or a fresh install comes up without it.
  • Photos are served through /api/photos/[...], never /photos/… — with standalone output, files written into a volume at runtime are not served statically. See Architecture.
  • AGENTS.md in the repository root carries the working agreements for this project — the deploy loop, the changelog rules, and the traps that have cost time before. It follows agents.md; CLAUDE.md is a symlink to it, so there is one file and not two to keep in step. (If CLAUDE.md ever shows up as a 9-byte text file, git was cloned with core.symlinks=false — git config core.symlinks true, delete it, check it out again.)
  • Docker lives in docker/ — Dockerfile, the three compose files (.dev, .prod, .demo) and .env.example. The build context stays the repository root, so builds are -f docker/Dockerfile ..
  • Pushing to main is the deploy, and it reaches the running servers on its own: GitHub Actions publishes the image and each host's timer pulls it within about five minutes. Nothing is built on a laptop. See Deployment.
  • ops/ holds the host-side pieces — the two auto-updaters and the DOCKER-USER port rules — with the reasoning that made each necessary. Nothing in there is used by the application or the Docker build; it is version-controlled so it is reviewable, because each one started as a file on exactly one box.

← All pages

Clone this wiki locally