Context
Node 24 is the current active LTS (Node 22 moved to maintenance on
2026-04-22, Node 20 has entered end-of-life). Node 24 is the version
GitHub Actions runners default to for new Node.js actions.
This was attempted in #18 (bumping 20 → 22) and deliberately deferred
because bumping the types produced 17 hard type errors in
@opencodehub/sarif. The 24.x strictness is a superset, so the same
fixes apply and we should jump straight to 24 rather than land 22 and
re-bump again.
Runtime-support policy
We support Node 22 and Node 24. 22 is maintenance-line LTS with
bugfixes through 2027-04; 24 is the current active LTS. Node 20 is
end-of-life and no longer tested.
engines.node → >=22.0.0
- CI test matrix runs on both 22 and 24 across all three OSes
Why it breaks (TS4111)
@types/node@22+ ships stricter index-signature inference (carried
forward into 24.x). Properties accessed via . on a record whose type
is Record<string, T> or { [k: string]: T } must now use bracket
notation (record["key"]). This is the TS4111 diagnostic.
Concrete failures to fix
All are in packages/sarif/ (surfaced when the bump was attempted in
#18):
Total: 17 call sites across 4 files. Re-run pnpm -r exec tsc --noEmit
after the bump to catch anything new that surfaces under Node 24 types
specifically (fetch / Response / Headers / File lib-dom overlap
behavior tightened in 24.x).
Node 24-specific shifts to audit
fetch, Request, Response, Headers are built-in globals;
@types/node@24 no longer redeclares them. If any workspace depended
on the redeclaration (e.g. importing from undici purely for types),
those imports become redundant and should be removed.
File and Blob are globals.
require(esm) is now stable — we don't use it, but worth noting if
any loader shim was relying on require being CommonJS-only.
Two ways to fix the TS4111 errors
Option A (mechanical): Change x.foo → x["foo"] at each call
site. Minimal diff, preserves runtime behavior. Ugly but fast.
Option B (structural): Replace the passthrough Record<string, unknown>
shapes in packages/sarif/src/schemas.ts (SARIF properties bag) and
the suppressions schema with concrete Zod object schemas that declare
the fields explicitly. Those fields stop being index-signature
properties and the errors disappear without bracket-access ugliness.
Recommendation: Option B for suppressions.ts (the fields are
load-bearing and deserve concrete typing), Option A for the SARIF
passthrough bag (it's intentionally open-ended to preserve foreign
namespace properties — see the comment in schemas.ts).
CI test matrix — add Node 22 + 24
Current ci.yml test job only varies os, so mise-action resolves
whichever Node version mise.toml pins — one Node version across three
OSes. To actually test both supported Node versions:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [22, 24]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: jdx/mise-action@v4
with:
install_args: node@${{ matrix.node }}
- name: Ensure node-gyp is available for native tree-sitter build
run: npm i -g node-gyp
- run: pnpm install --frozen-lockfile
- run: pnpm -r test
That's 6 test jobs instead of 3. Also update the branch-protection
required-checks list to include the new job names
(test (ubuntu-latest, 22), test (ubuntu-latest, 24), etc.) via
gh api -X PUT /repos/theagenticguy/opencodehub/branches/main/protection.
Config files to update
Acceptance
Context
Node 24 is the current active LTS (Node 22 moved to maintenance on
2026-04-22, Node 20 has entered end-of-life). Node 24 is the version
GitHub Actions runners default to for new Node.js actions.
This was attempted in #18 (bumping 20 → 22) and deliberately deferred
because bumping the types produced 17 hard type errors in
@opencodehub/sarif. The 24.x strictness is a superset, so the samefixes apply and we should jump straight to 24 rather than land 22 and
re-bump again.
Runtime-support policy
We support Node 22 and Node 24. 22 is maintenance-line LTS with
bugfixes through 2027-04; 24 is the current active LTS. Node 20 is
end-of-life and no longer tested.
engines.node→>=22.0.0Why it breaks (TS4111)
@types/node@22+ships stricter index-signature inference (carriedforward into 24.x). Properties accessed via
.on a record whose typeis
Record<string, T>or{ [k: string]: T }must now use bracketnotation (
record["key"]). This is theTS4111diagnostic.Concrete failures to fix
All are in
packages/sarif/(surfaced when the bump was attempted in#18):
src/fingerprint.test.ts:145:49—.primaryLocationLineHashsrc/schema-validation.test.ts:53:32—.primaryLocationLineHashsrc/suppressions.ts:113:26—.rulessrc/suppressions.ts:113:42—.suppressionssrc/suppressions.ts:124:25—.ruleIdsrc/suppressions.ts:125:34—.filePathPatternsrc/suppressions.ts:126:25—.reasonsrc/suppressions.ts:127:28—.expiresAtsrc/suppressions.test.ts— 8 more.suppressionsaccesses(lines 45, 88, 119, 126, 138, 150, 159, 175, 216)
Total: 17 call sites across 4 files. Re-run
pnpm -r exec tsc --noEmitafter the bump to catch anything new that surfaces under Node 24 types
specifically (
fetch/Response/Headers/Filelib-dom overlapbehavior tightened in 24.x).
Node 24-specific shifts to audit
fetch,Request,Response,Headersare built-in globals;@types/node@24no longer redeclares them. If any workspace dependedon the redeclaration (e.g. importing from
undicipurely for types),those imports become redundant and should be removed.
FileandBlobare globals.require(esm)is now stable — we don't use it, but worth noting ifany loader shim was relying on
requirebeing CommonJS-only.Two ways to fix the TS4111 errors
Option A (mechanical): Change
x.foo→x["foo"]at each callsite. Minimal diff, preserves runtime behavior. Ugly but fast.
Option B (structural): Replace the passthrough
Record<string, unknown>shapes in
packages/sarif/src/schemas.ts(SARIF properties bag) andthe suppressions schema with concrete Zod object schemas that declare
the fields explicitly. Those fields stop being index-signature
properties and the errors disappear without bracket-access ugliness.
Recommendation: Option B for
suppressions.ts(the fields areload-bearing and deserve concrete typing), Option A for the SARIF
passthrough bag (it's intentionally open-ended to preserve foreign
namespace properties — see the comment in
schemas.ts).CI test matrix — add Node 22 + 24
Current
ci.ymltestjob only variesos, somise-actionresolveswhichever Node version
mise.tomlpins — one Node version across threeOSes. To actually test both supported Node versions:
That's 6 test jobs instead of 3. Also update the branch-protection
required-checks list to include the new job names
(
test (ubuntu-latest, 22),test (ubuntu-latest, 24), etc.) viagh api -X PUT /repos/theagenticguy/opencodehub/branches/main/protection.Config files to update
mise.toml—node = "22"→node = "24"(local dev default).nvmrc/.node-version—22→24package.jsonengines.node→">=22.0.0".github/workflows/ci.ymltestmatrix — addnode: [22, 24]axisgh apiAcceptance
@types/nodeat24.12.x(latest 24 LTS point-release) in everyworkspace
package.jsonmise.toml+.nvmrc+.node-versionpin Node 24 for local devengines.nodeat">=22.0.0"in rootpackage.jsonnode: [22, 24]× all three OSes (6 jobs)pnpm -r buildcleanpnpm -r exec tsc --noEmitclean (expect ~17 TS4111 fixes)pnpm -r testgreen on all 6 test matrix jobsosv-scannerstill clean on refreshed lockfileSBOM.cdx.json+THIRD_PARTY_LICENSES.md