Skip to content

Commit 537455e

Browse files
trivikraduh95
authored andcommitted
stream: fix merge handling for object-like sources
merge() treated any final non-iterable object as an options object. That dropped valid from() inputs such as ArrayBuffer, ArrayBufferView, and streamable protocol objects. Fixes: #63355 Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 PR-URL: #63356 Fixes: #63355 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
1 parent d57bd2b commit 537455e

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

lib/internal/streams/iter/consumers.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// ondrain() - backpressure drain utility
99

1010
const {
11+
ArrayBufferIsView,
1112
ArrayBufferPrototypeGetByteLength,
1213
ArrayBufferPrototypeSlice,
1314
ArrayPrototypeMap,
@@ -51,9 +52,12 @@ const {
5152

5253
const {
5354
drainableProtocol,
55+
toAsyncStreamable,
56+
toStreamable,
5457
} = require('internal/streams/iter/types');
5558

5659
const {
60+
isAnyArrayBuffer,
5761
isSharedArrayBuffer,
5862
} = require('internal/util/types');
5963

@@ -65,8 +69,12 @@ function isMergeOptions(value) {
6569
return (
6670
value !== null &&
6771
typeof value === 'object' &&
72+
!ArrayBufferIsView(value) &&
73+
typeof value[toStreamable] !== 'function' &&
74+
typeof value[toAsyncStreamable] !== 'function' &&
6875
!isAsyncIterable(value) &&
69-
!isSyncIterable(value)
76+
!isSyncIterable(value) &&
77+
!isAnyArrayBuffer(value)
7078
);
7179
}
7280

test/parallel/test-stream-iter-consumers-merge.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const {
99
push,
1010
merge,
1111
text,
12+
toAsyncStreamable,
13+
toStreamable,
1214
} = require('stream/iter');
1315

1416
// =============================================================================
@@ -162,6 +164,27 @@ async function testMergeStringSources() {
162164
assert.ok(combined.includes('world'));
163165
}
164166

167+
// merge() accepts object-like sources that are normalized via from()
168+
async function testMergeObjectLikeSources() {
169+
const arrayBuffer = new TextEncoder().encode('abc').buffer;
170+
const dataView = new DataView(new TextEncoder().encode('def').buffer);
171+
const streamable = {
172+
[toStreamable]() {
173+
return 'ghi';
174+
},
175+
};
176+
const asyncStreamable = {
177+
[toAsyncStreamable]() {
178+
return Promise.resolve('jkl');
179+
},
180+
};
181+
182+
assert.strictEqual(await text(merge(arrayBuffer)), 'abc');
183+
assert.strictEqual(await text(merge(dataView)), 'def');
184+
assert.strictEqual(await text(merge(streamable)), 'ghi');
185+
assert.strictEqual(await text(merge(asyncStreamable)), 'jkl');
186+
}
187+
165188
Promise.all([
166189
testMergeTwoSources(),
167190
testMergeSingleSource(),
@@ -172,4 +195,5 @@ Promise.all([
172195
testMergeConsumerBreak(),
173196
testMergeSignalMidIteration(),
174197
testMergeStringSources(),
198+
testMergeObjectLikeSources(),
175199
]).then(common.mustCall());

0 commit comments

Comments
 (0)