Skip to content

feat: support Node 26 - #89

Open
not-matthias wants to merge 3 commits into
mainfrom
cod-3391-support-nodejs-v26
Open

feat: support Node 26#89
not-matthias wants to merge 3 commits into
mainfrom
cod-3391-support-nodejs-v26

Conversation

@not-matthias

@not-matthias not-matthias commented Aug 27, 2026

Copy link
Copy Markdown
Member

Adds Node 26 to the CI matrix, ships a prebuild for its ABI (147), and moves the pinned toolchain onto it.

.nvmrc pins 26.8.1, not 26.8.0

The 26.8.0 release reports its own version as 26.8.0-alpha.0.0.0 — an upstream packaging slip, since the official binary tarball says the same thing. node-gyp derives the headers URL from process.version, so compiling the native addon against it 404s:

gyp http GET https://nodejs.org/download/release/v26.8.0-alpha.0.0.0/node-v26.8.0-alpha.0.0.0-headers.tar.gz
gyp http 404

That fires during plain pnpm install, because a package with a binding.gyp and no install script gets an implicit node-gyp rebuild. 26.8.1, released a day later, reports a clean 26.8.1 and has the same ABI, so pinning it is the whole fix.

The CI matrix entry is "26", which floats to the latest 26.x and so was never affected.

V8 14.6 turns a tolerated natives mismatch into a fatal abort

optimizeFunction / optimizeFunctionSync requested %OptimizeFunctionOnNextCall(fn) without first marking the function via %PrepareFunctionForOptimization(fn). V8 used to ignore the missing marker; as of V8 14.6 it is a fatal CHECK:

# Check failed: CheckMarkedForManualOptimization(isolate, *function).

The process dies with exit 133. This is mode-dependent, which is why it is easy to miss: analysis mode passes --no-opt, which discards the optimization request before the check runs. So simulation is fine and walltime aborts — as does any uninstrumented run under the runner's node shim, which still harvests --allow-natives-syntax.

mode Node 24 Node 26 before Node 26 after
simulation (--no-opt) ok ok ok
walltime ok abort ok
disabled, under shim ok abort ok

Unlike the version-string issue this is a real V8 change, and it reproduces on 26.8.1.

Worth a careful look at one detail: both natives calls have to stay inline in the exported helpers. My first attempt factored the new call into a shared helper, and rollup silently dropped the argument at the call site (prepareForOptimization()), because fn is only ever referenced inside an eval string. That produced a different fatal CHECK (IsJSFunction(args[0])). There is a comment on the code saying so.

The new test in packages/core/tests/optimization.integ.test.ts spawns a child with --allow-natives-syntax against the built package, since an aborting process cannot be observed from an in-process assertion. It was verified to fail on both bug variants before the fix.

Prebuild and supported-version list

build-native-addon gains --target 26.0.0 for ABI 147. SUPPORTED_NODE_MAJORS mirrors those targets by its own doc comment, so it gains 26 too — otherwise every Node 26 user gets an "unsupported version" annotation. nodeVersion.integ.test.ts asserted that 26 warns, so its cases move accordingly.

Verification

Run against real 22.22.2 / 24.19.0 / 26.8.1 toolchains, sequentially: install --frozen-lockfile, build, lint, typecheck, test, 17/17 bench scripts, and a shim replay using the runner's actual node.sh to confirm the injected V8 flags survive (the no-shim control does report missing flags, so the check is not vacuous). All three core test suites pass on 26.8.1.

The install step was re-run with lifecycle scripts explicitly enabled, since that is what exercises the implicit node-gyp rebuild — the path that produced the 404 above.

Two pre-existing items, unchanged here: benchmark.js-plugin/tests/index.integ.test.ts fails identically on all three versions when the repo is checked out as a git worktree (benchmark URIs pick up the worktree directory name), and packages/core/src/native_core/linux_perf/utils.h calls raw v8::String::Utf8Value in a --no-napi prebuild. The latter only bites when one working tree is reused across majors, because Node 24 does not export the 2-arg constructor; each CI job builds against its own Node, so CI is unaffected.

The first commit is an unrelated prettier ordering fix. The check job runs lint, typecheck and test but not format, so the drift went unnoticed and blocked the pre-commit hook.

Fixes COD-3391

The import ordering in this file drifted from the
prettier-plugin-organize-imports output. The CI check job runs lint,
typecheck and test but not format, so the drift was not caught and it
blocks the pre-commit hook.
The optimization helpers asked V8 to optimize a function with
%OptimizeFunctionOnNextCall without first marking it via
%PrepareFunctionForOptimization. V8 used to ignore the missing marker, but
since V8 14.6 (Node 26) the mismatch is a fatal CHECK failure that aborts
the process with exit code 133.

