PWA configuration (manifest, service worker, offline shell) (#18) #40

Merged
james merged 1 commit from 18-pwa-configuration into main 2026-06-14 01:49:22 +00:00
Owner

Closes #18.

Summary

  • Web app manifest at /manifest.webmanifest via app/manifest.ts, plus PNG (192/512) and SVG icons rendered from public/icon.svg by scripts/render-icons.mjs.
  • Service worker via @serwist/next: precaches the build manifest, runtime caches with Serwist's defaultCache, falls back to /offline for any document navigation that misses cache. Registered from a client component (app/sw-register.tsx) on idle, mounted in the root layout.
  • Static /offline page (force-static) for the offline shell.
  • PWA-essential routes added to the public allowlist (/, /offline, /manifest.webmanifest, /sw.js, icons, /workbox- prefix) with reasoning inline in lib/auth/public-routes.ts.
  • Service worker has its own TS config (tsconfig.sw.json) targeting WebWorker libs so app/sw.ts typechecks against worker globals; npm run typecheck runs both passes.
  • next.config.mjs wraps the config with withSerwist and adds a webpack externals matcher that externalises both Node-only DB packages (pg/libsql family) and the node: URI scheme — needed because Next.js's instrumentation compile follows the dynamic import from instrumentation.ts into db/client.ts, which has top-level import fs from "node:fs". Without externalising node: URIs, npm run dev errors with UnhandledSchemeError: Reading from "node:fs" is not handled by plugins.
  • Middleware test (tests/middleware.test.ts) extended to assert the new PWA-essential public routes.

Test plan

  • npm run typecheck — passes both app and SW configs.
  • npm run lint — clean.
  • npm test — 61 passed / 20 skipped.
  • npm run build — succeeds, service worker bundled, 11 routes generated.
  • npm run dev — starts clean, no node:fs error.
  • Curl probes: /, /manifest.webmanifest, /offline, /icon-192.png, /sw.js → 200; /dashboard → 401 (default-deny intact).
  • Manual (browser): Lighthouse PWA audit — installability + offline.
  • Manual (browser): Install as standalone app from a modern browser.
  • Manual (browser): Kill network after first load and confirm the shell + /offline fallback render without a network round-trip.

The three manual browser checks above are the issue's acceptance criteria and need a real browser to verify.

🤖 Generated with Claude Code

Closes #18. ## Summary - Web app manifest at `/manifest.webmanifest` via `app/manifest.ts`, plus PNG (192/512) and SVG icons rendered from `public/icon.svg` by `scripts/render-icons.mjs`. - Service worker via `@serwist/next`: precaches the build manifest, runtime caches with Serwist's `defaultCache`, falls back to `/offline` for any document navigation that misses cache. Registered from a client component (`app/sw-register.tsx`) on idle, mounted in the root layout. - Static `/offline` page (`force-static`) for the offline shell. - PWA-essential routes added to the public allowlist (`/`, `/offline`, `/manifest.webmanifest`, `/sw.js`, icons, `/workbox-` prefix) with reasoning inline in `lib/auth/public-routes.ts`. - Service worker has its own TS config (`tsconfig.sw.json`) targeting `WebWorker` libs so `app/sw.ts` typechecks against worker globals; `npm run typecheck` runs both passes. - `next.config.mjs` wraps the config with `withSerwist` and adds a webpack externals matcher that externalises both Node-only DB packages (pg/libsql family) and the `node:` URI scheme — needed because Next.js's instrumentation compile follows the dynamic import from `instrumentation.ts` into `db/client.ts`, which has top-level `import fs from "node:fs"`. Without externalising `node:` URIs, `npm run dev` errors with `UnhandledSchemeError: Reading from "node:fs" is not handled by plugins`. - Middleware test (`tests/middleware.test.ts`) extended to assert the new PWA-essential public routes. ## Test plan - [x] `npm run typecheck` — passes both app and SW configs. - [x] `npm run lint` — clean. - [x] `npm test` — 61 passed / 20 skipped. - [x] `npm run build` — succeeds, service worker bundled, 11 routes generated. - [x] `npm run dev` — starts clean, no `node:fs` error. - [x] Curl probes: `/`, `/manifest.webmanifest`, `/offline`, `/icon-192.png`, `/sw.js` → 200; `/dashboard` → 401 (default-deny intact). - [ ] **Manual (browser):** Lighthouse PWA audit — installability + offline. - [ ] **Manual (browser):** Install as standalone app from a modern browser. - [ ] **Manual (browser):** Kill network after first load and confirm the shell + `/offline` fallback render without a network round-trip. The three manual browser checks above are the issue's acceptance criteria and need a real browser to verify. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
PWA configuration (manifest, service worker, offline shell) (#18)
Some checks failed
PR / Lint (pull_request) Successful in 29s
PR / Typecheck (pull_request) Successful in 35s
PR / Test (sqlite) (pull_request) Successful in 38s
PR / Build (pull_request) Successful in 55s
PR / Test (postgres) (pull_request) Failing after 2m0s
4266ea0704
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
james force-pushed 18-pwa-configuration from 4266ea0704
Some checks failed
PR / Lint (pull_request) Successful in 29s
PR / Typecheck (pull_request) Successful in 35s
PR / Test (sqlite) (pull_request) Successful in 38s
PR / Build (pull_request) Successful in 55s
PR / Test (postgres) (pull_request) Failing after 2m0s
to cf9337fdc3
All checks were successful
PR / OSV-Scanner (pull_request) Successful in 30s
PR / Test (postgres) (pull_request) Successful in 1m39s
PR / Test (sqlite) (pull_request) Successful in 1m51s
PR / npm audit (pull_request) Successful in 3m10s
PR / Build (pull_request) Successful in 3m14s
PR / Lint (pull_request) Successful in 3m21s
PR / Typecheck (pull_request) Successful in 3m25s
PR / Trivy (image) (pull_request) Successful in 3m38s
2026-06-14 01:44:30 +00:00
Compare
james merged commit 35fc772713 into main 2026-06-14 01:49:22 +00:00
Sign in to join this conversation.
No description provided.