Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# random-server — notes for Claude

A REST API server that returns random JSON data (people, words, values, coords,
and an always-empty route). TypeScript + Express 5, Cucumber tests, Swagger UI
at `/api-docs`, optional `API_KEY` auth, graceful shutdown, multi-stage Docker
and an always-empty route). TypeScript 7 + Express 5, Cucumber 13 tests, Swagger
UI at `/api-docs`, optional `API_KEY` auth, graceful shutdown, multi-stage Docker
on Node 24, published to GHCR + Docker Hub via tag-triggered workflows.

## Remaining work (open GitHub issues)
Expand All @@ -15,15 +15,21 @@ Run `gh issue list` for the current state.
## Conventions

- **TypeScript build:** source in `src/`, compiled to `dist/` via `npm run build`
(`tsc` + `copyfiles` for the `*.yaml` swagger files). `start` runs
`node dist/index.js`.
(`tsc`, then `scripts/copy-yaml.mjs` to mirror the `*.yaml` swagger files into
`dist/`). `start` runs `node dist/index.js`. The copy step is a plain
`fs.cpSync` with no dependencies — it replaced `copyfiles`, which was
unmaintained and dragged in `glob@7` + `inflight`.
- **Tests:** `npm test` (Cucumber, `features/`). The suite **spawns the compiled
server** (`node dist/index.js`) in `BeforeAll`, so `npm run build` must run
first. The test server launches with `API_KEY=demo-key`. CI
(`.github/workflows/test.yml`) runs build + test on push/PR to `main`.
- **Release:** bump the version and push a `v*` tag → the publish workflows build
and push multi-platform images to GHCR + Docker Hub and sync the README to
Docker Hub. See the README "Publish" section.
- **PR CI never builds the Docker image** — only the tag-triggered publish
workflows do. A broken `Dockerfile` or build script therefore stays invisible
until release. Run `docker build .` locally before tagging whenever the build
pipeline, `Dockerfile`, or anything `npm run build` touches has changed.
- **Routing (Express 5 / path-to-regexp v8):** bare `*` wildcards are gone — a
route like `app.get('*', ...)` throws `PathError: Missing parameter name` at
**boot**, not at request time, so it takes the whole server down. Use the named
Expand All @@ -49,19 +55,25 @@ Run `gh issue list` for the current state.
updates are independent of this file and arrive regardless.
- **Transitive CVEs are pinned via `overrides`,** not by adding direct
dependencies — see the `overrides` block in `package.json` (`js-yaml`, `qs`,
`brace-expansion`, plus a **nested** `copyfiles → minimatch →
brace-expansion` entry, which is needed because copyfiles pins an old
minimatch that the root override alone doesn't reach). When a follow-up
advisory lands for something already pinned, **bump the existing entry**
rather than adding a second one — `brace-expansion` has now been moved twice
this way.
`brace-expansion`). When a follow-up advisory lands for something already
pinned, **bump the existing entry** rather than adding a second one —
`brace-expansion` has been moved twice this way (`5.0.6 → 5.0.7 → 5.0.9`).
Overrides can also be **scoped to one dependent** when only that package is
the problem: `swagger-jsdoc → glob: ^13.0.6` exists solely to silence a
deprecation warning, and is safe because glob v13 still exports the `.sync`
swagger-jsdoc calls and the `apis` entries are literal paths, not patterns.
- **Docker base image: stay on LTS Node.** Dependabot will propose odd-numbered
current releases (Node 25 was declined in #60); take a major only when the
next LTS ships. The `Dockerfile` pins the floating `24-alpine` tag, so
patch/minor Node updates already arrive at build time with no PR.
- **Closing a Dependabot PR** stops it re-proposing *that* version but not
future ones — it opens a fresh PR when a newer version appears. That's why
#60 was closed without an `ignore` rule: Node 26 LTS should still get a PR.
- **Keep install output warning-free.** A cold `npm ci` currently emits **zero**
`npm warn deprecated` lines; keep it that way. Warnings that always appear and
never matter train you to skip the output, so a real one gets missed. Fix the
cause — drop or replace the offending package, or scope an override — rather
than hiding it behind `--silent` or a redirect.
- **Untyped JS dependencies fail the build under TypeScript 7** with `TS7016`
(5.x silently inferred `any`). `@mitchallen/uptime` was vendored to
`src/uptime.ts` for exactly this reason — don't re-add it. For a tiny untyped
Expand Down