Skip to content

Commit e575971

Browse files
mcollinaaduh95
authored andcommitted
stream: expose ReadableStreamTee
Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: #64195 Refs: nodejs/undici#5358 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent bb9951e commit e575971

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

‎doc/api/webstreams.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,28 @@ For more details refer to the relevant documentation:
106106

107107
## API
108108

109+
### `ReadableStreamTee(stream[, cloneForBranch2])`
110+
111+
<!-- YAML
112+
added: REPLACEME
113+
-->
114+
115+
> Stability: 1 - Experimental
116+
117+
*`stream` {ReadableStream}
118+
*`cloneForBranch2` {boolean} When `true`, chunks enqueued into the second
119+
branch are cloned from chunks enqueued into the first branch. **Default:**
120+
`false`.
121+
* Returns: {ReadableStream\[]} Two {ReadableStream} branches.
122+
123+
Runs the WHATWG `ReadableStreamTee` abstract operation on `stream`.
124+
125+
This differs from `readableStream.tee()` only when `cloneForBranch2` is
126+
`true`. The `tee()` method always passes `false`, while other web platform
127+
specifications, such as Fetch body cloning, pass `true` so that the second
128+
branch receives cloned chunks and consumption of one branch cannot mutate chunks
129+
seen by the other.
130+
109131
### Class: `ReadableStream`
110132

111133
<!-- YAML

‎lib/stream/web.js‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,20 @@ const {
1818
ReadableStreamBYOBRequest,
1919
ReadableByteStreamController,
2020
ReadableStreamDefaultController,
21+
isReadableStream,
22+
readableStreamTee,
2123
}=require('internal/webstreams/readablestream');
2224

25+
const{
26+
codes: {
27+
ERR_INVALID_ARG_TYPE,
28+
},
29+
}=require('internal/errors');
30+
31+
const{
32+
validateBoolean,
33+
}=require('internal/validators');
34+
2335
const{
2436
ByteLengthQueuingStrategy,
2537
CountQueuingStrategy,
@@ -35,8 +47,17 @@ const {
3547
DecompressionStream,
3648
}=require('internal/webstreams/compression');
3749

50+
functionReadableStreamTee(stream,cloneForBranch2=false){
51+
if(!isReadableStream(stream)){
52+
thrownewERR_INVALID_ARG_TYPE('stream','ReadableStream',stream);
53+
}
54+
validateBoolean(cloneForBranch2,'cloneForBranch2');
55+
returnreadableStreamTee(stream,cloneForBranch2);
56+
}
57+
3858
module.exports={
3959
ReadableStream,
60+
ReadableStreamTee,
4061
ReadableStreamDefaultReader,
4162
ReadableStreamBYOBReader,
4263
ReadableStreamBYOBRequest,

‎test/parallel/test-whatwg-readablestream.js‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
ByteLengthQueuingStrategy,
1616
CountQueuingStrategy,
1717
ReadableStream,
18+
ReadableStreamTee,
1819
ReadableStreamDefaultReader,
1920
ReadableStreamDefaultController,
2021
ReadableByteStreamController,
@@ -1527,6 +1528,28 @@ class Source {
15271528
}).then(common.mustCall());
15281529
}
15291530

1531+
{
1532+
// Test public ReadableStreamTee() cloneForBranch2 argument
1533+
assert.strictEqual(typeofReadableStreamTee,'function');
1534+
constchunk=newUint8Array([65]);
1535+
constreadable=newReadableStream({
1536+
start(controller){
1537+
controller.enqueue(chunk);
1538+
controller.close();
1539+
},
1540+
});
1541+
const[r1,r2]=ReadableStreamTee(readable,true);
1542+
1543+
(async()=>{
1544+
const{value: value1}=awaitr1.getReader().read();
1545+
assert.strictEqual(value1[0],65);
1546+
value1[0]=66;
1547+
1548+
const{value: value2}=awaitr2.getReader().read();
1549+
assert.strictEqual(value2[0],65);
1550+
})().then(common.mustCall());
1551+
}
1552+
15301553
{
15311554
// Test tee() cloneForBranch2 argument
15321555
constreadable=newReadableStream({

0 commit comments

Comments
 (0)