Skip to content

Commit 1e713e7

Browse files
committed
stream: prefer sync iterator in fromSync
When an input provides both Symbol.iterator and Symbol.asyncIterator, fromSync() should consume the synchronous iterator instead of rejecting the input as async. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5
1 parent b59def5 commit 1e713e7

2 files changed

Lines changed: 20 additions & 4 deletions

File tree

lib/internal/streams/iter/from.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,10 @@ function fromSync(input) {
484484
return fromSync(input[toStreamable]());
485485
}
486486

487-
// Reject explicit async inputs
488-
if (isAsyncIterable(input)) {
487+
const isIterable = isSyncIterable(input);
488+
489+
// Reject explicit async-only inputs
490+
if (!isIterable && isAsyncIterable(input)) {
489491
throw new ERR_INVALID_ARG_TYPE(
490492
'input',
491493
'a synchronous input (not AsyncIterable)',
@@ -501,7 +503,7 @@ function fromSync(input) {
501503
}
502504

503505
// Must be a SyncStreamable
504-
if (!isSyncIterable(input)) {
506+
if (!isIterable) {
505507
throw new ERR_INVALID_ARG_TYPE(
506508
'input',
507509
['string', 'ArrayBuffer', 'ArrayBufferView', 'Iterable', 'toStreamable'],

test/parallel/test-stream-iter-from-sync.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
const common = require('../common');
55
const assert = require('assert');
6-
const { fromSync } = require('stream/iter');
6+
const { fromSync, textSync } = require('stream/iter');
77

88
function testFromSyncString() {
99
// String input should be UTF-8 encoded
@@ -186,6 +186,19 @@ function testFromSyncRejectsAsyncIterable() {
186186
assert.throws(() => fromSync(gen()), { code: 'ERR_INVALID_ARG_TYPE' });
187187
}
188188

189+
function testFromSyncPrefersIteratorForDualIterable() {
190+
const input = {
191+
*[Symbol.iterator]() {
192+
yield new TextEncoder().encode('sync');
193+
},
194+
async *[Symbol.asyncIterator]() {
195+
yield new TextEncoder().encode('async');
196+
},
197+
};
198+
199+
assert.strictEqual(textSync(fromSync(input)), 'sync');
200+
}
201+
189202
// Promise rejected
190203
function testFromSyncRejectsPromise() {
191204
assert.throws(() => fromSync(Promise.resolve('hello')),
@@ -232,6 +245,7 @@ Promise.all([
232245
testFromSyncTopLevelProtocolOverIterator(),
233246
testFromSyncIgnoresAsyncStreamable(),
234247
testFromSyncRejectsAsyncIterable(),
248+
testFromSyncPrefersIteratorForDualIterable(),
235249
testFromSyncRejectsPromise(),
236250
testFromSyncDataView(),
237251
]).then(common.mustCall());

0 commit comments

Comments
 (0)