Skip to content

fix(ci): pin @types/node to restore dtslint, add Node 26 to matrix - #743

Merged
const-cloudinary merged 1 commit into
masterfrom
fix-ci
Aug 19, 2026
Merged

fix(ci): pin @types/node to restore dtslint, add Node 26 to matrix#743
const-cloudinary merged 1 commit into
masterfrom
fix-ci

Conversation

@const-cloudinary

Copy link
Copy Markdown
Member

Problem

Every CI job — all 9 Node versions — fails at the dtslint step. Unit tests pass first (798–799 passing, 0 failing), then:

Error: Errors in typescript@local for external dependencies:
../node_modules/@types/node/web-globals/streams.d.ts(10,9): error TS2502: 'CompressionStream' is referenced directly or indirectly in its own type annotation.
../node_modules/@types/node/web-globals/streams.d.ts(17,9): error TS2502: 'DecompressionStream' is referenced directly or indirectly in its own type annotation.

This affects master and therefore every open PR. No repository change caused it.

Root cause

@types/node@22.19.0 introduced a web-globals/ directory. Its streams.d.ts declares globals through a conditional-type / interface+var merge:

interface CompressionStream extends _CompressionStream {}
var CompressionStream: typeof globalThis extends { onmessage: any; CompressionStream: infer T } ? T
    : typeof webstreams.CompressionStream;

TypeScript 4.9's checker cannot resolve this and reports TS2502; TS 5.x resolves it fine. @types/node was declared as ^22.9.3, so the caret resolved to the newest 22.x (22.20.1) and the break appeared on its own. master last went green on 2026-06-18, before 22.19.0 was published.

@types/node does ship a ts5.6/ fallback for older TypeScript, but its typesVersions map only reaches down to <=5.6 — TS 4.9 is below that floor, and dtslint resolves the root web-globals/ files directly, so the fallback cannot help.

Fix

Pin @types/node to ~22.18.0, keeping it below 22.19.0 where the offending file does not exist. The tilde also prevents the same silent drift from recurring.

This is a dev-only change with no production impact: @types/node and typescript are both devDependencies (the sole runtime dependency is lodash), neither is in package.json.files, and the package ships plain CommonJS — TypeScript never compiles shipped code.

Verification

The failure is dependency-resolution, not Node-version specific. Confirmed in clean Docker containers, changing only the @types/node version:

Node @types/node 22.20.1 ~22.18.0 (this PR)
9 ❌ TS2502 ✅ pass
12 ✅ pass
26 ❌ TS2502 ✅ pass

Installing from the edited package.json resolves @types/node to 22.18.13 with web-globals/ absent. npm run lint, npm run test:unit (315 passing) and npm run dtslint all pass.

Node 10, 14, 16, 18, 20, 22 and 24 were not tested individually; given identical behaviour at 9, 12 and 26 and that the mechanism is a missing file rather than a runtime feature, they carry no expected risk. Integration tests were already passing before the dtslint step.

Also included

Node 26 added to the test matrix, verified green (full suite in a clean node:26 container).

Considered and rejected

Upgrading to TypeScript 5.x also fixes the type error and is the better long-term direction, but TS 5.9 declares engines: { node: ">=14.17" } — it would break the Node 9, 10 and 12 legs. That needs a supported-versions decision (ask-first per AGENTS.md) and belongs in its own PR.

Follow-up worth considering (not in this PR)

package-lock.json is gitignored (.gitignore:15), so CI never sees it, and the install step is npm ci || npm i — with no lockfile npm ci always fails and silently falls through to npm i, re-resolving carets against the live registry on every run. That is how this broke with no repository change. This PR closes the current instance; committing the lockfile and using plain npm ci would close the class, but it pins all transitive dependencies and can surface unrelated version changes, so it deserves its own PR.

🤖 Generated with Claude Code

All CI jobs were failing at the dtslint step with TS2502 errors in
@types/node's web-globals/streams.d.ts:

  error TS2502: 'CompressionStream' is referenced directly or indirectly
  in its own type annotation.

@types/node@22.19.0 introduced a web-globals/ directory whose
streams.d.ts declares globals via a conditional-type/interface merge that
TypeScript 4.9 cannot resolve (TS 5.x can). Since @types/node was declared
as ^22.9.3, the caret resolved to 22.20.1 and the failure appeared with no
change to this repository. Pinning to ~22.18.0 keeps @types/node below
22.19.0, where the offending file does not exist.

Verified in clean containers that the failure reproduces and the pin fixes
it on Node 9, 12 and 26; lint, unit tests and dtslint pass.

Also add Node 26 to the test matrix, verified green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@const-cloudinary
const-cloudinary merged commit b86b32b into master Aug 19, 2026
10 checks passed
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.

2 participants