feat: accept Next.js 16.4.4 on the native Node path

SO
1h ago
+3037-263

Stacked on #436. Native Node Functions accept Next.js 16.4.4 as well as 16.4.3.

Why

The native Node path qualified exact next@16.4.3 bytes and refused anything else. Since the scaffolder asks for ^16.4.x, a new native app now installs 16.4.4. Projects pinned to 16.4.3 must keep building, so both releases are supported.

What 16.4.4 changes for this path

Of every source the path pins, 16.4.4 changes only these:

  • package.json.
  • The twelve next-server runtimes. They require next/dist/compiled/fast-jwt where 16.4.3 required jsonwebtoken (#100885).
  • build/templates/app-page-runtime.js. The resume now emits the Turbopack bootstrap (#100907).

Every other reviewed source is byte-identical between the two releases, which I checked against both installed packages: the route modules, route-module.js, load-components.js, the evaluator, the templates, request-meta.js, route-kind.js, pages-handler.js, require-hook.js and the ws bundle.

Two more changes affect the path:

  • The Turbopack production runtime no longer installs a chunk's leading strict factory table (#100866).
  • The JWT library is now fast-jwt, which needs a profile of its own.

Changes

  • Per-release pins (node-next-release.ts).
    • Each release carries its package.json, runtime and template bytes. Lookups accept the reviewed bytes of either release.
    • Where the package is known, the bytes must be that package's own release. The Pages proof refuses a package that holds another release's copy of a runtime or of the template. The cache-policy rewrite, the app-page factory and the cache-write proof each check the release that package.json names.
    • The ws and require-hook profiles accept either release's package, since their bytes are the same in both.
  • Turbopack runtime.
    • 16.4.4's three body fingerprints are computed from the release's own snapshot. Running the same computation on 16.4.3's snapshot reproduces the existing three fingerprints exactly.
    • A runtime proof now reports whether the runtime installs strict factory tables. Next to a runtime that does not, a chunk that leads with such a table is refused.
  • fast-jwt profile.
    • The bundle has no eval and no Function.
    • Its one Node loader read is safer-buffer's process.binding("buffer"), guarded by !o.kStringMaxLength. Node 24 and 26 export kStringMaxLength, so that read never runs.
    • That one site is the only exemption granted to the closure walker. A test confirms the walker refuses nothing else in the bundle.
    • The bootstrap guard accepts the record, answers the node: builtins, and seals the crypto functions fast-jwt captures at load.
    • A CI-only test signs and verifies preview data through the guard.
  • Producer per release. The independent Pages handler producer compiles with 16.4.4 under --next=16.4.4. Its workflow now takes its pnpm version from the fixture: action-setup was refusing the pinned 12.15.0 next to the repository's packageManager, so no producer job had been running.
  • CI and fixtures.
    • tools/native-addon-next-ci.mts builds the official fixtures with a reviewed release named by UPWIND_NATIVE_NEXT (16.4.3 by default).
    • fixtures/next-native moves to 16.4.4.

Still to come in this PR

  • The Pages "proved" mode needs the 16.4.4 getHandler whole-factory references. They come from the producer's 16.4.4 artifacts, which cannot run until pnpm's one-day release-age default lets the fixture install 16.4.4 (after 2026-11-03 10:49 UTC).
    • Until those references land, a Pages-only native build on 16.4.4 is still refused.
    • App Router native Functions (the whole server output) build on 16.4.4 with this change.
  • Once the references land, I'll add the 16.4.4 leg to the native fixture matrix.

Checks run locally

  • 183 adapter and runtime suites around the native path pass, apart from three macOS-only environment failures (sharp's libvips and /var vs /private/var).
  • The fast-jwt guard integration test passes on Node 24.22.1.
  • tsc, ESLint and oxfmt pass on every changed file, and so do knip and check:vocabulary.

Review notes

  • Real fast-jwt qualification (Greptile, cubic). The unit suites never reach a positive JWT qualification, for jsonwebtoken either: that requires a completed route closure, which only a real native build produces. The official native fixture CI exercises it for 16.4.3, building prerender-native-module in the Pages "proved" mode with jsonwebtoken retained. The 16.4.4 leg of the same job will do it for fast-jwt once the getHandler references are in. Until then, the unit test refuses a fast-jwt route approval the same way it refuses a jsonwebtoken one, and the closure test shows the binding read is the bundle's only refusal.
  • Taken in ddfcad94.
    • The Pages proof's package.json must match the release's exact bytes, not just its version.
    • The nested-table probe is total, and the cohort reads the chunks for it once.
    • The JWT builtin lists are frozen.
    • The lock comparison allows integrity changes only in Next.js's own entries.
    • The docs state the pending Pages-only limitation and the 16.4.3 CI fixture.
    • The producer README is updated.

Summary by CodeRabbit

  • New Features
    • Added support for Next.js 16.4.4 alongside 16.4.3 in native integrations and Pages handler profiles.
    • Pages handler profiles can be generated with server minification enabled or disabled, and can target either supported Next.js version.
    • Added support for validating fast-jwt evaluation in the native integration environment.
  • Documentation
    • Updated compatibility and profile guidance for Next.js 16.4.4 and its JWT handling.