From 7f19defca2891c70e59c225c105b9829e9b2dccf Mon Sep 17 00:00:00 2001 From: Dave Jong Date: Tue, 8 Sep 2026 10:25:17 +0200 Subject: [PATCH] Validate on Node 26, and take its type declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 26 is the current release line, and nothing checked this package against it. Added to the matrix rather than swapped in: 18 and 20 are past their upstream support and are kept deliberately, because this package exists to protect apps on whatever runtime a builder platform gives them — so the oldest runtime `engines` claims is where a regression matters most. That is not theoretical. The Node 18 job is what caught a detection field arriving empty there, because that runtime exposes no global `crypto`. The type declarations move with it. `@types/node` 26 no longer has a name for the platform-specific path surface, so `installLocation` takes the module's own type instead: `path.posix` and `path.win32` ARE that shape, which is what a caller passes, and it stays true however the declarations spell it. The parameter exists so the POSIX normalization can be tested at all — CI runs on ubuntu, where the separator is already right — and that is unchanged. Verified on 18, 20, 22 and 26: typecheck, the full suite and the build. --- .github/workflows/ci.yml | 6 ++++++ package-lock.json | 16 ++++++++-------- package.json | 2 +- src/parsers/node_modules.ts | 6 +++++- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec98a36d..1831dba4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,10 +178,16 @@ jobs: fail-fast: false matrix: node-version: + # The floor `engines` claims, through to the current release line. 18 and 20 are past their + # upstream support, and are kept deliberately: this package exists to protect apps on whatever + # runtime a builder platform happens to give them, so the oldest runtime it claims to support is + # the one where a regression matters most. That is not theoretical — the Node 18 job is what + # caught a detection field arriving empty there because the runtime exposes no global `crypto`. - 18.x - 20.x - 22.x - 24.x + - 26.x steps: - name: Checkout diff --git a/package-lock.json b/package-lock.json index 5139845d..71d32171 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "patchstack-connect": "dist/cli.js" }, "devDependencies": { - "@types/node": "^20.11.0", + "@types/node": "^26.5.0", "fastify": "^5.12.1", "tsup": "^8.0.0", "typescript": "^5.4.0", @@ -1004,13 +1004,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.5.0.tgz", + "integrity": "sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~8.9.0" } }, "node_modules/@vitest/expect": { @@ -2531,9 +2531,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 0ecc39ae..3714ecaa 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { - "@types/node": "^20.11.0", + "@types/node": "^26.5.0", "fastify": "^5.12.1", "tsup": "^8.0.0", "typescript": "^5.4.0", diff --git a/src/parsers/node_modules.ts b/src/parsers/node_modules.ts index 2c003ac8..5c7363d5 100644 --- a/src/parsers/node_modules.ts +++ b/src/parsers/node_modules.ts @@ -178,8 +178,12 @@ async function mapLimit(items: T[], fn: (item: T) => Promise): Promise< * — a guard no test can distinguish is one nobody can tell is still working. It has to be the whole * module rather than just the separator: POSIX `path.relative` cannot parse `C:\\app` either, so passing * a lone backslash would test nothing that resembles the Windows path. + * + * Typed as the module itself rather than by the name the type once had. `path.posix` and `path.win32` + * ARE that shape — the platform variants expose the same surface — so this is what a caller can pass, + * and it stays true however the type declarations spell it. */ -export function installLocation(root: string, pkgDir: string, impl: path.PlatformPath = path): string { +export function installLocation(root: string, pkgDir: string, impl: typeof path = path): string { return impl.relative(root, pkgDir).split(impl.sep).join('/'); }