From ab68aad1a01bf2c102057a74b0174cd7cfa7ca93 Mon Sep 17 00:00:00 2001 From: Efe Karasakal Date: Wed, 11 Mar 2026 18:11:02 +0100 Subject: [PATCH 1/2] stream: validate streams in pipeline --- lib/internal/streams/pipeline.js | 86 +++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index ad3f0796875aae..f243e456ce741a 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -13,7 +13,6 @@ const { const { eos } = require('internal/streams/end-of-stream'); const { once } = require('internal/util'); const destroyImpl = require('internal/streams/destroy'); -const Duplex = require('internal/streams/duplex'); const { AbortError, aggregateTwoErrors, @@ -36,6 +35,8 @@ const { isIterable, isReadable, isReadableNodeStream, + isWritableNodeStream, + isWritableStream, isNodeStream, isTransformStream, isWebStream, @@ -159,7 +160,8 @@ async function pumpToWeb(readable, writable, finish, { end }) { try { for await (const chunk of readable) { await writer.ready; - writer.write(chunk).catch(() => {}); + writer.write(chunk).catch(() => { + }); } await writer.ready; @@ -179,6 +181,76 @@ async function pumpToWeb(readable, writable, finish, { end }) { } } +function getPipelineArgName(i, len) { + if (i === 0) return 'source'; + if (i === len - 1) return 'destination'; + return `transform[${i - 1}]`; +} + +function isValidPipelineSource(stream) { + return ( + isIterable(stream) || + isReadableNodeStream(stream) || + isReadableStream(stream) || + isTransformStream(stream) + ); +} + +function isValidPipelineTransform(stream) { + return ( + isTransformStream(stream) || + (isNodeStream(stream) && + isReadableNodeStream(stream) && + isWritableNodeStream(stream)) + ); +} + +function isValidPipelineDestination(stream) { + return ( + isWritableNodeStream(stream) || + isWritableStream(stream) || + isTransformStream(stream) + ); +} + +function validatePipelineStream(stream, i, len) { + if (typeof stream === 'function') { + return; + } + + const name = getPipelineArgName(i, len); + + if (i === 0) { + if (!isValidPipelineSource(stream)) { + throw new ERR_INVALID_ARG_TYPE( + name, + ['Readable', 'Iterable', 'AsyncIterable', 'ReadableStream', 'TransformStream'], + stream, + ); + } + return; + } + + if (i === len - 1) { + if (!isValidPipelineDestination(stream)) { + throw new ERR_INVALID_ARG_TYPE( + name, + ['Writable', 'WritableStream', 'TransformStream'], + stream, + ); + } + return; + } + + if (!isValidPipelineTransform(stream)) { + throw new ERR_INVALID_ARG_TYPE( + name, + ['Duplex', 'Transform', 'TransformStream'], + stream, + ); + } +} + function pipeline(...streams) { return pipelineImpl(streams, once(popCallback(streams))); } @@ -192,6 +264,10 @@ function pipelineImpl(streams, callback, opts) { throw new ERR_MISSING_ARGS('streams'); } + for (let i = 0; i < streams.length; i++) { + validatePipelineStream(streams[i], i, streams.length); + } + const ac = new AbortController(); const signal = ac.signal; const outerSignal = opts?.signal; @@ -298,10 +374,8 @@ function pipelineImpl(streams, callback, opts) { throw new ERR_INVALID_RETURN_VALUE( 'Iterable, AsyncIterable or Stream', 'source', ret); } - } else if (isIterable(stream) || isReadableNodeStream(stream) || isTransformStream(stream)) { - ret = stream; } else { - ret = Duplex.from(stream); + ret = stream; } } else if (typeof stream === 'function') { if (isTransformStream(ret)) { @@ -402,8 +476,6 @@ function pipelineImpl(streams, callback, opts) { 'val', ['Readable', 'Iterable', 'AsyncIterable', 'ReadableStream', 'TransformStream'], ret); } ret = stream; - } else { - ret = Duplex.from(stream); } } From c5da09b641b66ba24f5b19d3a4fa6134234f0b96 Mon Sep 17 00:00:00 2001 From: Efe Karasakal Date: Wed, 11 Mar 2026 20:36:09 +0100 Subject: [PATCH 2/2] stream: inline name arg creation --- lib/internal/streams/pipeline.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index f243e456ce741a..4359c9a5d47c2f 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -181,12 +181,6 @@ async function pumpToWeb(readable, writable, finish, { end }) { } } -function getPipelineArgName(i, len) { - if (i === 0) return 'source'; - if (i === len - 1) return 'destination'; - return `transform[${i - 1}]`; -} - function isValidPipelineSource(stream) { return ( isIterable(stream) || @@ -218,12 +212,10 @@ function validatePipelineStream(stream, i, len) { return; } - const name = getPipelineArgName(i, len); - if (i === 0) { if (!isValidPipelineSource(stream)) { throw new ERR_INVALID_ARG_TYPE( - name, + 'source', ['Readable', 'Iterable', 'AsyncIterable', 'ReadableStream', 'TransformStream'], stream, ); @@ -234,7 +226,7 @@ function validatePipelineStream(stream, i, len) { if (i === len - 1) { if (!isValidPipelineDestination(stream)) { throw new ERR_INVALID_ARG_TYPE( - name, + 'destination', ['Writable', 'WritableStream', 'TransformStream'], stream, ); @@ -244,7 +236,7 @@ function validatePipelineStream(stream, i, len) { if (!isValidPipelineTransform(stream)) { throw new ERR_INVALID_ARG_TYPE( - name, + `transform[${i - 1}]`, ['Duplex', 'Transform', 'TransformStream'], stream, );