Analysis mode is unaffected because --no-opt discards the optimization
request before the check runs, so this only shows up in walltime mode and
in uninstrumented runs under the runner's node shim, which still harvests
--allow-natives-syntax.

Both natives calls have to stay inline in the exported helpers: a shared
helper that only touches `fn` inside an eval string looks like it ignores
its parameter, and the bundler drops the argument at the call site.

Refs COD-3391
Add Node 26 to the CI matrix, ship a prebuild for its ABI (147) and move
the pinned toolchain onto it. SUPPORTED_NODE_MAJORS mirrors the
build-native-addon targets, so it gains 26 as well.

.nvmrc pins 26.8.1 rather than 26.8.0: the 26.8.0 release reports its own
version as 26.8.0-alpha.0.0.0, and node-gyp derives the headers URL from
process.version, so building the native addon 404s on that release.

Fixes COD-3391
@not-matthias
not-matthias force-pushed the cod-3391-support-nodejs-v26 branch from e436aa0 to b414596 Compare August 27, 2026 16:58
@codspeed-hq

codspeed-hq Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging this PR will regress 22 benchmarks

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 26 improved benchmarks
❌ 22 regressed benchmarks
✅ 190 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory switch 2 432 B 13,384 B -96.77%
Simulation switch 1 202.1 µs 694.5 µs -70.9%
Simulation test_recursive_fibo_10 277.5 µs 723.8 µs -61.65%
Memory recursive fibo 15 with hooks 432 B 656 B -34.15%
Memory wait 1sec 14.3 KB 21.5 KB -33.41%
Memory wait 500ms 14.3 KB 21.5 KB -33.41%
Memory wait 500ms 20.3 KB 27.5 KB -25.97%
WallTime test sync baz 10 108 ns 144 ns -25%
WallTime switch 2 84 ns 108 ns -22.22%
WallTime test sync baz 10 96 ns 120 ns -20%
WallTime long body 218.5 µs 267.5 µs -18.32%
WallTime long body 218.4 µs 265.2 µs -17.62%
WallTime long body 218.6 µs 264.9 µs -17.48%
WallTime long body 217.4 µs 263.4 µs -17.46%
WallTime long body 218.5 µs 264.4 µs -17.36%
Memory test_recursive_cached_fibo_10 656 B 784 B -16.33%
Memory test sync baz 10 656 B 784 B -16.33%
Memory short body 656 B 784 B -16.33%
WallTime switch 1 72 ns 84 ns -14.29%
WallTime switch 2 84 ns 96 ns -12.5%
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing cod-3391-support-nodejs-v26 (b414596) with main (9338d9a)

Open in CodSpeed

@not-matthias
not-matthias marked this pull request as ready for review August 28, 2026 08:36
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown

Greptile Summary

Adds Node.js 26 support across the development toolchain, compatibility CI, native prebuild targets, and supported-version reporting.

  • Pins the repository toolchain to Node 26.8.1 and updates Node type definitions.
  • Adds ABI 147 prebuild generation and Node 26 example coverage.
  • Prepares functions before requesting V8 optimization and adds child-process integration tests for both optimization helpers.

Confidence Score: 5/5

The PR appears safe to merge, with Node 26 support consistently represented in CI, native prebuild targets, runtime support reporting, and V8 optimization handling.

The changed optimization sequence satisfies the newer V8 requirement, its abort-prone behavior is covered in spawned-process tests, and the Node 26 support declarations remain aligned with native build targets and CI coverage.

Important Files Changed

Filename Overview
packages/core/src/optimization.ts Adds the V8-required preparation call inline before optimization for both asynchronous and synchronous helpers.
packages/core/tests/optimization.integ.test.ts Verifies both built optimization helpers in child processes with native syntax enabled, allowing fatal V8 failures to surface.
packages/core/package.json Extends native prebuild generation to the Node 26 target while retaining Node 22 and 24 targets.
packages/core/src/nodeVersion.ts Aligns supported-version reporting with the expanded native prebuild target list.
.github/workflows/ci.yml Adds Node 26 to example build and benchmark compatibility coverage.
package.json Updates development Node typings to the Node 26 line without changing the published runtime engine floor.
pnpm-lock.yaml Synchronizes lockfile resolutions for the Node 26 type-definition update.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Node 26 toolchain] --> B[CI compatibility matrix]
  A --> C[Native addon build]
  C --> D[ABI 147 prebuild]
  D --> E[Core native binding]
  F[Benchmark callback] --> G[Prepare for optimization]
  G --> H[Warm-up calls]
  H --> I[Optimize on next call]
  I --> J[Measured benchmark execution]
Loading

Reviews (1): Last reviewed commit: "feat: support Node 26" | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant