From 0383c9f7776ee9660629a2f80ab5803e7ac2a7c9 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 6 Oct 2026 12:48:19 +0200 Subject: [PATCH 1/3] fix: support Node.js 24 and 26 Signed-off-by: Matteo Collina --- build/replacements.mjs | 14 ++++++++++++++ lib/internal/streams/legacy.js | 7 ++++++- .../test-stream-pipe-without-listenerCount.js | 3 ++- .../test-stream-pipe-without-listenerCount.js | 3 ++- test/common/index.js | 1 + test/parallel/test-stream-base-typechecking.js | 4 ++-- 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/build/replacements.mjs b/build/replacements.mjs index e5d97ecc7..710a7b73b 100644 --- a/build/replacements.mjs +++ b/build/replacements.mjs @@ -55,6 +55,11 @@ const internalStreamsRequireErrors = ["require\\('internal/errors'\\)", "require const internalStreamsRequireEventEmitter = ['const EE =', 'const { EventEmitter: EE } ='] +const internalStreamsLegacyListenerCount = [ + "EE\\.listenerCount\\(this, 'error'\\) === 0", + "this.listenerCount?.('error') === 0" +] + const internalStreamsRequirePrimordials = ['= primordials', "= require('../../ours/primordials')"] const internalStreamsRequireRelativeUtil = [ @@ -157,6 +162,7 @@ const testCommonKnownGlobals = [ typeof AbortSignal !== 'undefined' ? AbortSignal : require('abort-controller').AbortSignal, typeof EventTarget !== 'undefined' ? EventTarget : require('event-target-shim').EventTarget, typeof navigator !== 'undefined' ? navigator : {}, + typeof sessionStorage !== 'undefined' ? sessionStorage : {}, ` ] @@ -214,6 +220,12 @@ const testParallelReadableBufferListInspect = [ const testParallelRequireStream = ["require\\('stream'\\)", "require('../../lib/ours/index')"] +const testParallelStreamBaseTypecheckingError = [ + "code: 'ERR_INVALID_ARG_TYPE',\\n\\s+message: 'Second argument must be a buffer'", + `code: /^ERR_(?:INVALID_ARG_TYPE|UNKNOWN_ENCODING)$/, + message: /^(?:Second argument must be a buffer|Unknown encoding: buffer)$/` +] + const testParallelRequireStreamConsumer = ["require\\('stream/consumer'\\)", "require('../../lib/stream/consumer')"] const testParallelRequireStreamInternals = ["require\\('(internal/.+)'\\)", "require('../../lib/$1')"] @@ -317,6 +329,7 @@ export const replacements = { removetoWebReadableMethod, stringDecoderRequirePackage ], + 'lib/internal/streams/legacy.js': [internalStreamsLegacyListenerCount], 'lib/internal/streams/.+': [ internalStreamsRequireErrors, internalStreamsRequireEventEmitter, @@ -358,6 +371,7 @@ export const replacements = { 'test/common/index.js': [testCommonKnownGlobals], 'test/parallel/.+': [ testParallelIncludeTap, + testParallelStreamBaseTypecheckingError, testParallelRequireStream, testParallelRequireStreamConsumer, testParallelRequireStreamInternals, diff --git a/lib/internal/streams/legacy.js b/lib/internal/streams/legacy.js index d492f7ff4..8b22b400c 100644 --- a/lib/internal/streams/legacy.js +++ b/lib/internal/streams/legacy.js @@ -42,8 +42,13 @@ Stream.prototype.pipe = function (dest, options) { // Don't leave dangling pipes when there are errors. function onerror(er) { + var _this$listenerCount cleanup() - if (EE.listenerCount(this, 'error') === 0) { + if ( + ((_this$listenerCount = this.listenerCount) === null || _this$listenerCount === undefined + ? undefined + : _this$listenerCount.call(this, 'error')) === 0 + ) { this.emit('error', er) } } diff --git a/src/test/browser/test-stream-pipe-without-listenerCount.js b/src/test/browser/test-stream-pipe-without-listenerCount.js index 1e8238cd8..79278ec65 100644 --- a/src/test/browser/test-stream-pipe-without-listenerCount.js +++ b/src/test/browser/test-stream-pipe-without-listenerCount.js @@ -10,13 +10,14 @@ module.exports = function (t) { read: function () {} }) r.listenerCount = undefined + r.on('error', function () {}) const w = new Stream() w.on('pipe', function () { r.emit('error', new Error('Readable Error')) }) - t.throws(() => r.pipe(w), 'TypeError: this.listenerCount is not a function') + t.doesNotThrow(() => r.pipe(w)) } module.exports[kReadableStreamSuiteName] = 'stream-pipe-without-listenerCount' diff --git a/test/browser/test-stream-pipe-without-listenerCount.js b/test/browser/test-stream-pipe-without-listenerCount.js index 3b498a09b..292eaa78e 100644 --- a/test/browser/test-stream-pipe-without-listenerCount.js +++ b/test/browser/test-stream-pipe-without-listenerCount.js @@ -8,10 +8,11 @@ module.exports = function (t) { read: function () {} }) r.listenerCount = undefined + r.on('error', function () {}) const w = new Stream() w.on('pipe', function () { r.emit('error', new Error('Readable Error')) }) - t.throws(() => r.pipe(w), 'TypeError: this.listenerCount is not a function') + t.doesNotThrow(() => r.pipe(w)) } module.exports[kReadableStreamSuiteName] = 'stream-pipe-without-listenerCount' diff --git a/test/common/index.js b/test/common/index.js index 3e9b4c3ea..2c47c28da 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -255,6 +255,7 @@ let knownGlobals = [ typeof AbortSignal !== 'undefined' ? AbortSignal : require('abort-controller').AbortSignal, typeof EventTarget !== 'undefined' ? EventTarget : require('event-target-shim').EventTarget, typeof navigator !== 'undefined' ? navigator : {}, + typeof sessionStorage !== 'undefined' ? sessionStorage : {}, atob, btoa, clearImmediate, diff --git a/test/parallel/test-stream-base-typechecking.js b/test/parallel/test-stream-base-typechecking.js index 070e7563f..67d4aa3fd 100644 --- a/test/parallel/test-stream-base-typechecking.js +++ b/test/parallel/test-stream-base-typechecking.js @@ -20,8 +20,8 @@ const server = net.createServer().listen( }, { name: 'TypeError', - code: 'ERR_INVALID_ARG_TYPE', - message: 'Second argument must be a buffer' + code: /^ERR_(?:INVALID_ARG_TYPE|UNKNOWN_ENCODING)$/, + message: /^(?:Second argument must be a buffer|Unknown encoding: buffer)$/ } ) client.destroy() From 8513f4c5ee53ecb7b305cff946a2322fd8b430d5 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 6 Oct 2026 18:02:36 +0200 Subject: [PATCH 2/3] ci: run browser tests with supported Node.js Signed-off-by: Matteo Collina --- .github/workflows/browsers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/browsers.yml b/.github/workflows/browsers.yml index 4bf2ab12e..6fce3bbcd 100644 --- a/.github/workflows/browsers.yml +++ b/.github/workflows/browsers.yml @@ -23,10 +23,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Use Node.js 16 + - name: Use Node.js 20 uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 - name: Restore cached dependencies uses: actions/cache@v3 with: From d9da6791e86fa2899885e24b066da2aae3f0ec6e Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 6 Oct 2026 19:24:38 +0200 Subject: [PATCH 3/3] fix: install AJV peer for legacy npm Signed-off-by: Matteo Collina --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 98b48e686..7e38cdd2c 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@rollup/plugin-inject": "^4.0.4", "@rollup/plugin-node-resolve": "^13.3.0", "@sinonjs/fake-timers": "^9.1.2", + "ajv": "^8.20.0", "browserify": "^17.0.0", "c8": "^7.11.2", "esbuild": "^0.19.9",