| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 745ea1d commit 2b36433
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3188,6 +3188,9 @@ Returns whether the stream has been read from or cancelled. | |||
| 3188 | 3188 | <!-- YAML | |
| 3189 | 3189 | added: v17.0.0 | |
| 3190 | 3190 | changes: | |
| 3191 | + - version: REPLACEME | ||
| 3192 | + pr-url: https://github.com/nodejs/node/pull/58664 | ||
| 3193 | + description: Add 'type' option to specify 'bytes'. | ||
| 3191 | 3194 | - version: | |
| 3192 | 3195 | - v24.0.0 | |
| 3193 | 3196 | - v22.17.0 | |
@@ -3210,6 +3213,7 @@ changes: | |||
| 3210 | 3213 | If no value is provided, the size will be `1` for all the chunks. | |
| 3211 | 3214 | * `chunk` {any} | |
| 3212 | 3215 | * Returns: {number} | |
| 3216 | + * `type` {string} Must be 'bytes' or undefined. | ||
| 3213 | 3217 | * Returns: {ReadableStream} | |
| 3214 | 3218 | ||
| 3215 | 3219 | ### `stream.Writable.fromWeb(writableStream[, options])` | |
@@ -3383,11 +3387,14 @@ duplex.write('hello'); | |||
| 3383 | 3387 | duplex.once('readable', () => console.log('readable', duplex.read())); | |
| 3384 | 3388 | ``` | |
| 3385 | 3389 | ||
| 3386 | - ### `stream.Duplex.toWeb(streamDuplex)` | ||
| 3390 | + ### `stream.Duplex.toWeb(streamDuplex[, options])` | ||
| 3387 | 3391 | ||
| 3388 | 3392 | <!-- YAML | |
| 3389 | 3393 | added: v17.0.0 | |
| 3390 | 3394 | changes: | |
| 3395 | + - version: REPLACEME | ||
| 3396 | + pr-url: https://github.com/nodejs/node/pull/58664 | ||
| 3397 | + description: Add 'type' option to specify 'bytes'. | ||
| 3391 | 3398 | - version: | |
| 3392 | 3399 | - v24.0.0 | |
| 3393 | 3400 | - v22.17.0 | |
@@ -3396,6 +3403,8 @@ changes: | |||
| 3396 | 3403 | --> | |
| 3397 | 3404 | ||
| 3398 | 3405 | * `streamDuplex` {stream.Duplex} | |
| 3406 | + * `options` {Object} | ||
| 3407 | + * `type` {string} Must be 'bytes' or undefined. | ||
| 3399 | 3408 | * Returns: {Object} | |
| 3400 | 3409 | * `readable` {ReadableStream} | |
| 3401 | 3410 | * `writable` {WritableStream} | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1777,7 +1777,7 @@ text(readable).then((data) => { | |||
| 1777 | 1777 | [Streams]: stream.md | |
| 1778 | 1778 | [WHATWG Streams Standard]: https://streams.spec.whatwg.org/ | |
| 1779 | 1779 | [`stream.Duplex.fromWeb`]: stream.md#streamduplexfromwebpair-options | |
| 1780 | - [`stream.Duplex.toWeb`]: stream.md#streamduplextowebstreamduplex | ||
| 1780 | + [`stream.Duplex.toWeb`]: stream.md#streamduplextowebstreamduplex-options | ||
| 1781 | 1781 | [`stream.Duplex`]: stream.md#class-streamduplex | |
| 1782 | 1782 | [`stream.Readable.fromWeb`]: stream.md#streamreadablefromwebreadablestream-options | |
| 1783 | 1783 | [`stream.Readable.toWeb`]: stream.md#streamreadabletowebstreamreadable-options | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -191,8 +191,8 @@ Duplex.fromWeb = function(pair, options) { | |||
| 191 | 191 | options); | |
| 192 | 192 | }; | |
| 193 | 193 | ||
| 194 | - Duplex.toWeb = function(duplex) { | ||
| 195 | - return lazyWebStreams().newReadableWritablePairFromDuplex(duplex); | ||
| 194 | + Duplex.toWeb = function(duplex, options) { | ||
| 195 | + return lazyWebStreams().newReadableWritablePairFromDuplex(duplex, options); | ||
| 196 | 196 | }; | |
| 197 | 197 | ||
| 198 | 198 | let duplexify; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,6 +73,7 @@ const { | |||
| 73 | 73 | validateBoolean, | |
| 74 | 74 | validateFunction, | |
| 75 | 75 | validateObject, | |
| 76 | + validateOneOf, | ||
| 76 | 77 | } = require('internal/validators'); | |
| 77 | 78 | ||
| 78 | 79 | const { | |
@@ -417,7 +418,8 @@ function newStreamWritableFromWritableStream(writableStream, options = kEmptyObj | |||
| 417 | 418 | * @typedef {import('./queuingstrategies').QueuingStrategy} QueuingStrategy | |
| 418 | 419 | * @param {Readable} streamReadable | |
| 419 | 420 | * @param {{ | |
| 420 | - * strategy : QueuingStrategy | ||
| 421 | + * strategy? : QueuingStrategy | ||
| 422 | + * type? : 'bytes', | ||
| 421 | 423 | * }} [options] | |
| 422 | 424 | * @returns {ReadableStream} | |
| 423 | 425 | */ | |
@@ -432,6 +434,12 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj | |||
| 432 | 434 | 'stream.Readable', | |
| 433 | 435 | streamReadable); | |
| 434 | 436 | } | |
| 437 | + validateObject(options, 'options'); | ||
| 438 | + if (options.type !== undefined) { | ||
| 439 | + validateOneOf(options.type, 'options.type', ['bytes', undefined]); | ||
| 440 | + } | ||
| 441 | + | ||
| 442 | + const isBYOB = options.type === 'bytes'; | ||
| 435 | 443 | ||
| 436 | 444 | if (isDestroyed(streamReadable) || !isReadable(streamReadable)) { | |
| 437 | 445 | const readable = new ReadableStream(); | |
@@ -443,6 +451,9 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj | |||
| 443 | 451 | const highWaterMark = streamReadable.readableHighWaterMark; | |
| 444 | 452 | ||
| 445 | 453 | const evaluateStrategyOrFallback = (strategy) => { | |
| 454 | + // If the stream is BYOB, we only use highWaterMark | ||
| 455 | + if (isBYOB) | ||
| 456 | + return { highWaterMark }; | ||
| 446 | 457 | // If there is a strategy available, use it | |
| 447 | 458 | if (strategy) | |
| 448 | 459 | return strategy; | |
@@ -491,7 +502,19 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj | |||
| 491 | 502 | streamReadable.on('data', onData); | |
| 492 | 503 | ||
| 493 | 504 | return new ReadableStream({ | |
| 494 | - start(c) { controller = c; }, | ||
| 505 | + type: isBYOB ? 'bytes' : undefined, | ||
| 506 | + start(c) { | ||
| 507 | + controller = c; | ||
| 508 | + if (isBYOB) { | ||
| 509 | + streamReadable.once('end', () => { | ||
| 510 | + // close the controller | ||
| 511 | + controller.close(); | ||
| 512 | + // And unlock the last BYOB read request | ||
| 513 | + controller.byobRequest?.respond(0); | ||
| 514 | + wasCanceled = true; | ||
| 515 | + }); | ||
| 516 | + } | ||
| 517 | + }, | ||
| 495 | 518 | ||
| 496 | 519 | pull() { streamReadable.resume(); }, | |
| 497 | 520 | ||
@@ -601,9 +624,10 @@ function newStreamReadableFromReadableStream(readableStream, options = kEmptyObj | |||
| 601 | 624 | ||
| 602 | 625 | /** | |
| 603 | 626 | * @param {Duplex} duplex | |
| 627 | + * @param {{ type?: 'bytes' }} [options] | ||
| 604 | 628 | * @returns {ReadableWritablePair} | |
| 605 | 629 | */ | |
| 606 | - function newReadableWritablePairFromDuplex(duplex) { | ||
| 630 | + function newReadableWritablePairFromDuplex(duplex, options = kEmptyObject) { | ||
| 607 | 631 | // Not using the internal/streams/utils isWritableNodeStream and | |
| 608 | 632 | // isReadableNodeStream utilities here because they will return false | |
| 609 | 633 | // if the duplex was created with writable or readable options set to | |
@@ -615,9 +639,11 @@ function newReadableWritablePairFromDuplex(duplex) { | |||
| 615 | 639 | throw new ERR_INVALID_ARG_TYPE('duplex', 'stream.Duplex', duplex); | |
| 616 | 640 | } | |
| 617 | 641 | ||
| 642 | + validateObject(options, 'options'); | ||
| 643 | + | ||
| 618 | 644 | if (isDestroyed(duplex)) { | |
| 619 | 645 | const writable = new WritableStream(); | |
| 620 | - const readable = new ReadableStream(); | ||
| 646 | + const readable = new ReadableStream({ type: options.type }); | ||
| 621 | 647 | writable.close(); | |
| 622 | 648 | readable.cancel(); | |
| 623 | 649 | return { readable, writable }; | |
@@ -633,8 +659,8 @@ function newReadableWritablePairFromDuplex(duplex) { | |||
| 633 | 659 | ||
| 634 | 660 | const readable = | |
| 635 | 661 | isReadable(duplex) ? | |
| 636 | - newReadableStreamFromStreamReadable(duplex) : | ||
| 637 | - new ReadableStream(); | ||
| 662 | + newReadableStreamFromStreamReadable(duplex, options) : | ||
| 663 | + new ReadableStream({ type: options.type }); | ||
| 638 | 664 | ||
| 639 | 665 | if (!isReadable(duplex)) | |
| 640 | 666 | readable.cancel(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -131,3 +131,26 @@ process.on('exit', () => { | |||
| 131 | 131 | assert.deepStrictEqual(Buffer.from(result.value), dataToRead); | |
| 132 | 132 | })); | |
| 133 | 133 | } | |
| 134 | + | ||
| 135 | + // Duplex.toWeb BYOB | ||
| 136 | + { | ||
| 137 | + const dataToRead = Buffer.from('hello'); | ||
| 138 | + const dataToWrite = Buffer.from('world'); | ||
| 139 | + | ||
| 140 | + const duplex = Duplex({ | ||
| 141 | + read() { | ||
| 142 | + this.push(dataToRead); | ||
| 143 | + this.push(null); | ||
| 144 | + }, | ||
| 145 | + write: common.mustCall((chunk) => { | ||
| 146 | + assert.strictEqual(chunk, dataToWrite); | ||
| 147 | + }) | ||
| 148 | + }); | ||
| 149 | + | ||
| 150 | + const { writable, readable } = Duplex.toWeb(duplex, { type: 'bytes' }); | ||
| 151 | + writable.getWriter().write(dataToWrite); | ||
| 152 | + const data = new Uint8Array(dataToRead.length); | ||
| 153 | + readable.getReader({ mode: 'byob' }).read(data).then(common.mustCall((result) => { | ||
| 154 | + assert.deepStrictEqual(Buffer.from(result.value), dataToRead); | ||
| 155 | + })); | ||
| 156 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,49 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + require('../common'); | ||
| 3 | + const { Readable } = require('stream'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + const common = require('../common'); | ||
| 6 | + | ||
| 7 | + let count = 0; | ||
| 8 | + | ||
| 9 | + const nodeStream = new Readable({ | ||
| 10 | + read(size) { | ||
| 11 | + if (this.destroyed) { | ||
| 12 | + return; | ||
| 13 | + } | ||
| 14 | + // Simulate a stream that pushes sequences of 16 bytes | ||
| 15 | + const buffer = Buffer.alloc(size); | ||
| 16 | + for (let i = 0; i < size; i++) { | ||
| 17 | + buffer[i] = count++ % 16; | ||
| 18 | + } | ||
| 19 | + this.push(buffer); | ||
| 20 | + } | ||
| 21 | + }); | ||
| 22 | + | ||
| 23 | + // Test validation of 'type' option | ||
| 24 | + assert.throws( | ||
| 25 | + () => { | ||
| 26 | + Readable.toWeb(nodeStream, { type: 'wrong type' }); | ||
| 27 | + }, | ||
| 28 | + { | ||
| 29 | + code: 'ERR_INVALID_ARG_VALUE' | ||
| 30 | + } | ||
| 31 | + ); | ||
| 32 | + | ||
| 33 | + // Test normal operation with ReadableByteStream | ||
| 34 | + const webStream = Readable.toWeb(nodeStream, { type: 'bytes' }); | ||
| 35 | + const reader = webStream.getReader({ mode: 'byob' }); | ||
| 36 | + const expected = new Uint8Array(16); | ||
| 37 | + for (let i = 0; i < 16; i++) { | ||
| 38 | + expected[i] = count++; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + for (let i = 0; i < 1000; i++) { | ||
| 42 | + // Read 16 bytes of data from the stream | ||
| 43 | + const receive = new Uint8Array(16); | ||
| 44 | + reader.read(receive).then(common.mustCall((result) => { | ||
| 45 | + // Verify the data received | ||
| 46 | + assert.ok(!result.done); | ||
| 47 | + assert.deepStrictEqual(result.value, expected); | ||
| 48 | + })); | ||
| 49 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,15 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + require('../common'); | ||
| 3 | + const { Readable } = require('stream'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + const common = require('../common'); | ||
| 6 | + { | ||
| 7 | + const r = Readable.from([]); | ||
| 8 | + // Cancelling reader while closing should not cause uncaught exceptions | ||
| 9 | + r.on('close', common.mustCall(() => reader.cancel())); | ||
| 10 | + | ||
| 11 | + const reader = Readable.toWeb(r, { type: 'bytes' }).getReader({ mode: 'byob' }); | ||
| 12 | + reader.read(new Uint8Array(16)).then(common.mustCall((result) => { | ||
| 13 | + assert.ok(result.done); | ||
| 14 | + })); | ||
| 15 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments