From 676d5ae6798d3f71c693adb179c06207a30329cb Mon Sep 17 00:00:00 2001 From: Jan Gregor Emge-Triebel Date: Tue, 4 Aug 2026 16:55:14 +0200 Subject: [PATCH 1/2] Align nuxt-app on Node 24 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing about the runtime changes. Four declarations that disagreed with it now agree. The production build log for main already says: Warning: Due to "engines" ... the Node.js Version defined in your Project Settings ("20.x") will not apply, Node.js Version "24.x" will be used So engines.node is authoritative on Vercel — over the project setting and over .nvmrc, which Vercel never consults. Narrowing engines to ^24.11.0 || >=26.0.0 keeps it resolving 24.x, so the deployed runtime is untouched. Workflows now read nuxt-app/.nvmrc instead of naming a version, so CI and local development cannot drift apart again: one file, not three. The Directus extension stays on 22. It has its own lockfile and Jest setup and is frozen under the licence block, so moving its runtime would be an untested change to a tree nobody may upgrade. .nvmrc's v22 was not arbitrary — #174 set it to fix ERR_REQUIRE_ESM on Node 20. That was a floor, and 24 clears it by a wider margin, so moving forward cannot reintroduce it. Also removes nuxt-app/.npmrc. Its two options are pnpm-only no-ops under npm, and npm 11 (which ships with Node 24) promotes them to deprecation warnings on every command, including in Vercel's build log. Verified on Node 24.19.0 locally after a clean npm ci, not by pushing and reading CI: format clean, 91 tests, lint 0 errors/127 warnings, ratchet steady at 263, build exit 0, lockfile byte-identical. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JkKMceYAzAYLrSyC42FWTf --- .github/workflows/run_tests.yml | 9 ++- .github/workflows/smoke_tests.yml | 4 +- AGENTS.md | 3 +- docs/dependency-upgrade-plan.md | 109 +++++++++++++++++++++++++++--- nuxt-app/.npmrc | 2 - nuxt-app/.nvmrc | 2 +- nuxt-app/package.json | 2 +- 7 files changed, 113 insertions(+), 18 deletions(-) delete mode 100644 nuxt-app/.npmrc diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 54033864..56a3177b 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -17,6 +17,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + # Deliberately still 22 while nuxt-app moves to 24: this tree has its own lockfile, its own + # Jest setup, and is frozen pending the Directus licence clarification. Moving its runtime + # would be an untested change to a tree nobody is allowed to upgrade. - name: Use Node.js 22.x uses: actions/setup-node@v4 with: @@ -43,10 +46,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Use Node.js 22.x + # Reads nuxt-app/.nvmrc rather than naming a version here, so CI and local development cannot + # drift apart again — there is one file to change, not three. + - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version-file: nuxt-app/.nvmrc cache: 'npm' cache-dependency-path: 'nuxt-app/package-lock.json' diff --git a/.github/workflows/smoke_tests.yml b/.github/workflows/smoke_tests.yml index 5bb7e337..1b9ce7b0 100644 --- a/.github/workflows/smoke_tests.yml +++ b/.github/workflows/smoke_tests.yml @@ -22,10 +22,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Use Node.js 22.x + - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version-file: nuxt-app/.nvmrc cache: 'npm' cache-dependency-path: 'nuxt-app/package-lock.json' diff --git a/AGENTS.md b/AGENTS.md index 24c96620..215d91c6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,8 @@ A podcast/conference/meetup platform for the German developer community. Built w - **Server**: Nitro (Nuxt's server engine) - **Search**: Algolia - **AI**: Google Gemini (spam filtering) -- **Node**: v19+ (v22 in CI) +- **Node**: 24 for `nuxt-app` — see `nuxt-app/.nvmrc`, which CI reads too. The Directus extension + still builds on 22. ## Directory Structure diff --git a/docs/dependency-upgrade-plan.md b/docs/dependency-upgrade-plan.md index 19d5cbd7..8ea9f7a5 100644 --- a/docs/dependency-upgrade-plan.md +++ b/docs/dependency-upgrade-plan.md @@ -41,6 +41,7 @@ each phase leaves the app in a shippable state and can be reverted on its own. | 6 | Deferred: Tailwind 4, Node 24 | ⬜ Deliberately deferred | | — | [Registry sweep and the formatter bump](#registry-sweep-and-the-formatter-bump--done-2026-08-04) | ✅ Done (2026-08-04) | | — | [Prettier sweep + format gate](#the-prettier-sweep-and-the-format-gate--done-2026-08-04) | ✅ Done (2026-08-04) | +| — | [Node 24 alignment for `nuxt-app`](#node-24-alignment-for-nuxt-app--done-2026-08-04) | ✅ Done (2026-08-04) | | — | [After the plan — follow-up backlog](#after-the-plan--follow-up-backlog) | 📋 Consolidated, unscheduled | Each phase's own write-up ends with what it deliberately left behind. Those are also gathered into @@ -1617,6 +1618,85 @@ loaded is indistinguishable from the same chunk rehashed. Asset filenames keep t those are still compared. And 11 routes is not every route — the four portal and checkout pages are behind flags or payment state and were not exercised. +## Node 24 alignment for `nuxt-app` — done 2026-08-04 + +**Nothing about the runtime changed. Four declarations that disagreed with it now agree.** Verified from +the production build log for `main` (`3bde75d`): + +``` +Warning: Due to "engines": { "node": "^22.19.0 || ^24.11.0 || >=26.0.0" } in your `package.json` file, +the Node.js Version defined in your Project Settings ("20.x") will not apply, +Node.js Version "24.x" will be used instead. +``` + +So `engines.node` is authoritative on Vercel — it beats the project setting *and* `.nvmrc`, which +Vercel does not mention at all. Production has been on 24.x while every declaration said 22 or 20. + +| declaration | was | now | +| --- | --- | --- | +| `nuxt-app/package.json` `engines.node` | `^22.19.0 \|\| ^24.11.0 \|\| >=26.0.0` | `^24.11.0 \|\| >=26.0.0` | +| `nuxt-app/.nvmrc` | `v22` | `24` | +| `run_tests.yml` (nuxt-app job) | `node-version: 22` | `node-version-file: nuxt-app/.nvmrc` | +| `smoke_tests.yml` | `node-version: 22` | `node-version-file: nuxt-app/.nvmrc` | +| Vercel project `nodeVersion` | `20.x` | ⬜ **still `20.x`** — dashboard-only, see below | + +Narrowing `engines` keeps Vercel resolving 24.x, so the deployed runtime is untouched. The workflows +now **read `.nvmrc`** rather than naming a version, so CI and local development cannot drift apart +again — one file to change instead of three. That is the same reasoning as the format gate: remove the +opportunity for divergence rather than re-synchronising by hand. + +**The Directus extension stays on Node 22**, in both `run_tests.yml` and +`algolia-index-maintenance.yml`. It has its own lockfile and Jest setup and is frozen under the licence +block; moving its runtime would be an untested change to a tree nobody is permitted to upgrade. + +### The `v22` in `.nvmrc` was a floor, not a ceiling — checked before moving it + +`nuxt-app/.nvmrc` was not missing, and it was not arbitrary. It came from +**#174, "Pin nuxt-app to Node 22 to fix Vercel runtime crash"** (2026-05-06), and the crash was +`ERR_REQUIRE_ESM`: a lockfile regen pulled `jsdom@28 → html-encoding-sniffer@6 → @exodus/bytes` +(ESM-only), whose CJS `require()` works only on Node 22.12+ or 20.19+. Vercel was running Node 20 and +every SSR function died at startup. + +That pin raised a floor. **Node 24 clears it by a wider margin than 22 did**, so moving forward cannot +reintroduce that failure — and the chain is not even present now, since Phase 5 pinned +`isomorphic-dompurify` back to `~2.20.0` with jsdom 26. Worth recording because a commit titled "pin to +fix crash" reads like a ceiling, and moving it without opening it would have been reckless. + +### npm 11 arrives with Node 24 and gates dependency install scripts + +Node 24.19.0 bundles npm 11.17.0, which does not run third-party lifecycle scripts by default: + +``` +npm warn allow-scripts 4 packages have install scripts not yet covered by allowScripts: +npm warn allow-scripts esbuild@0.28.1 (postinstall: node install.js) +npm warn allow-scripts fsevents@2.3.2, fsevents@2.3.3, unrs-resolver@1.12.2 +``` + +**The project's own `postinstall` still runs** — `nuxt prepare` executed and regenerated +`.nuxt/eslint.config.mjs`, which `npm run lint` depends on. Only *dependency* scripts are gated, and +none of them turned out to be load-bearing: after a clean `npm ci` with all four blocked, `esbuild +--version` works (its binary comes from the `@esbuild/darwin-arm64` optional dependency, not from +`install.js`), and the full gate set passes. + +**Left blocked rather than allowlisted.** Blocking is the safer default, an `allowScripts` entry is a +standing grant to execute arbitrary code at install time, and nothing needs it. The warnings in CI logs +are expected. + +### Verification — run on Node 24, not on 22 + +Installed 24.19.0 locally and ran everything against it after a clean `npm ci`, rather than pushing and +letting CI find out: `prettier:check` clean, **91 tests passing**, `lint` 0 errors / 127 warnings +(identical to Node 22), typecheck ratchet **steady at 263**, and +`SKIP_PRERENDER_ROUTE_DISCOVERY=true npm run build` exit 0. `npm ci` under npm 11 left +`package-lock.json` byte-identical. + +### Still outstanding: the Vercel project setting + +`nodeVersion` remains `20.x`, now overridden twice over. It is dashboard-only — the MCP surface exposes +no project-update call — so it needs a human. The reason to fix it is unchanged and is *not* about +today's runtime: if `engines.node` were ever simplified or dropped, the project would silently fall back +to Node 20, which is below both Nuxt's floor and the `require(esm)` threshold that caused #174. + ## Phase 6 — Deliberately deferred Not blocked by EOL. Do **not** fold these into the phases above. @@ -1625,8 +1705,10 @@ Not blocked by EOL. Do **not** fold these into the phases above. and `@nuxtjs/tailwindcss@6` hard-pins `tailwindcss ~3.4.17`, so it also means moving to `@tailwindcss/vite`. `tailwind.config.js` carries a substantial custom theme (brand colours, seven custom breakpoints). Its own project. -- [ ] **Node 22 → 24.** Verify Vercel's supported runtimes first. Update `engines.node`, `.nvmrc`, - and the `node-version` in every workflow together. +- [x] ✅ **Node 22 → 24** — done 2026-08-04 for `nuxt-app`. See + [the write-up](#node-24-alignment-for-nuxt-app--done-2026-08-04). The Directus extension stays on + 22 deliberately. **Production was already running 24.x**, so this aligned the declarations with + reality rather than changing the runtime. - [ ] **TypeScript 6.0.3 → 7.x.** Only the 7.x jump remains — **6.0.3 landed separately after Phase 2** (see below). TS 7 is the Go-based native rewrite, and the open question is no longer "will our code cope" but "do the tools support it": TS 7 broke `ts-api-utils` (it @@ -1945,13 +2027,15 @@ browser-support decision nobody made. what runs, so this does **not** pre-empt Phase 6's "Node 22 → 24" item — that one is about `engines`, `.nvmrc` and the workflows agreeing, and should be told that production is already on 24. -- [ ] **Add a `.nvmrc`.** None exists, and Phase 4 raised `engines.node` to - `^22.19.0 || ^24.11.0 || >=26.0.0` — ahead of at least one developer's local Node. Pairs - naturally with Phase 6's Node 24 item, which already says to move `engines`, `.nvmrc` and - every workflow's `node-version` together. -- [ ] **Remove or justify `nuxt-app/.npmrc`.** `shamefully-hoist` and `strict-peer-dependencies` - are pnpm options and no-ops under npm (Appendix B) — they read as protection that is not - there. +- [x] ✅ **~~Add a `.nvmrc`~~ — corrected: one already existed.** `nuxt-app/.nvmrc` has been tracked + since **#174 (2026-05-06)**, holding `v22`. This document claimed "none exists" because the check + was run at the repo root only. Moved to `24` in the Node alignment — see + [the write-up](#node-24-alignment-for-nuxt-app--done-2026-08-04). +- [x] ✅ **Removed `nuxt-app/.npmrc`** — done 2026-08-04. Both options were pnpm-only no-ops under npm + (Appendix B), and npm 11 — which arrives with Node 24 — promotes them to + `Unknown project config … will stop working in the next major version of npm` on every command, + in CI *and* in Vercel's build log. Verified no pnpm lockfile or reference exists anywhere in the + repo before deleting. ### 6. Standing items, not one-off tasks @@ -2300,4 +2384,11 @@ Tracked so nobody has to rediscover them. None are urgent on their own. | 2026-08-04 | **Four detectors were built and discarded** before that comparison worked: whitespace-stripping counted quote normalisation as a content change (63 false positives), template-collapsing counted attribute reflow (46), and the raw diff was drowned in chunk hashes, `data-v-*` ids, keyframe names and a per-build uuid. Each mask narrows sensitivity, so the final script asserts it can still see an added class, a removed class, a changed attribute and inline whitespace. | | 2026-08-04 | The sweep's single rendered difference — `>Regulär<` becoming `> Regulär <` where Prettier broke a 120+ character line — was **measured in Chromium rather than reasoned about**, with a positive control. Identical geometry for the real case; the same edit between two `inline-block` siblings shifts 4.12px. Without the control, "identical" would have been indistinguishable from a measurement that cannot see whitespace. Prettier judges whitespace significance from a tag's default display, so a `
` made `inline-block` by a Tailwind class is exactly the blind spot to check for. | | 2026-08-04 | Recorded that `main` has **no branch protection** (404 from the protection endpoint), so no CI check has ever blocked a merge. Phase 0 made upgrades detectable, not unmergeable — a distinction the plan had been treating as settled. Needs a repo admin, like Renovate. | +| 2026-08-04 | Node 24 alignment shipped for `nuxt-app` only, with the Directus extension left on 22. Its jobs use a separate lockfile and Jest setup and the tree is frozen under the licence block, so moving its runtime would be an untested change nobody is allowed to follow up on. | +| 2026-08-04 | Workflows changed to `node-version-file: nuxt-app/.nvmrc` instead of a literal `node-version`. Re-synchronising three declarations by hand is what produced this drift; making CI read the file removes the opportunity. Same reasoning as the format gate one layer down. | +| 2026-08-04 | **Opened `.nvmrc` before moving it, and it changed the framing.** It was not missing — this document's "none exists" was another repo-root-only check — and its `v22` came from #174, "Pin nuxt-app to Node 22 to fix Vercel runtime crash". That crash was `ERR_REQUIRE_ESM` on Node 20, so the pin was a **floor**; Node 24 clears it by a wider margin. A commit titled "pin to fix crash" reads like a ceiling, and moving it unread would have been reckless. | +| 2026-08-04 | Narrowed `engines.node` to `^24.11.0 \|\| >=26.0.0` after confirming from the production build log that Vercel already resolves **24.x** and that `engines` beats both the project setting and `.nvmrc`. The deployed runtime is therefore unchanged by this PR — the declarations were what disagreed with reality. | +| 2026-08-04 | Verified on Node 24 locally — installed 24.19.0, clean `npm ci`, full gate set — rather than pushing and reading CI. A Node major is the case where a native dependency breaks, and a CI round-trip is a slower way to learn that than five minutes on the machine. | +| 2026-08-04 | Left npm 11's gated dependency install scripts (`esbuild`, `fsevents`, `unrs-resolver`) **blocked rather than allowlisted**. The project's own `postinstall` still runs, so `nuxt prepare` is unaffected; a clean `npm ci` with all four blocked passes every gate, and `allowScripts` would be a standing grant to run arbitrary install-time code for no benefit. | +| 2026-08-04 | Deleted `nuxt-app/.npmrc` as part of the Node move rather than as its own item: npm 11 promotes its two pnpm-only no-ops to deprecation warnings on every command, including in Vercel's build log, and this PR is what puts npm 11 in CI. | | 2026-08-03 | SDK 22's `RequestError` refactor was treated as the one real risk and checked at runtime, not by reading. `isTransientError()` casts to `{ response?: { status?: number } }`, so a shape change would compile, pass every gate, and silently stop prerender retries under `prerender.failOnError` — one flaky CMS response would then abort a deploy. `RequestError` preserves `.response`; confirmed across 7 transient codes, 5 permanent codes and a connection refusal. | diff --git a/nuxt-app/.npmrc b/nuxt-app/.npmrc deleted file mode 100644 index cf040424..00000000 --- a/nuxt-app/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -shamefully-hoist=true -strict-peer-dependencies=false diff --git a/nuxt-app/.nvmrc b/nuxt-app/.nvmrc index 53d1c14d..a45fd52c 100644 --- a/nuxt-app/.nvmrc +++ b/nuxt-app/.nvmrc @@ -1 +1 @@ -v22 +24 diff --git a/nuxt-app/package.json b/nuxt-app/package.json index 931f07e8..8e5fbd4b 100644 --- a/nuxt-app/package.json +++ b/nuxt-app/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "engines": { - "node": "^22.19.0 || ^24.11.0 || >=26.0.0" + "node": "^24.11.0 || >=26.0.0" }, "scripts": { "build": "nuxt build", From afef04885f80c1382abe0649169a4fce81498109 Mon Sep 17 00:00:00 2001 From: Jan Gregor Emge-Triebel Date: Wed, 5 Aug 2026 16:03:14 +0200 Subject: [PATCH 2/2] Address review: sync lockfile engines, correct README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three review findings, all correct — and all of them declarations that a PR claiming "every declaration agrees" had missed. package-lock.json still carried ^22.19.0 || ... in packages[""].engines, so the next npm install would have rewritten it as unexplained noise in someone else's PR. Synced. README.md still said "Node.js version 19+ (CI uses v22, tested up to v24)", wrong on both counts, in the file a new contributor reads first. Replaced with a table, since the two trees genuinely differ now. The lockfile sync was produced under npm 10 on purpose. The same command under npm 11 rewrites 168 lines, stripping "dev": true from ~50 optional packages — npm 11's changed dev/optional classification, not a consequence of narrowing engines. That has semantic content (npm ci --omit=dev would install a different set), so it is logged for its own change rather than smuggled in here. Not fixed: .nvmrc's `24` is looser than engines' ^24.11.0. The reviewer is right, and the suggested fix is worse — .nvmrc supports no ranges, so pinning 24.11.0 would hold CI and developers on a superseded patch (CI resolved 24.18.0 today) with no security releases inside the major. The only real fix is engine-strict=true, which changes install behaviour for other people, so it is logged for a decision instead. Verified on Node 24.19.0 after a clean npm ci: format clean, 91 tests, lint 0 errors/127 warnings, ratchet steady at 263, build exit 0. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JkKMceYAzAYLrSyC42FWTf --- README.md | 12 +++++- docs/dependency-upgrade-plan.md | 66 +++++++++++++++++++++++++++++++++ nuxt-app/package-lock.json | 2 +- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d2a37d5f..c56bd888 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,17 @@ Source code of: [https://www.programmier.bar/](https://www.programmier.bar/) ## Setup -These install instructions assume you are using Node.js version 19+ (CI uses v22, tested up to v24). +**The two trees need different Node versions.** Run `nvm use` (or your manager's equivalent) inside the +directory you are working in: -**Prerequisites for Node 22+:** +| tree | Node | source of truth | +| --- | --- | --- | +| `nuxt-app` | **24** | `nuxt-app/.nvmrc`, which CI reads too. `engines.node` requires `^24.11.0 \|\| >=26.0.0` | +| `directus-cms` | 22 | its workflows; deliberately left behind while the tree is frozen | + +Node 24 is also what production runs — Vercel resolves it from `nuxt-app/package.json`'s `engines.node`. + +**Prerequisites:** - Python 3 with setuptools (for native module compilation) - On macOS: `brew install python-setuptools` diff --git a/docs/dependency-upgrade-plan.md b/docs/dependency-upgrade-plan.md index 8ea9f7a5..ce13a8b7 100644 --- a/docs/dependency-upgrade-plan.md +++ b/docs/dependency-upgrade-plan.md @@ -1690,6 +1690,50 @@ letting CI find out: `prettier:check` clean, **91 tests passing**, `lint` 0 erro `SKIP_PRERENDER_ROUTE_DISCOVERY=true npm run build` exit 0. `npm ci` under npm 11 left `package-lock.json` byte-identical. +### Three review findings, all valid + +Reviewers caught three things this PR's own "all declarations agree" claim had missed: + +1. **`package-lock.json` still carried the old range** in `packages[""].engines`. Real: the lockfile + mirrors the manifest, so it read `^22.19.0 || …` while `package.json` read `^24.11.0 || …`, and the + next `npm install` would have rewritten it as unexplained noise inside someone else's PR. Synced. +2. **`README.md` still said "Node.js version 19+ (CI uses v22, tested up to v24)"** — wrong on both + counts, and the file a new contributor reads first. Replaced with a table naming 24 for `nuxt-app` + and 22 for `directus-cms`, since the two trees genuinely differ now. +3. **`.nvmrc: 24` is looser than `engines: ^24.11.0`** — it permits 24.0.0–24.10.x. Correct, and it + cannot be fixed the obvious way; see below. + +#### `.nvmrc` cannot express the floor, and pinning a patch would be worse + +The suggested fix is to put `24.11.0` in `.nvmrc`. That trades a narrow problem for a permanent one: +the format supports no ranges, so an exact version pins both developers *and* CI to a superseded patch — +CI would install 24.11.0 instead of the 24.18.0 it resolved today, and stop picking up Node security +releases within the major. There is no `.nvmrc` value meaning "≥24.11 inside major 24". + +What actually happens: both resolvers pick the newest available 24.x, so `.nvmrc: 24` yields a version +satisfying `engines` in every normal case. The residual gap is a developer who *already* has an old +24.x installed — `nvm use` would select it, and npm only **warns** (`EBADENGINE`). + +**The one way to make the floor binding is `engine-strict=true`**, which promotes that warning to a hard +install failure. Not added here: this PR's claim is that nothing about the runtime or install behaviour +changes, and `engine-strict` would fail a colleague's `npm install` rather than warn. It is a one-line +`.npmrc` if wanted — and it would be an `.npmrc` containing an option that actually does something, +unlike the one this PR deletes. + +### Discovered: npm 11 wants to rewrite the lockfile's dev classification + +Syncing the lockfile under **npm 11** (Node 24) produced **168 changed lines**, not one: it strips +`"dev": true` from roughly fifty optional packages (`@emnapi/*` and similar). Under **npm 10** the same +command produces exactly the one-line `engines` change. So the churn is npm's changed dev/optional +classification, not a consequence of narrowing `engines`. + +Deliberately **not** taken here — it has semantic content (`npm ci --omit=dev` would install a different +set), and bundling fifty unverified reclassifications into a PR whose claim is "nothing changes" would +make that claim false. The one-line sync was produced under npm 10 for that reason. + +Worth knowing that this is already live and harmless: Vercel builds on Node 24, so **production has been +resolving this lockfile with npm 11 for some time** without trouble. Logged as its own item below. + ### Still outstanding: the Vercel project setting `nodeVersion` remains `20.x`, now overridden twice over. It is dashboard-only — the MCP surface exposes @@ -2015,6 +2059,24 @@ browser-support decision nobody made. empty `content` alone as out of scope.) - [ ] **Drop `@ubclaunchpad/vue-fathom`** (last publish April 2022, 1 usage). Fathom's own snippet is a few lines — inlining it removes a dependency entirely. See Appendix A. +- [ ] **Regenerate `nuxt-app/package-lock.json` under npm 11, deliberately.** Node 24 brings npm 11, + which classifies dev/optional dependencies differently: `npm install --package-lock-only` rewrites + **168 lines**, stripping `"dev": true` from ~50 optional packages (`@emnapi/*` and friends). The + same command under npm 10 changes one line. + + Not urgent, and **not broken today** — Vercel already builds on Node 24, so production has been + resolving the current lockfile with npm 11 without trouble. But it means anyone running + `npm install` on Node 24 gets a large incidental diff, which is how unrelated churn ends up inside + an unrelated PR. + + Wants its own change, because it is not cosmetic: `npm ci --omit=dev` would install a different + set afterwards. Verify a production-shaped install (`npm ci --omit=dev`) still yields a working + build before and after, rather than trusting the reclassification. +- [ ] **Consider `engine-strict=true`.** `engines.node` is currently advisory — npm warns `EBADENGINE` + and continues, so the `^24.11.0` floor is not actually enforced anywhere except by whichever Node + `.nvmrc` happens to select. One line in a new `nuxt-app/.npmrc` makes it binding. Deliberately left + out of the Node 24 alignment because it changes install behaviour for other people: a wrong local + Node would fail the install instead of warning. CI and Vercel are unaffected either way. - [ ] **Re-check the `brace-expansion` advisory.** It stopped being reported during Phase 4, but the package is **still 2.1.4, unchanged** — the advisory data moved, not our tree. Treat it as unresolved rather than fixed. @@ -2391,4 +2453,8 @@ Tracked so nobody has to rediscover them. None are urgent on their own. | 2026-08-04 | Verified on Node 24 locally — installed 24.19.0, clean `npm ci`, full gate set — rather than pushing and reading CI. A Node major is the case where a native dependency breaks, and a CI round-trip is a slower way to learn that than five minutes on the machine. | | 2026-08-04 | Left npm 11's gated dependency install scripts (`esbuild`, `fsevents`, `unrs-resolver`) **blocked rather than allowlisted**. The project's own `postinstall` still runs, so `nuxt prepare` is unaffected; a clean `npm ci` with all four blocked passes every gate, and `allowScripts` would be a standing grant to run arbitrary install-time code for no benefit. | | 2026-08-04 | Deleted `nuxt-app/.npmrc` as part of the Node move rather than as its own item: npm 11 promotes its two pnpm-only no-ops to deprecation warnings on every command, including in Vercel's build log, and this PR is what puts npm 11 in CI. | +| 2026-08-04 | Three review findings on the Node alignment all held up: the lockfile's `packages[""].engines` still carried the old range, `README.md` still told contributors Node 19+ with CI on 22, and `.nvmrc: 24` is looser than `engines: ^24.11.0`. A PR claiming "all declarations agree" had missed three declarations — worth recording, because the claim was the whole point of the PR. | +| 2026-08-04 | Synced the lockfile's engine metadata **under npm 10, producing a one-line diff**, after the same command under npm 11 rewrote 168 lines by stripping `"dev": true` from ~50 optional packages. Bundling fifty unverified reclassifications into a PR whose claim is "nothing changes" would have made the claim false. The npm 11 rewrite is logged as its own item. | +| 2026-08-04 | Declined the suggested `.nvmrc` fix of pinning `24.11.0`. The format supports no ranges, so an exact version pins CI and developers to a superseded patch — CI resolved 24.18.0 today and would have installed 24.11.0 instead, forgoing Node security releases inside the major. There is no `.nvmrc` value meaning "≥24.11 within 24"; the reviewer identified a real gap with no good fix at that layer. | +| 2026-08-04 | Left `engines.node` **advisory** rather than adding `engine-strict=true` alongside the Node move. It is the only way to make the floor binding, but it converts a colleague's wrong-Node warning into a failed install — a behaviour change for someone not party to the decision, and outside a PR whose claim is that install behaviour is unchanged. Logged for a decision. | | 2026-08-03 | SDK 22's `RequestError` refactor was treated as the one real risk and checked at runtime, not by reading. `isTransientError()` casts to `{ response?: { status?: number } }`, so a shape change would compile, pass every gate, and silently stop prerender retries under `prerender.failOnError` — one flaky CMS response would then abort a deploy. `RequestError` preserves `.response`; confirmed across 7 transient codes, 5 permanent codes and a connection refusal. | diff --git a/nuxt-app/package-lock.json b/nuxt-app/package-lock.json index abd3c189..dfa69e8d 100644 --- a/nuxt-app/package-lock.json +++ b/nuxt-app/package-lock.json @@ -46,7 +46,7 @@ "vue-tsc": "^3.3.9" }, "engines": { - "node": "^22.19.0 || ^24.11.0 || >=26.0.0" + "node": "^24.11.0 || >=26.0.0" } }, "node_modules/@algolia/cache-browser-local-storage": {