`code.nochebuena.dev/einherjar/spa-server` is a container-first HTTP server for single-page applications and progressive web apps. It serves static assets directly and falls back to `index.html` for navigation routes that do not resolve to a file — the standard SPA routing contract — while a missing asset returns `404` rather than the HTML shell.
The module ships as a ready-to-use Docker base image. Deploying a SPA to a container requires a single `COPY` instruction. No nginx, no custom configuration, no index.html redirect logic to maintain.
`GET /health` returns `200 OK` with a JSON body consistent with all Einherjar health responses:
```json
{"status":"UP","components":{}}
```
`503 Service Unavailable` is never returned — if the process is alive, it is healthy. Wire `/health` directly to your container liveness and readiness probes.
---
## Routing behaviour
| Request | Result |
|---|---|
| `/app.js` — file exists | Served directly with correct `Content-Type` |
| `/assets/logo.png` — file exists | Served directly |
| `/main.js` — **no file, has an extension** | **`404`** — a missing asset is not masked as HTML |
| `/api/x` — no file, `Accept: application/json` | **`404`** — a non-HTML client is not handed `index.html` |
The fallback to `index.html` is deliberately scoped to **navigation** requests (no
file extension, and `Accept` includes `text/html` or `*/*`). A request for a missing
asset returns `404` instead of `index.html`, so a broken deploy fails at the first
request rather than delivering the SPA shell as JavaScript (`Unexpected token '<'`).
---
## Caching
`Cache-Control` is set per file, correct for Vite, CRA and Angular alike:
| File | `Cache-Control` |
|---|---|
| `index.html` | `no-cache` — revalidated every load, so a new deploy is never masked |
| Service workers (`ngsw.json`, `ngsw-worker.js`, `sw.js`, `service-worker.js`, `safety-worker.js`, `workbox-*.js`) | `no-cache` — a client is never pinned to a superseded release |