Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/internal/streams/compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,12 @@ module.exports = function compose(...streams) {
try {
const { value, done } = await reader.read();

if (!d.push(value)) {
if (done) {
d.push(null);
return;
}

if (done) {
d.push(null);
if (!d.push(value)) {
return;
}
} catch {
Expand Down
21 changes: 21 additions & 0 deletions test/parallel/test-stream-compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,24 @@ const assert = require('assert');
assert.strictEqual(duplex.destroyed, true);

}

// Regression test: compose with a web TransformStream tail must always emit
// null (EOF) when the source finishes. The done check must precede the
// backpressure check in the reader.read() loop; otherwise push(null) can be
// skipped if canPushMore() returns false on the final done:true read.
{
const { TransformStream } = globalThis;
const { Readable } = require('stream');

// A web TransformStream as the tail exercises the isWebStream code path
// in compose that loops over reader.read() results.
const ts = new TransformStream();
const src = Readable.from(['hello', ' ', 'world']);
const composed = compose(src, ts);

let result = '';
composed.on('data', (chunk) => { result += Buffer.from(chunk).toString(); });
composed.on('end', common.mustCall(() => {
assert.strictEqual(result, 'hello world');
}));
}
Loading