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
20 changes: 20 additions & 0 deletions doc/api/webstreams.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,26 @@ For more details refer to the relevant documentation:

## API

### `ReadableStreamTee(stream[, cloneForBranch2])`

<!-- YAML
added: REPLACEME
-->

* `stream` {ReadableStream}
* `cloneForBranch2` {boolean} When `true`, chunks enqueued into the second
branch are cloned from chunks enqueued into the first branch. **Default:**
`false`.
* Returns: {ReadableStream\[]} Two {ReadableStream} branches.

Runs the WHATWG `ReadableStreamTee` abstract operation on `stream`.

This differs from `readableStream.tee()` only when `cloneForBranch2` is
`true`. The `tee()` method always passes `false`, while other web platform
specifications, such as Fetch body cloning, pass `true` so that the second
branch receives cloned chunks and consumption of one branch cannot mutate chunks
seen by the other.

### Class: `ReadableStream`

<!-- YAML
Expand Down
21 changes: 21 additions & 0 deletions lib/stream/web.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,20 @@ const {
ReadableStreamBYOBRequest,
ReadableByteStreamController,
ReadableStreamDefaultController,
isReadableStream,
readableStreamTee,
} = require('internal/webstreams/readablestream');

const {
codes: {
ERR_INVALID_ARG_TYPE,
},
} = require('internal/errors');

const {
validateBoolean,
} = require('internal/validators');

const {
ByteLengthQueuingStrategy,
CountQueuingStrategy,
Expand All @@ -35,8 +47,17 @@ const {
DecompressionStream,
} = require('internal/webstreams/compression');

function ReadableStreamTee(stream, cloneForBranch2 = false) {
if (!isReadableStream(stream)) {
throw new ERR_INVALID_ARG_TYPE('stream', 'ReadableStream', stream);
}
validateBoolean(cloneForBranch2, 'cloneForBranch2');
return readableStreamTee(stream, cloneForBranch2);
}

module.exports = {
ReadableStream,
ReadableStreamTee,
ReadableStreamDefaultReader,
ReadableStreamBYOBReader,
ReadableStreamBYOBRequest,
Expand Down
23 changes: 23 additions & 0 deletions test/parallel/test-whatwg-readablestream.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
ByteLengthQueuingStrategy,
CountQueuingStrategy,
ReadableStream,
ReadableStreamTee,
ReadableStreamDefaultReader,
ReadableStreamDefaultController,
ReadableByteStreamController,
Expand Down Expand Up @@ -1527,6 +1528,28 @@ class Source {
}).then(common.mustCall());
}

{
// Test public ReadableStreamTee() cloneForBranch2 argument
assert.strictEqual(typeof ReadableStreamTee, 'function');
const chunk = new Uint8Array([65]);
const readable = new ReadableStream({
start(controller) {
controller.enqueue(chunk);
controller.close();
},
});
const [r1, r2] = ReadableStreamTee(readable, true);

(async () => {
const { value: value1 } = await r1.getReader().read();
assert.strictEqual(value1[0], 65);
value1[0] = 66;

const { value: value2 } = await r2.getReader().read();
assert.strictEqual(value2[0], 65);
})().then(common.mustCall());
}

{
// Test tee() cloneForBranch2 argument
const readable = new ReadableStream({
Expand Down
Loading