Merge pull request #2578 from magyargergo/require-node-22.18

fix(deps)!: require Node >=22.18 (keep Babel 8) and drop @types/uuid stub
This commit is contained in:
Gergő Magyar 2026-07-21 11:51:10 +01:00 • committed by GitHub
commit 276fb5fda5
18 changed files with 48 additions and 58 deletions

View file

@ -11,7 +11,7 @@
"@anthropic-ai/claude-code": "2.1.214"
},
"engines": {
"node": "22.16.0"
"node": "22.18.0"
}
},
"node_modules/@anthropic-ai/claude-code": {

View file

@ -3,7 +3,7 @@
"version": "0.0.0",
"private": true,
"engines": {
"node": "22.16.0"
"node": "22.18.0"
},
"dependencies": {
"@anthropic-ai/claude-code": "2.1.214"

View file

@ -11,7 +11,7 @@
"gitnexus": "1.6.9"
},
"engines": {
"node": "22.16.0"
"node": "22.18.0"
}
},
"node_modules/@emnapi/runtime": {

View file

@ -3,7 +3,7 @@
"private": true,
"version": "1.0.0",
"engines": {
"node": "22.16.0"
"node": "22.18.0"
},
"dependencies": {
"gitnexus": "1.6.9"

View file

@ -378,15 +378,16 @@ jobs:
"$PREFIX/bin/gitnexus" --version
fi
# Node engines-floor gate (#2372). The embedding resolvers statically named
# `module.registerHooks`, which only exists on Node >= 22.15 / >= 23.5, so on
# the supported floor (engines: >=22.0.0) those ESM modules failed to LINK —
# a class vitest/tsx transforms structurally mask, and the default
# `node-version: 22` (resolves to latest) never hits. Build the dist on 22.x,
# then import-link every module R1 names as a load surface on a pinned 22.14
# so a regression fails here instead of shipping to users on that Node range.
# Node engines-floor gate (#2372). A module that statically names an API
# newer than the supported floor (e.g. `module.registerHooks`, added in
# 22.15) fails to LINK on the floor — a class vitest/tsx transforms
# structurally mask, and the default `node-version: 22` (resolves to latest)
# never hits. Build the dist on 22.x, then import-link every module R1 names
# as a load surface on the pinned engines floor (22.18.0, per package.json
# `engines: ^22.18.0 || >=24.11.0`) so a regression fails here instead of
# shipping to users on the minimum supported Node.
node-floor-compat:
name: node floor compat (22.14)
name: node floor compat (22.18)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
@ -415,14 +416,14 @@ jobs:
# (so no package-manager cache is needed).
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22.14.0'
node-version: '22.18.0'
package-manager-cache: false
- name: Import-link the built dist on Node 22.14
- name: Import-link the built dist on Node 22.18
shell: bash
run: |
set -euo pipefail
node --version
node --version | grep -q '^v22\.14\.' || { echo "expected Node 22.14.x" >&2; exit 1; }
node --version | grep -q '^v22\.18\.' || { echo "expected Node 22.18.x" >&2; exit 1; }
for m in \
core/embeddings/runtime-install \
core/embeddings/onnxruntime-node-resolver \
@ -556,7 +557,7 @@ jobs:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22.16.0'
node-version: '22.18.0'
cache: npm
cache-dependency-path: |
gitnexus/package-lock.json

View file

@ -325,7 +325,7 @@ jobs:
if: steps.context.outputs.ready == 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22.16.0'
node-version: '22.18.0'
- name: Install and preflight Claude subprocess isolation
id: isolation
@ -377,7 +377,7 @@ jobs:
.github/claude-canary-runtime/package-lock.json \
"${runtime_dir}/package-lock.json"
printf '%s\n' 'registry=https://registry.npmjs.org/' 'audit=false' 'fund=false' > "${npmrc}"
test "$(node --version)" = 'v22.16.0'
test "$(node --version)" = 'v22.18.0'
test "$(uname -m)" = 'x86_64'
# The trusted lock and these independent receipts pin both the thin
@ -398,7 +398,7 @@ jobs:
if (
lock.lockfileVersion !== 3 ||
lock.packages?.['']?.dependencies?.['@anthropic-ai/claude-code'] !== '2.1.214' ||
lock.packages?.['']?.engines?.node !== '22.16.0'
lock.packages?.['']?.engines?.node !== '22.18.0'
) {
throw new Error('Claude runtime lock root is not exact');
}
@ -506,7 +506,7 @@ jobs:
install -m 0600 .github/gitnexus-review-runtime/package.json "${runtime_dir}/package.json"
install -m 0600 .github/gitnexus-review-runtime/package-lock.json "${runtime_dir}/package-lock.json"
printf '%s\n' 'registry=https://registry.npmjs.org/' 'audit=false' 'fund=false' > "${npmrc}"
test "$(node --version)" = 'v22.16.0'
test "$(node --version)" = 'v22.18.0'
npm ci \
--prefix "${runtime_dir}" \
--userconfig "${npmrc}" \
@ -1241,7 +1241,7 @@ jobs:
CLAUDE_CONFIG_DIR: ${{ runner.temp }}/gitnexus-review-claude-config
CLAUDE_WORKING_DIR: ${{ runner.temp }}/gitnexus-review-control
NPM_CONFIG_IGNORE_SCRIPTS: 'true'
NODE_VERSION: '22.16.0'
NODE_VERSION: '22.18.0'
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
path_to_claude_code_executable: ${{ runner.temp }}/gitnexus-review-claude-runtime/node_modules/@anthropic-ai/claude-code/bin/claude.exe

View file

@ -110,7 +110,7 @@ jobs:
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22.16.0'
node-version: '22.18.0'
cache: npm
cache-dependency-path: |
gitnexus/package-lock.json

3
.gitignore vendored
View file

@ -68,9 +68,8 @@ gitnexus-web/test-results/
eval/.coverage
eval/.hypothesis/
# Local docs (docs/plans/ stays tracked — gitnexus-plan output travels with the work)
# Local docs — planning output (gitnexus-plan / gitnexus-work) stays local, not tracked
docs/*
!docs/plans/
gitnexus/test/fixtures/mini-repo/*.md
gitnexus/test/fixtures/mini-repo/.claude

View file

@ -13,7 +13,7 @@ This project uses the [PolyForm Noncommercial License 1.0.0](https://polyformpro
## Development setup
**Prerequisites:** Node.js — `gitnexus/` requires `>=22.0.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version.
**Prerequisites:** Node.js — `gitnexus/` requires `^22.18.0 || >=24.11.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version.
1. Clone the repository.
2. **Shared package:** `cd gitnexus-shared && npm install && npm run build`

View file

@ -172,7 +172,7 @@ def test_eval_ci_uses_locked_uv_and_blocking_native_containment_jobs():
}
assert containment["timeout-minutes"] == 20
assert containment_node_setup["with"] == {
"node-version": "22.16.0",
"node-version": "22.18.0",
"cache": "npm",
"cache-dependency-path": "gitnexus/package-lock.json\ngitnexus-shared/package-lock.json\n",
}

View file

@ -60,7 +60,6 @@
"@types/express": "^5.0.6",
"@types/js-yaml": "^4.0.9",
"@types/node": "^26.0.0",
"@types/uuid": "^11.0.0",
"@vitest/coverage-v8": "^4.0.18",
"gitnexus-shared": "file:../gitnexus-shared",
"tsx": "^4.0.0",
@ -68,7 +67,7 @@
"vitest": "^4.0.18"
},
"engines": {
"node": ">=22.0.0"
"node": "^22.18.0 || >=24.11.0"
},
"optionalDependencies": {
"@huggingface/transformers": "^4.1.0",
@ -1990,17 +1989,6 @@
"@types/node": "*"
}
},
"node_modules/@types/uuid": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz",
"integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==",
"deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.",
"dev": true,
"license": "MIT",
"dependencies": {
"uuid": "*"
}
},
"node_modules/@vitest/coverage-v8": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz",

View file

@ -107,7 +107,6 @@
"@types/express": "^5.0.6",
"@types/js-yaml": "^4.0.9",
"@types/node": "^26.0.0",
"@types/uuid": "^11.0.0",
"@vitest/coverage-v8": "^4.0.18",
"gitnexus-shared": "file:../gitnexus-shared",
"tsx": "^4.0.0",
@ -120,6 +119,6 @@
}
},
"engines": {
"node": ">=22.0.0"
"node": "^22.18.0 || >=24.11.0"
}
}

View file

@ -3,8 +3,10 @@
*
* `module.registerHooks` — the synchronous ESM/CJS resolution-hook API the
* embedding-stack resolvers rely on — was added in Node 22.15.0 (and 23.5.0 on
* the 23.x line). The gitnexus engines floor is `>=22.0.0`, which admits Node
* 22.0–22.14 AND 23.0–23.4, where the export is absent.
* the 23.x line). The gitnexus engines floor is `^22.18.0 || >=24.11.0`, so
* every supported runtime exposes it — but `engines` is advisory (not
* engine-strict), so a below-floor Node (22.0–22.14, or the unsupported
* 23.0–23.4 line) can still run, where the export is absent.
*
* In this `"type": "module"` package, a *static named* import of a missing
* builtin export (`import { registerHooks } from 'node:module'`) is a

View file

@ -52,8 +52,8 @@
* per-resolution cost is a single string comparison.
*
* `module.registerHooks` is marked `@experimental` and requires Node >= 22.15
* (the gitnexus engines floor is >= 22.0.0). On older runtimes it is absent and
* this is a graceful no-op: embeddings then resolve onnxruntime-common exactly
* (below the gitnexus engines floor of `^22.18.0 || >=24.11.0`). On below-floor
* runtimes it is absent and this is a graceful no-op: embeddings then resolve onnxruntime-common exactly
* as before — fine on hoisted layouts. Any failure during installation is
* swallowed.
*/
@ -100,9 +100,9 @@ export const ensureOnnxRuntimeCommonResolvable = (): void => {
attempted = true;
try {
// Node < 22.15 / < 23.5 (the gitnexus engines floor is >= 22.0.0): no
// synchronous hooks API. Degrade gracefully — the import still works on
// hoisted layouts.
// Node < 22.15 / < 23.5 (below the gitnexus engines floor of
// ^22.18.0 || >=24.11.0): no synchronous hooks API. Degrade gracefully —
// the import still works on hoisted layouts.
const registerHooks = getRegisterHooks();
if (typeof registerHooks !== 'function') return;

View file

@ -36,8 +36,8 @@
* So CUDA-12 hosts, Windows (DirectML), macOS, and CPU-only hosts are
* untouched. Idempotent; any failure is swallowed and leaves the default
* resolution exactly as before. `module.registerHooks` requires Node >= 22.15
* (the gitnexus engines floor is >= 22.0.0); on older runtimes the redirect is
* a no-op, but the default copy's CUDA major is still probed so an
* (below the gitnexus engines floor of `^22.18.0 || >=24.11.0`); on below-floor
* runtimes the redirect is a no-op, but the default copy's CUDA major is still probed so an
* already-matching host (e.g. CUDA 12 + transformers' CUDA-12 build) keeps
* auto-selecting the GPU.
* `npm link` / symlinked local-dev checkouts are a known caveat: `resolveOurOrtNodeDir`/

View file

@ -191,7 +191,7 @@ export const ensureEmbeddingStackResolvable = (): void => {
hookAttempted = true;
try {
// Node < 22.15 / < 23.5 (engines floor is >= 22.0.0): no synchronous hooks
// Node < 22.15 / < 23.5 (below the engines floor of ^22.18.0 || >=24.11.0): no synchronous hooks
// API. Degrade gracefully — normally-installed stacks still resolve; only
// the runtime-prefix fallback is unavailable. Reachable now that the import
// is a namespace access (see node-module-compat.ts) rather than a static

View file

@ -2,8 +2,9 @@ import { describe, it, expect, vi, afterEach } from 'vitest';
/**
* Tests for the #2372 `node:module` compat seam. `module.registerHooks` was
* added in Node 22.15 / 23.5, but the engines floor is >=22.0.0, so on
* 22.0–22.14 and 23.0–23.4 the export is absent. `getRegisterHooks()` must
* added in Node 22.15 / 23.5. The engines floor is ^22.18.0 || >=24.11.0 (all
* >=22.15), but engines is advisory, so a below-floor 22.0–22.14 / 23.0–23.4
* runtime can still run, where the export is absent. `getRegisterHooks()` must
* hand back the real function when present and `undefined` when not — the value
* the resolver guards degrade on. `isPrefixRuntimeLoadable()` (exported from
* runtime-install.ts so CLI code never imports the compat module) is the

View file

@ -589,12 +589,12 @@ describe('gitnexus review-agent workflow security contract', () => {
}
expect(runtimePackage.dependencies?.gitnexus).toBe('1.6.9');
expect(runtimePackage.engines?.node).toBe('22.16.0');
expect(runtimePackage.engines?.node).toBe('22.18.0');
expect(runtimeLock.packages?.['node_modules/gitnexus']?.version).toBe('1.6.9');
expect(runtimeLock.packages?.['node_modules/gitnexus']?.integrity).toMatch(/^sha512-/);
expect(workflow).not.toMatch(/gitnexus@(latest|next|beta)/);
expect(workflow).toContain("node-version: '22.16.0'");
expect(workflow).toContain('test "$(node --version)" = \'v22.16.0\'');
expect(workflow).toContain("node-version: '22.18.0'");
expect(workflow).toContain('test "$(node --version)" = \'v22.18.0\'');
expect(workflow).toContain('npm ci');
expect(workflow).not.toContain('--package-lock=false');
expect(workflow).toContain(
@ -682,7 +682,7 @@ describe('gitnexus review-agent workflow security contract', () => {
'${{ runner.temp }}/gitnexus-review-claude-runtime/node_modules/@anthropic-ai/claude-code/bin/claude.exe';
expect(claudeRuntimePackage.dependencies?.['@anthropic-ai/claude-code']).toBe('2.1.214');
expect(claudeRuntimePackage.engines?.node).toBe('22.16.0');
expect(claudeRuntimePackage.engines?.node).toBe('22.18.0');
expect(claudeRuntimeLock.lockfileVersion).toBe(3);
expect(claudeRuntimeLock.packages?.['node_modules/@anthropic-ai/claude-code']).toMatchObject({
version: '2.1.214',
@ -1119,7 +1119,7 @@ describe('gitnexus review-agent workflow security contract', () => {
'CLAUDE_CONFIG_DIR: ${{ runner.temp }}/gitnexus-review-claude-config',
);
expect(analyze).toContain('CLAUDE_WORKING_DIR: ${{ runner.temp }}/gitnexus-review-control');
expect(analyze).toContain("NODE_VERSION: '22.16.0'");
expect(analyze).toContain("NODE_VERSION: '22.18.0'");
expect(analyze).toContain('checkout-index --all --force');
expect(analyze).toContain('find "${review_dir}" -type l -print0');
expect(analyze).toContain('Escaping copied review symlink');