Upgrade to Next.js 16.x (#42) #57

Merged
james merged 2 commits from 42-nextjs-16-upgrade into main 2026-06-15 11:41:05 +00:00
Owner

Closes #42.

Summary

  • Bumps next and eslint-config-next past the ^15.3.0 cap to ^16.2.9. engines.node rises from >=20.0.0 to >=20.9.0 to match Next 16's floor; the Dockerfile (node:22-slim) already exceeds it.
  • Renames middleware.tsproxy.ts (and tests/middleware.test.tstests/proxy.test.ts). Next 16 renamed the per-request gate; proxy is Node-runtime only by definition, which kills the experimental.nodeMiddleware flag — and silences the schema-validator warning the flag was producing.
  • Adds --webpack to dev and build scripts. Next 16 makes Turbopack the default for next build, and our next.config.mjs carries a custom webpack() hook (the node: URI externals we added in #18). Turbopack migration is explicitly out of scope per the issue; the flag keeps webpack as the bundler.
  • Rewrites eslint.config.mjs to use eslint-config-next 16's native flat-config exports directly. The previous FlatCompat.extends(...) path errored under v16 (Converting circular structure to JSON). Drops the now-unused @eslint/eslintrc dep.
  • Adds ADR-0006 documenting the major-version pinning policy and today's pin at 16.x.
  • Drops the bundled-picomatch .trivyignore entry (CVE-2026-33671) — its own comment specified "Revisit when Next.js is bumped." If Trivy still flags it on 16.2.9, this PR's image_scan job will surface it and we can re-add with refreshed rationale.
  • next-env.d.ts and tsconfig.json are touched by Next 16's typegen on first build (.next/dev/ output split; jsx: react-jsx is the new default). Committed as auto-generated.

What didn't change

  • No code paths use the 15→16 breaking-change surfaces that would have forced edits: no params/searchParams/cookies/headers/draftMode consumers, no next/image, no AMP, no parallel routes, no serverRuntimeConfig, no sitemaps, no PPR. Verified by grep.
  • React stays on ^19.0.0 (resolves to 19.2 transitively under Next 16).

Test plan

  • npm run typecheck — clean.
  • npm run lint — clean (after flat-config rewrite).
  • npm test — 61 passed / 20 skipped.
  • npm run build — succeeds; build output shows ƒ Proxy (Middleware).
  • npm run dev — starts clean, no Invalid next.config.mjs options detected warning, no node:fs error.
  • Curl probes: /, /manifest.webmanifest, /offline, /icon-192.png, /sw.js, /api/health → 200; /dashboard → 401 (default-deny intact).
  • CI passes: build, dual-engine tests, and the three security scans (npm_audit, osv_scan, image_scan). Image_scan will confirm whether the dropped picomatch entry needs re-adding.

🤖 Generated with Claude Code

Closes #42. ## Summary - Bumps `next` and `eslint-config-next` past the `^15.3.0` cap to `^16.2.9`. `engines.node` rises from `>=20.0.0` to `>=20.9.0` to match Next 16's floor; the Dockerfile (`node:22-slim`) already exceeds it. - Renames `middleware.ts` → `proxy.ts` (and `tests/middleware.test.ts` → `tests/proxy.test.ts`). Next 16 renamed the per-request gate; `proxy` is Node-runtime only by definition, which kills the `experimental.nodeMiddleware` flag — and silences the schema-validator warning the flag was producing. - Adds `--webpack` to `dev` and `build` scripts. Next 16 makes Turbopack the default for `next build`, and our `next.config.mjs` carries a custom `webpack()` hook (the `node:` URI externals we added in #18). Turbopack migration is explicitly out of scope per the issue; the flag keeps webpack as the bundler. - Rewrites `eslint.config.mjs` to use eslint-config-next 16's native flat-config exports directly. The previous `FlatCompat.extends(...)` path errored under v16 (`Converting circular structure to JSON`). Drops the now-unused `@eslint/eslintrc` dep. - Adds ADR-0006 documenting the major-version pinning policy and today's pin at 16.x. - Drops the bundled-picomatch `.trivyignore` entry (`CVE-2026-33671`) — its own comment specified "Revisit when Next.js is bumped." If Trivy still flags it on 16.2.9, this PR's `image_scan` job will surface it and we can re-add with refreshed rationale. - `next-env.d.ts` and `tsconfig.json` are touched by Next 16's typegen on first build (`.next/dev/` output split; `jsx: react-jsx` is the new default). Committed as auto-generated. ## What didn't change - No code paths use the 15→16 breaking-change surfaces that would have forced edits: no `params`/`searchParams`/`cookies`/`headers`/`draftMode` consumers, no `next/image`, no AMP, no parallel routes, no `serverRuntimeConfig`, no sitemaps, no PPR. Verified by grep. - React stays on `^19.0.0` (resolves to 19.2 transitively under Next 16). ## Test plan - [x] `npm run typecheck` — clean. - [x] `npm run lint` — clean (after flat-config rewrite). - [x] `npm test` — 61 passed / 20 skipped. - [x] `npm run build` — succeeds; build output shows `ƒ Proxy (Middleware)`. - [x] `npm run dev` — starts clean, no `Invalid next.config.mjs options detected` warning, no `node:fs` error. - [x] Curl probes: `/`, `/manifest.webmanifest`, `/offline`, `/icon-192.png`, `/sw.js`, `/api/health` → 200; `/dashboard` → 401 (default-deny intact). - [ ] CI passes: build, dual-engine tests, and the three security scans (`npm_audit`, `osv_scan`, `image_scan`). Image_scan will confirm whether the dropped picomatch entry needs re-adding. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Upgrade to Next.js 16.x (#42)
Some checks failed
PR / OSV-Scanner (pull_request) Successful in 28s
PR / Lint (pull_request) Successful in 47s
PR / npm audit (pull_request) Successful in 47s
PR / Typecheck (pull_request) Successful in 49s
PR / Test (postgres) (pull_request) Successful in 52s
PR / Test (sqlite) (pull_request) Successful in 52s
PR / Build (pull_request) Successful in 1m11s
PR / Trivy (image) (pull_request) Failing after 1m27s
444c3f72c3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-add bundled-picomatch ignore: Next 16.2.9 still ships picomatch 4.0.3 (#42)
All checks were successful
PR / OSV-Scanner (pull_request) Successful in 23s
PR / Trivy (image) (pull_request) Successful in 28s
PR / npm audit (pull_request) Successful in 1m41s
PR / Lint (pull_request) Successful in 1m46s
PR / Typecheck (pull_request) Successful in 1m49s
PR / Test (sqlite) (pull_request) Successful in 1m51s
PR / Build (pull_request) Successful in 1m56s
PR / Test (postgres) (pull_request) Successful in 1m56s
b3fe011ba8
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
james force-pushed 42-nextjs-16-upgrade from b3fe011ba8
All checks were successful
PR / OSV-Scanner (pull_request) Successful in 23s
PR / Trivy (image) (pull_request) Successful in 28s
PR / npm audit (pull_request) Successful in 1m41s
PR / Lint (pull_request) Successful in 1m46s
PR / Typecheck (pull_request) Successful in 1m49s
PR / Test (sqlite) (pull_request) Successful in 1m51s
PR / Build (pull_request) Successful in 1m56s
PR / Test (postgres) (pull_request) Successful in 1m56s
to caed303a4e
All checks were successful
PR / OSV-Scanner (pull_request) Successful in 24s
PR / Typecheck (pull_request) Successful in 40s
PR / npm audit (pull_request) Successful in 40s
PR / Lint (pull_request) Successful in 54s
PR / Static analysis (Semgrep) (pull_request) Successful in 54s
PR / Test (sqlite) (pull_request) Successful in 54s
PR / Test (postgres) (pull_request) Successful in 56s
PR / Build (pull_request) Successful in 1m6s
PR / Trivy (image) (pull_request) Successful in 1m22s
2026-06-15 11:39:15 +00:00
Compare
james merged commit e182d75e4e into main 2026-06-15 11:41:05 +00:00
Sign in to join this conversation.
No description provided.