@@ -186,22 +186,22 @@ The API supports two models:
186186Pull streams have natural backpressure -- the consumer drives the pace, so
187187the source is never read faster than the consumer can process. Push streams
188188need explicit backpressure because the producer and consumer run
189- independently. The ` highWaterMark ` and ` backpressure ` options on ` push() ` ,
189+ independently. The ` budget ` and ` backpressure ` options on ` push() ` ,
190190` broadcast() ` , and ` share() ` control how this works.
191191
192192#### The two-buffer model
193193
194194Push streams use a two-part buffering system. Think of it like a bucket
195- (slots ) being filled through a hose (pending writes), with a float valve
195+ (buffer ) being filled through a hose (pending writes), with a float valve
196196that closes when the bucket is full:
197197
198198``` text
199- highWaterMark (e.g., 3 )
199+ budget (e.g., 16384 )
200200 |
201201 Producer v
202202 | +---------+
203203 v | |
204- [ write() ] ----+ +--->| slots |---> Consumer pulls
204+ [ write() ] ----+ +--->| buffer |---> Consumer pulls
205205 [ write() ] | | | (bucket)| for await (...)
206206 [ write() ] v | +---------+
207207 +--------+ ^
@@ -214,29 +214,29 @@ that closes when the bucket is full:
214214 'strict' mode limits this too!
215215```
216216
217- * ** Slots (the bucket)** -- data ready for the consumer, capped at
218- ` highWaterMark ` . When the consumer pulls, it drains all slots at once
219- into a single batch.
217+ * ** Buffer (the bucket)** -- data ready for the consumer, capped at
218+ ` budget ` bytes . When the consumer pulls, it drains all buffered data
219+ at once into a single batch.
220220
221- * ** Pending writes (the hose)** -- writes waiting for slot space. After
221+ * ** Pending writes (the hose)** -- writes waiting for buffer space. After
222222 the consumer drains, pending writes are promoted into the now-empty
223- slots and their promises settle.
223+ buffer and their promises settle.
224224
225225How each policy uses these buffers:
226226
227- | Policy | Slots limit | Pending writes limit |
228- | --------------- | --------------- | -------------------- |
229- | ` 'strict' ` | ` highWaterMark ` | ` highWaterMark ` |
230- | ` 'block ' ` | ` highWaterMark ` | Unbounded |
231- | ` 'drop-oldest' ` | ` highWaterMark ` | N/A (never waits) |
232- | ` 'drop-newest' ` | ` highWaterMark ` | N/A (never waits) |
227+ | Policy | Buffer limit | Pending writes limit |
228+ | --------------- | ------------ | -------------------- |
229+ | ` 'strict' ` | ` budget ` | ` budget ` |
230+ | ` 'unbounded ' ` | ` budget ` | Unbounded |
231+ | ` 'drop-oldest' ` | ` budget ` | N/A (never waits) |
232+ | ` 'drop-newest' ` | ` budget ` | N/A (never waits) |
233233
234234#### Strict (default)
235235
236236Strict mode catches "fire-and-forget" patterns where the producer calls
237237` write() ` without awaiting, which would cause unbounded memory growth.
238- It limits both the slots buffer and the pending writes queue to
239- ` highWaterMark ` .
238+ It limits both the buffer and the pending writes queue to
239+ ` budget ` bytes .
240240
241241If you properly await each write, you can only ever have one pending
242242write at a time (yours), so you never hit the pending writes limit.
@@ -246,7 +246,7 @@ overflows:
246246``` mjs
247247import { push , text } from ' node:stream/iter' ;
248248
249- const { writer , readable } = push ({ highWaterMark : 16 });
249+ const { writer , readable } = push ({ budget : 16384 });
250250
251251// Consumer must run concurrently -- without it, the first write
252252// that fills the buffer blocks the producer forever.
@@ -265,7 +265,7 @@ console.log(await consuming);
265265const { push , text } = require (' node:stream/iter' );
266266
267267async function run () {
268- const { writer , readable } = push ({ highWaterMark : 16 });
268+ const { writer , readable } = push ({ budget : 16384 });
269269
270270 // Consumer must run concurrently -- without it, the first write
271271 // that fills the buffer blocks the producer forever.
@@ -293,9 +293,9 @@ for (const item of dataset) {
293293// --> throws "Backpressure violation: too many pending writes"
294294```
295295
296- #### Block
296+ #### Unbounded
297297
298- Block mode caps slots at ` highWaterMark ` but places no limit on the
298+ Unbounded mode caps buffered bytes at ` budget ` but places no limit on the
299299pending writes queue. Awaited writes block until the consumer makes room,
300300just like strict mode. The difference is that unawaited writes silently
301301queue forever instead of throwing -- a potential memory leak if the
@@ -309,8 +309,8 @@ properly, or when migrating code from those APIs.
309309import { push , text } from ' node:stream/iter' ;
310310
311311const { writer , readable } = push ({
312- highWaterMark : 16 ,
313- backpressure: ' block ' ,
312+ budget : 16384 ,
313+ backpressure: ' unbounded ' ,
314314});
315315
316316const consuming = text (readable);
@@ -328,8 +328,8 @@ const { push, text } = require('node:stream/iter');
328328
329329async function run () {
330330 const { writer , readable } = push ({
331- highWaterMark : 16 ,
332- backpressure: ' block ' ,
331+ budget : 16384 ,
332+ backpressure: ' unbounded ' ,
333333 });
334334
335335 const consuming = text (readable);
@@ -355,19 +355,19 @@ any scenario where stale data is less valuable than current data.
355355``` mjs
356356import { push } from ' node:stream/iter' ;
357357
358- // Keep only the 5 most recent readings
358+ // Keep only the most recent ~16 KB of readings
359359const { writer , readable } = push ({
360- highWaterMark : 5 ,
360+ budget : 16384 ,
361361 backpressure: ' drop-oldest' ,
362362});
363363```
364364
365365``` cjs
366366const { push } = require (' node:stream/iter' );
367367
368- // Keep only the 5 most recent readings
368+ // Keep only the most recent ~16 KB of readings
369369const { writer , readable } = push ({
370- highWaterMark : 5 ,
370+ budget : 16384 ,
371371 backpressure: ' drop-oldest' ,
372372});
373373```
@@ -382,19 +382,19 @@ shedding load under pressure.
382382``` mjs
383383import { push } from ' node:stream/iter' ;
384384
385- // Accept up to 10 buffered items ; discard anything beyond that
385+ // Accept up to 16 KB of buffered data ; discard anything beyond that
386386const { writer , readable } = push ({
387- highWaterMark : 10 ,
387+ budget : 16384 ,
388388 backpressure: ' drop-newest' ,
389389});
390390```
391391
392392``` cjs
393393const { push } = require (' node:stream/iter' );
394394
395- // Accept up to 10 buffered items ; discard anything beyond that
395+ // Accept up to 16 KB of buffered data ; discard anything beyond that
396396const { writer , readable } = push ({
397- highWaterMark : 10 ,
397+ budget : 16384 ,
398398 backpressure: ' drop-newest' ,
399399});
400400```
@@ -416,14 +416,16 @@ if (writer.endSync() < 0) await writer.end();
416416writer .fail (err); // Always synchronous, no fallback needed
417417```
418418
419- #### ` writer.desiredSize `
419+ #### ` writer.canWrite `
420420
421- * {number |null}
421+ * {boolean |null}
422422
423- The number of buffer slots available before the high water mark is reached.
424- Returns ` null ` if the writer is closed or the consumer has disconnected.
423+ Returns ` true ` if the next write is likely to be accepted (buffered data is
424+ below capacity), ` false ` if backpressure is active, or ` null ` if the writer
425+ is closed or the consumer has disconnected.
425426
426- The value is always non-negative.
427+ This is a hint, not a guarantee: the state can change between the check and
428+ the write. Use [ ` ondrain() ` ] [ ] to wait for capacity rather than polling.
427429
428430#### ` writer.end([options]) `
429431
@@ -759,10 +761,10 @@ added: v25.9.0
759761* ` ...transforms ` {Function|Object} Optional transforms applied to the
760762 readable side.
761763* ` options ` {Object}
762- * ` highWaterMark ` {number} Maximum number of buffered slots before
763- backpressure is applied. Must be >= 1; values below 1 are clamped to 1 .
764- ** Default:** ` 4 ` .
765- * ` backpressure ` {string} Backpressure policy: ` 'strict' ` , ` 'block ' ` ,
764+ * ` budget ` {number} Maximum number of buffered bytes before
765+ backpressure is applied. Must be >= 16384 .
766+ ** Default:** ` 16384 ` .
767+ * ` backpressure ` {string} Backpressure policy: ` 'strict' ` , ` 'unbounded ' ` ,
766768 ` 'drop-oldest' ` , or ` 'drop-newest' ` . ** Default:** ` 'strict' ` .
767769 * ` signal ` {AbortSignal} Abort the stream.
768770* Returns: {Object}
@@ -821,18 +823,18 @@ added: v25.9.0
821823-->
822824
823825* ` options ` {Object}
824- * ` highWaterMark ` {number} Buffer size for both directions.
825- ** Default:** ` 4 ` .
826+ * ` budget ` {number} Buffer size in bytes for both directions.
827+ ** Default:** ` 16384 ` .
826828 * ` backpressure ` {string} Policy for both directions.
827829 ** Default:** ` 'strict' ` .
828830 * ` signal ` {AbortSignal} Cancellation signal for both channels.
829831 * ` a ` {Object} Options specific to the A-to-B direction. Overrides
830832 shared options.
831- * ` highWaterMark ` {number}
833+ * ` budget ` {number}
832834 * ` backpressure ` {string}
833835 * ` b ` {Object} Options specific to the B-to-A direction. Overrides
834836 shared options.
835- * ` highWaterMark ` {number}
837+ * ` budget ` {number}
836838 * ` backpressure ` {string}
837839* Returns: {Array} A pair ` [channelA, channelB] ` of duplex channels.
838840
@@ -1062,9 +1064,10 @@ fulfills with `true` when the writer can accept more data.
10621064``` mjs
10631065import { push , ondrain , text } from ' node:stream/iter' ;
10641066
1065- const { writer , readable } = push ({ highWaterMark: 2 });
1066- writer .writeSync (' a' );
1067- writer .writeSync (' b' );
1067+ const { writer , readable } = push ({ budget: 16384 });
1068+ const chunk = new Uint8Array (8192 ); // 8 KB
1069+ writer .writeSync (chunk);
1070+ writer .writeSync (chunk); // 16 KB total -- buffer full
10681071
10691072// Start consuming so the buffer can actually drain
10701073const consuming = text (readable);
@@ -1082,9 +1085,10 @@ await consuming;
10821085const { push , ondrain , text } = require (' node:stream/iter' );
10831086
10841087async function run () {
1085- const { writer , readable } = push ({ highWaterMark: 2 });
1086- writer .writeSync (' a' );
1087- writer .writeSync (' b' );
1088+ const { writer , readable } = push ({ budget: 16384 });
1089+ const chunk = new Uint8Array (8192 ); // 8 KB
1090+ writer .writeSync (chunk);
1091+ writer .writeSync (chunk); // 16 KB total -- buffer full
10881092
10891093 // Start consuming so the buffer can actually drain
10901094 const consuming = text (readable);
@@ -1193,9 +1197,9 @@ added: v25.9.0
11931197-->
11941198
11951199* ` options ` {Object}
1196- * ` highWaterMark ` {number} Buffer size in slots . Must be >= 1; values
1197- below 1 are clamped to 1. ** Default:** ` 16 ` .
1198- * ` backpressure ` {string} ` 'strict' ` , ` 'block ' ` , ` 'drop-oldest' ` , or
1200+ * ` budget ` {number} Buffer size in bytes . Must be >= 16384.
1201+ ** Default:** ` 65536 ` .
1202+ * ` backpressure ` {string} ` 'strict' ` , ` 'unbounded ' ` , ` 'drop-oldest' ` , or
11991203 ` 'drop-newest' ` . ** Default:** ` 'strict' ` .
12001204 * ` signal ` {AbortSignal}
12011205* Returns: {Object}
@@ -1254,12 +1258,6 @@ async function run() {
12541258run ().catch (console .error );
12551259```
12561260
1257- #### ` broadcast.bufferSize `
1258-
1259- * {number}
1260-
1261- The number of chunks currently buffered.
1262-
12631261#### ` broadcast.cancel([reason]) `
12641262
12651263* ` reason ` {Error}
@@ -1308,9 +1306,9 @@ added: v25.9.0
13081306
13091307* ` source ` {AsyncIterable} The source to share.
13101308* ` options ` {Object}
1311- * ` highWaterMark ` {number} Buffer size. Must be >= 1; values below 1
1312- are clamped to 1. ** Default:** ` 16 ` .
1313- * ` backpressure ` {string} ` 'strict' ` , ` 'block ' ` , ` 'drop-oldest' ` , or
1309+ * ` budget ` {number} Buffer size in bytes . Must be >= 16384.
1310+ ** Default:** ` 65536 ` .
1311+ * ` backpressure ` {string} ` 'strict' ` , ` 'unbounded ' ` , ` 'drop-oldest' ` , or
13141312 ` 'drop-newest' ` . ** Default:** ` 'strict' ` .
13151313* Returns: {Share}
13161314
@@ -1350,12 +1348,6 @@ async function run() {
13501348run ().catch (console .error );
13511349```
13521350
1353- #### ` share.bufferSize `
1354-
1355- * {number}
1356-
1357- The number of chunks currently buffered.
1358-
13591351#### ` share.cancel([reason]) `
13601352
13611353* ` reason ` {Error}
@@ -1401,8 +1393,8 @@ added: v25.9.0
14011393
14021394* ` source ` {Iterable} The sync source to share.
14031395* ` options ` {Object}
1404- * ` highWaterMark ` {number} Must be >= 1; values below 1 are clamped
1405- to 1. ** Default:** ` 16 ` .
1396+ * ` budget ` {number} Must be >= 16384.
1397+ ** Default:** ` 65536 ` .
14061398 * ` backpressure ` {string} ** Default:** ` 'strict' ` .
14071399* Returns: {SyncShare}
14081400
@@ -1502,7 +1494,7 @@ added: v26.1.0
15021494 * ` backpressure ` {string} Backpressure policy. ** Default:** ` 'strict' ` .
15031495 * ` 'strict' ` -- writes are rejected when the buffer is full. Catches
15041496 callers that ignore backpressure.
1505- * ` 'block ' ` -- writes wait for drain when the buffer is full. Recommended
1497+ * ` 'unbounded ' ` -- writes wait for drain when the buffer is full. Recommended
15061498 for use with [ ` pipeTo() ` ] [ ] .
15071499 * ` 'drop-newest' ` -- writes are silently discarded when the buffer is full.
15081500 * ` 'drop-oldest' ` -- ** not supported** . Throws ` ERR_INVALID_ARG_VALUE ` .
@@ -1535,7 +1527,7 @@ const writable = new Writable({
15351527});
15361528
15371529await pipeTo (from (' hello world' ),
1538- fromWritable (writable, { backpressure: ' block ' }));
1530+ fromWritable (writable, { backpressure: ' unbounded ' }));
15391531```
15401532
15411533``` cjs
@@ -1548,7 +1540,7 @@ async function run() {
15481540 });
15491541
15501542 await pipeTo (from (' hello world' ),
1551- fromWritable (writable, { backpressure: ' block ' }));
1543+ fromWritable (writable, { backpressure: ' unbounded ' }));
15521544}
15531545run ();
15541546```
@@ -2071,6 +2063,7 @@ console.log(textSync(stream)); // 'hello world'
20712063[` from ()` ]: #frominput
20722064[` fromSync ()` ]: #fromsyncinput
20732065[` node: zlib/ iter` ]: zlib.md#iterable-compression
2066+ [` ondrain ()` ]: #ondraindrainable
20742067[` pipeTo ()` ]: #pipetosource-transforms-writer-options
20752068[` pull ()` ]: #pullsource-transforms-options
20762069[` pullSync ()` ]: #pullsyncsource-transforms-options
0 commit comments