Introduction
The Streams Standard [STREAMS] defines ReadableStream, WritableStream, and TransformStream: class-based streams with chunk-at-a-time delivery. The Iterable Streams API [ITER-STREAMS] defines a bytes-only streaming model built directly on the ECMAScript iteration protocols: streams are AsyncIterable<Uint8Array[]>, iteration yields batches of chunks to amortize per-tick costs, and production goes through a Writer interface with an explicit byte budget and backpressure policies.
This specification bridges the two models from the web streams side. It defines:
-
A new reader mode —
getReader({ mode: "iter" })— that reads batches from anyReadableStreamand conforms to the iterable streams consumption model. -
A new readable stream controller type —
new ReadableStream({ type: "iter", start(writer) })— whose controller is an [ITER-STREAMS]Writer, providing the push model. -
A new writable stream controller type —
new WritableStream({ type: "iter", start(source) })— whose controller is an [ITER-STREAMS] readable (anAsyncIterable<Uint8Array[]>), providing batch-native sinks. -
A new writer mode —
getWriter({ mode: "iter" })— that exposes the [ITER-STREAMS]Writerinterface over anyWritableStream. -
Batch-native
TransformStreams whose transformer contract is the [ITER-STREAMS] transform contract. -
A fused, pull-through pipe: when both ends of a pipe are iter-type, piping uses the iterable streams model directly — lazy, batch-preserving-where-possible, with backpressure coupled end to end.
// (a) Batched reads from any ReadableStream const reader= rs. getReader({ mode: 'iter' }); for await ( const chunksof reader) { // chunks is Uint8Array[] } // (b) Push-model construction; the controller IS an iter-streams Writer const rs= new ReadableStream({ type: 'iter' , async start( writer) { if ( ! writer. writeSync( header)) await writer. write( header); await writer. end(); } }); // (c) Batch-native sink; the controller IS an iter-streams readable const ws= new WritableStream({ type: 'iter' , async start( source) { for await ( const chunksof source) { socket. writev( chunks); } } });
Internally, the new controller and reader types are based on the iterable streams model, allowing implementations to take advantage of its performance characteristics. Because they are new controller and reader types, backwards compatibility with existing streams is preserved: every existing operation on an iter-type stream works through compatibility facades, and every existing stream gains batched reading without modification.
Goals
-
Batched consumption for all streams: Reading a
ReadableStreambatch-at-a-time amortizes async iteration cost regardless of how the stream was created. -
Full iterable-streams citizenship: Objects defined by this specification conform structurally to the [ITER-STREAMS] interfaces and protocols. An iter reader is a
ByteReadableStream; the iter controllers implementWriterandByteReadableStreamrespectively; the protocol symbols are honored. Web streams participate in the iterable streams ecosystem —Stream.pull(),Stream.pipeTo(),Stream.text(), transforms — without adapters. -
Backwards compatibility: An iter-type stream is a
ReadableStream(orWritableStream). Default readers, default writers,tee(), piping, and every other existing operation continue to work. -
Pull-through piping: When both ends of a pipe are iter-type, the pipe is the iterable streams model: lazy, fused, and driven by the ultimate consumer.
-
Bytes-only where it matters, generic where it helps: Iter-type streams are bytes-only, matching [ITER-STREAMS]. The iter reader is generic: on a value-oriented stream it still provides batching-as-amortization, without claiming iterable-streams integration.
Non-Goals
-
Modifying the Iterable Streams API: [ITER-STREAMS] remains a separate specification. This specification consumes its interfaces and protocols; it defines nothing on the
Streamnamespace. -
Replacing default or byte streams: The default and
"bytes"stream types are untouched. This specification adds a third type alongside them. -
New consumption utilities: Draining helpers (
text(),bytes(), etc.) belong to [ITER-STREAMS]. This specification makes web streams work with them rather than duplicating them. -
BYOB integration: The
"byob"reader mode and iter-type streams do not interact.getReader({ mode: "byob" })on an iter-type stream throws, exactly as it does on a default stream.
Relationship to Existing Specifications
-
Streams Standard [STREAMS]: This specification extends the Streams Standard with new reader, writer, and controller types, and amends several of its algorithms and IDL definitions. See § 15 Modifications to the Streams Standard. The Streams Standard is a normative dependency.
-
Iterable Streams API [ITER-STREAMS]: Defines the
Writerinterface,ByteReadableStreamstructural type, byte budget and backpressure policy model, transform contract, and protocol symbols that this specification’s types implement. A normative dependency.
Note: This specification is intended for standardization through ECMA TC55 (WinterTC), not through the WHATWG. Because Web IDL provides no mechanism for extending enumerations from outside a specification, the changes to the Streams Standard are expressed as amendments in § 15 Modifications to the Streams Standard. If the extensions prove successful, upstreaming some or all of them into the Streams Standard would be a desirable outcome; the design deliberately confines the amendments to well-isolated extension points (new enum values, reserved dictionary members, and type-dispatched algorithm steps) to keep that path open.
Design Rationale
The duality
The central design is a duality between the two stream directions and the two [ITER-STREAMS] interfaces:
| Web streams side | Iterable streams side | |
|---|---|---|
| Producing into a readable | ReadableStreamIterController
| implements Writer
|
| Consuming from a writable | WritableStreamIterController
| implements ByteReadableStream
|
| Consuming a readable | ReadableStreamIterReader
| is a ByteReadableStream
|
| Producing into a writable | WritableStreamIterWriter
| implements Writer
|
[ITER-STREAMS] states that Writer "is an interface, not a concrete class — any object implementing this interface can serve as a writer," and that its stream types are structural (AsyncIterable<Uint8Array[]>). The controllers defined here are those objects. There is no wrapping and no adapter layer between the models: the controller handed to an underlying source’s start() is a Writer; the source handed to an underlying sink’s start() is an iterable-streams readable.
Why the integration needs no changes to [ITER-STREAMS]
Three properties of [ITER-STREAMS] carry the integration:
-
Stream.from()consultsSymbol.for('Stream.toAsyncStreamable')before the iteration protocols. This specification installs that hook onReadableStream.prototype(§ 10 The toAsyncStreamable hook), so every iterable-streams entry point —Stream.from(),Stream.pull(),Stream.text(),Stream.pipeTo()— automatically upgrades anyReadableStreamto batched reads. Without the hook, web streams still work through theSymbol.asyncIteratorfallback, but degrade to single-chunk batches. -
Stream.pipeTo()duck-types its destination on thewritemethod (preferringwritev). The controllers and writers defined here satisfy it structurally, so they are valid iterable-streams pipe destinations with batching, close, and error propagation intact. -
The
Symbol.for('Stream.drainableProtocol')symbol makesStream.ondrain()work with any object that carries it. TheWriter-implementing objects defined here carry it.
Batch boundaries are not semantic
Batching exists to amortize iteration costs. It carries no meaning: any operation may split, merge, or regroup batches, provided the flattened chunk sequence and its ordering are preserved (§ 5.1 Batch boundaries are not semantic). This principle is what makes the compatibility facades and the fused pipe observationally coherent: unbatching for a default reader, re-coalescing at a sink, and end-to-end fusion all preserve the only thing that matters — the chunks and their order.
Copyright
© 2026 Ecma International
Permission under Ecma’s copyright to copy, modify, prepare derivative works of, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the full text of this copyright notice on ALL copies of the work or portions thereof.
THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
1. Scope
This proposal defines the Iterable Web Streams Extensions: an extension of the Streams Standard [STREAMS] that integrates it with the Iterable Streams API [ITER-STREAMS]. It specifies:
-
A
ReadableStreamIterReaderacquired viagetReader({ mode: "iter" }), providing batched reads from anyReadableStream. -
A
ReadableStreamIterController, implementing the [ITER-STREAMS]Writerinterface, for push-model readable streams created withtype: "iter". -
A
WritableStreamIterController, implementing the [ITER-STREAMS]ByteReadableStreamstructural type, for batch-native writable sinks created withtype: "iter". -
A
WritableStreamIterWriteracquired viagetWriter({ mode: "iter" }), implementing the [ITER-STREAMS]Writerinterface over anyWritableStream. -
Batch-native
TransformStreams whose transformer contract is the [ITER-STREAMS] transform contract. -
Amendments to the Streams Standard: new enum values, batch-aware
ReadableStream.from(), thetoAsyncStreamableprototype hook, and a type-dispatched pipe algorithm with a fused pull-through path.
2. Conformance
A conforming implementation of this specification must also be a conforming implementation of the Streams Standard [STREAMS] as amended by § 15 Modifications to the Streams Standard, and must provide the [ITER-STREAMS] interfaces and protocol symbols to the extent this specification depends on them.
Note: An implementation need not expose the [ITER-STREAMS] Stream namespace to conform to this specification — the dependency is on the Writer interface contract, the structural stream types, the backpressure model, the transform contract, and the protocol symbols. In practice the two specifications are designed to be implemented together.
A conforming implementation shall also conform to [ECMASCRIPT] and [WEBIDL].
3. Normative references
The following documents are referred to in the text in such a way that some or all of their content constitutes requirements of this document.
References
Normative References
- [DOM]
- Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
- [ECMASCRIPT]
- ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
- [ITER-STREAMS]
- James M Snell. Iterable Streams API. Draft Proposal. URL: https://iter-streams.proposal.wintertc.org/
- [STREAMS]
- Adam Rice; et al. Streams Standard. Living Standard. URL: https://streams.spec.whatwg.org/
- [WEBIDL]
- Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
4. Terms and definitions
For the purposes of this document, the terms and definitions given in [ECMASCRIPT], the Streams Standard [STREAMS], the Iterable Streams API [ITER-STREAMS], and the following apply.
4.1. Iterable Web Streams Extensions
the extensions to the Streams Standard defined by this specification
4.2. batch
an array of chunks delivered as a single iteration step or read result; for byte-oriented streams, a Uint8Array[] as defined by the [ITER-STREAMS] batched chunks concept
4.3. iter readable stream
a ReadableStream whose controller is a ReadableStreamIterController
4.4. iter writable stream
a WritableStream whose controller is a WritableStreamIterController
4.5. facade
the adaptation layer through which existing Streams Standard operations (default readers, default writers, and the algorithms built on them) interoperate with iter-type streams
4.6. fused pipe
the pull-through pipe established when both ends of a pipe operation are iter-type streams, in which the destination sink’s iteration consumes the source directly with no intermediate buffering
5. Core concepts
5.1. Batch boundaries are not semantic
A batch is a delivery grouping, not a message boundary. Operations defined by this specification — and implementations, wherever this specification grants latitude — may split, merge, or regroup batches freely. The normative invariant is the flattened chunk sequence: the sequence of individual chunks obtained by concatenating batches in order. All operations MUST preserve the flattened chunk sequence and its ordering; none are required to preserve batch boundaries.
Note: This matches [ITER-STREAMS], in which batch composition is implementation-chosen and transforms regroup freely. Applications MUST NOT attach meaning to how chunks are grouped into batches.
5.2. Bytes-only types, generic reader
Iter readable streams and iter writable streams are bytes-only: their chunks are Uint8Array objects, and their production surfaces accept Uint8Array or strings (which are UTF-8 encoded), following the [ITER-STREAMS] Writer contract. Chunks of any other type are rejected with a TypeError.
The ReadableStreamIterReader, by contrast, is generic: it may be acquired on any ReadableStream and performs no chunk coercion. On a byte-carrying stream its batches are Uint8Array[] and the reader is a conforming [ITER-STREAMS] ByteReadableStream. On a value-oriented stream, batches are arrays of whatever the stream carries — batching still amortizes iteration cost, but the reader does not claim integration with the [ITER-STREAMS] byte-oriented utilities.
5.3. Internal state: the slots buffer
An iter-type stream’s internal state is the [ITER-STREAMS] model, not the Streams Standard’s chunk queue: a slots buffer of batches, a byte budget, and a buffered byte count, governed by a backpressure policy. Each write or writev occupies one slot; a slot’s byte size is the sum of its chunks' byte lengths.
The byte budget is configured through the stream’s queuing strategy: highWaterMark is the budget in bytes. If no strategy is provided, the budget is an implementation-defined default of at least 16384 bytes, mirroring [ITER-STREAMS]. A provided value below the implementation’s minimum MAY be clamped to that minimum. A size function MUST NOT be provided for iter-type streams; if present, the constructor throws a RangeError. Byte accounting is intrinsic.
Buffered bytes are decremented as chunks leave the buffer. When a facade consumes a slot partially (chunk at a time), the buffered byte count decreases per chunk as each chunk is consumed, not when the slot empties. This keeps producer-observable backpressure signals (canWrite, the drainable protocol) consistent between batch-native and facade consumption.
Note: The [ITER-STREAMS] overshoot rule — a write is accepted whenever buffered bytes are below the budget, and then counts fully even if it overshoots — means buffered bytes can exceed the budget. Through the default-writer facade this surfaces as a negative desiredSize, which is exactly the Streams Standard’s existing behavior for an over-full queue. The two models align without adjustment.
5.4. The compatibility invariant
Every existing Streams Standard operation works on iter-type streams through the facades:
| Stream | default reader / writer | byob reader | iter reader / writer |
|---|---|---|---|
default ReadableStream
| existing behavior | TypeError (existing)
| adapt: drain queue into a batch |
byte ReadableStream
| existing behavior | existing behavior | adapt: drain queue into a batch |
| iter readable stream | adapt: unbatch (§ 8.4 The default reader facade) | TypeError
| native |
default WritableStream
| existing behavior | — | adapt: Writer over default machinery (§ 12.3 On any other WritableStream: adapted)
|
| iter writable stream | adapt: writes into the slots buffer (§ 11.4 The default writer facade) | — | native |
tee(), piping between mixed types, async iteration of the stream itself (which remains chunk-at-a-time for compatibility), and integration points in other specifications that consume ReadableStream or WritableStream all operate through the default facades and continue to work unchanged.
5.5. Fusion
When both ends of a pipe are iter-type, the pipe is not a pump loop between two buffers. It is a fused pipe: the destination sink’s iteration is bound directly to the source’s batch sequence, through any composed transforms, back to the origin producer’s byte budget. Data moves only when the ultimate consumer pulls. See § 14 Piping.
6. Web IDL definitions
6.1. IterWriteOptions
dictionary {IterWriteOptions AbortSignal ; };signal
The IterWriteOptions dictionary mirrors the [ITER-STREAMS] WriteOptions dictionary.
6.2. The ReadableStreamIterReader interface
[Exposed=*]interface {ReadableStreamIterReader (constructor ReadableStream );stream Promise <ReadableStreamReadResult >read ();undefined releaseLock (); };ReadableStreamIterReader includes ReadableStreamGenericReader ;
The ReadableStreamIterReader interface provides batched reads. It is acquired via getReader({ mode: "iter" }) on any ReadableStream. See § 7 The iter reader.
6.3. The ReadableStreamIterController interface
[Exposed=*]interface {ReadableStreamIterController readonly attribute boolean ?;canWrite Promise <undefined >((write Uint8Array or USVString ),chunk optional IterWriteOptions = {});options Promise <undefined >(writev sequence <(Uint8Array or USVString )>,chunks optional IterWriteOptions = {});options boolean ((writeSync Uint8Array or USVString ));chunk boolean (writevSync sequence <(Uint8Array or USVString )>);chunks Promise <unsigned long long >(end optional IterWriteOptions = {});options long long ();endSync undefined (fail optional any ); };reason
The ReadableStreamIterController interface is the controller for iter readable streams. It implements the [ITER-STREAMS] Writer interface; its member semantics are those of the Writer interface, bound to the stream’s slots buffer. See § 8 Iter readable streams.
6.4. The WritableStreamIterController interface
[Exposed=*]interface {WritableStreamIterController readonly attribute AbortSignal ; };signal
The WritableStreamIterController interface is the controller for iter writable streams. It conforms to the [ITER-STREAMS] ByteReadableStream structural type: it has a Symbol.asyncIterator method returning an async iterator that yields batches. See § 11 Iter writable streams.
The signal attribute mirrors the Streams Standard’s WritableStreamDefaultController.signal: it is aborted when the stream is aborted, allowing a sink engaged in long-running work between iteration steps to observe cancellation.
6.5. The WritableStreamIterWriter interface
[Exposed=*]interface {WritableStreamIterWriter (constructor WritableStream );stream readonly attribute boolean ?;canWrite readonly attribute Promise <undefined >;closed Promise <undefined >((write Uint8Array or USVString ),chunk optional IterWriteOptions = {});options Promise <undefined >(writev sequence <(Uint8Array or USVString )>,chunks optional IterWriteOptions = {});options boolean ((writeSync Uint8Array or USVString ));chunk boolean (writevSync sequence <(Uint8Array or USVString )>);chunks Promise <unsigned long long >(end optional IterWriteOptions = {});options long long ();endSync undefined (fail optional any );reason undefined (); };releaseLock
The WritableStreamIterWriter interface implements the [ITER-STREAMS] Writer interface over a WritableStream. It is acquired via getWriter({ mode: "iter" }). See § 12 Iter writers.
6.6. Options dictionaries
dictionary {WritableStreamGetWriterOptions DOMString ;mode DOMString ; };backpressure dictionary {ReadableStreamFromOptions DOMString ; };type partial dictionary UnderlyingSource {DOMString = "strict"; };backpressure partial dictionary UnderlyingSink {DOMString = "strict"; };backpressure
The backpressure members take [ITER-STREAMS] BackpressurePolicy values. They are consulted only when the corresponding type is "iter"; for other stream types they are ignored. For iter writable streams, only "strict" and "unbounded" are permitted; see § 11.3 Backpressure policy.
Note: BackpressurePolicy is defined by [ITER-STREAMS]. The members are declared as DOMString because the enumeration is defined in another specification; values are validated in prose.
6.7. Protocol conformance
Objects defined by this specification participate in the [ITER-STREAMS] protocol symbols:
-
ReadableStreamIterControllerandWritableStreamIterWritereach provide a method keyed bySymbol.for('Stream.drainableProtocol'), with the semantics defined forWriterobjects by [ITER-STREAMS]: it returns a promise resolvingtruewhen buffered bytes fall below the byte budget, resolvingfalseif the writer closes while waiting, and rejecting if the writer errors.Stream.ondrain()therefore works with both. -
Both
Writer-implementing interfaces provideSymbol.asyncDisposeandSymbol.disposewith the disposal semantics defined by [ITER-STREAMS] (dispose callsfail()with no argument; async dispose of a closing writer awaits the graceful drain). -
WritableStreamIterControllerandReadableStreamIterReaderprovideSymbol.asyncIterator, conforming to theByteReadableStreamstructural type when the underlying chunks areUint8Arrayobjects. -
ReadableStreamgains aSymbol.for('Stream.toAsyncStreamable')method; see § 10 The toAsyncStreamable hook.
7. The iter reader
7.1. Acquisition
A ReadableStreamIterReader is acquired by calling getReader({ mode: "iter" }) on any ReadableStream, regardless of the stream’s underlying source type, or by its constructor. Acquisition locks the stream, exactly as for other reader types: while the reader is active, locked is true and acquiring any other reader throws a TypeError.
Unlike mode: "byob", mode: "iter" does not constrain the stream’s controller type. The reader performs no chunk coercion; see § 5.2 Bytes-only types, generic reader.
7.2. Reading
read() method reads the next batch. It returns a promise for a ReadableStreamReadResult:
- If the stream is errored, the promise rejects with the stored error.
- If chunks are available — in the slots buffer for an iter readable stream, or in the internal queue for other stream types — the promise resolves with
{ value, done: false }, where value is an array containing all currently available chunks, drained from the stream, in order. For an iter readable stream, the array MAY correspond to one or more whole or partial slots; boundaries are not preserved (§ 5.1 Batch boundaries are not semantic). - Otherwise, if the stream is closed, the promise resolves with
{ value: undefined, done: true }. - Otherwise, the read participates in the stream’s normal demand signaling (for pull-based sources, a pull is requested) and the promise resolves with a batch containing at least one chunk once data becomes available, or with
{ value: undefined, done: true }if the stream closes first.
Draining all available chunks per read is the batching amortization: one promise resolution delivers everything buffered. Implementations MAY cap the drained byte size of a single batch at an implementation-defined limit.
Reads decrement the buffered byte count and notify drain waiters per § 5.3 Internal state: the slots buffer, propagating demand to the producer.
7.3. Async iteration
ReadableStreamIterReader is obtained via its Symbol.asyncIterator method. Its behavior:
next()performs the same operation asread(), returning its result as the iterator result object.return(reason)cancels the stream with reason (as if byreader.cancel(reason)), releases the lock, and resolves with a done result.throw(reason)behaves asreturn(reason)and then rejects with reason.
A consumer breaking out of for await...of therefore cancels the source, matching the [ITER-STREAMS] convention that a consumer that stops iterating tears the pipeline down to the source.
When the stream’s chunks are Uint8Array objects, the reader is a conforming ByteReadableStream: it may be passed directly to Stream.pull(), Stream.text(), Stream.pipeTo(), and any other [ITER-STREAMS] consumer.
7.4. Reader generics
ReadableStreamIterReader includes the Streams Standard’s generic reader behaviors: the closed promise and cancel(reason) behave as for a default reader. releaseLock() releases the lock; pending read promises reject with a TypeError, matching default reader semantics.
8. Iter readable streams
8.1. Construction
An iter readable stream is constructed with type: "iter":
const rs= new ReadableStream({ type: 'iter' , async start( writer) { // writer is the ReadableStreamIterController — an iter-streams Writer if ( ! writer. writeSync( chunk)) await writer. write( chunk); const n= writer. endSync(); if ( n< 0 ) await writer. end(); } }, { highWaterMark: 65536 });
Constructor rules, applied when the underlying source’s type is "iter":
-
startis required. If absent, throw aTypeError. Thestartcallback is the only mechanism for acquiring the controller; a stream that can never be written to or ended is unusable by construction. -
If
pullorautoAllocateChunkSizeis present, throw aTypeError. The pull model is provided byReadableStream.from()(§ 9 ReadableStream.from() in batch mode), not by the controller. -
cancelis permitted and is invoked per § 8.3 Cancellation. -
The queuing strategy provides the byte budget per § 5.3 Internal state: the slots buffer; a
sizefunction throws aRangeError. -
The underlying source’s
backpressuremember selects the [ITER-STREAMS] backpressure policy for the controller; the default is"strict". All four policies are permitted. -
startis invoked with theReadableStreamIterControlleras its argument. Ifstartthrows or its returned promise rejects, the stream is errored with the thrown value, matching existing underlying-source semantics.
8.2. The controller is a Writer
The ReadableStreamIterController implements the [ITER-STREAMS] Writer interface, bound to the stream:
-
The slots buffer is the stream’s internal state; the byte budget and backpressure policy are those configured at construction.
-
write(),writev(),writeSync(),writevSync(),end(),endSync(),fail(), andcanWritehave the semantics defined by [ITER-STREAMS] for theWriterinterface, including string encoding, the try-fallback pattern, atomic writev, the pending writes queue for"strict"and"unbounded", and the end sentinel. -
end()resolves, with the total bytes written, when the last batch (and end sentinel) has been consumed; the stream then closes. From the perspective of readers, closure is ordinary stream closure. -
fail(reason)errors the stream with reason; pending reads reject with reason. -
The controller carries the drainable protocol and disposal symbols per § 6.7 Protocol conformance.
Chunks are accepted zero-copy: a written Uint8Array is not copied or detached, matching [ITER-STREAMS]. Producers MUST NOT mutate a chunk after writing it; see § 16 Security considerations.
8.3. Cancellation
cancel(), a reader’s cancel, or iterator return):
- The slots buffer is discarded.
- The controller transitions to the errored state with the cancel reason: subsequent writes reject (or return
falsefrom sync variants),canWritereturnsnull, pending write promises reject, a pendingend()promise rejects, and drain waiters are notified with the reason per the [ITER-STREAMS]fail()semantics. - The underlying source’s
cancelcallback, if any, is invoked with the reason, per existing Streams Standard semantics.
This is the web-streams expression of the [ITER-STREAMS] rule that a consumer that stops iterating signals cancellation to the writer.
8.4. The default reader facade
Default readers (and every operation built on them: async iteration of the stream, tee(), mixed-type piping, and consumers in other specifications) interoperate with iter readable streams through the unbatching facade:
-
A default read takes the next single chunk, in order, from the front of the slots buffer — consuming slots progressively, with a cursor into a partially consumed slot.
-
Buffered bytes decrement per chunk (§ 5.3 Internal state: the slots buffer).
-
Closure (after the end sentinel) and errors surface as ordinary default-reader closure and errors.
The flattened chunk sequence observed through the facade is identical to the sequence observed through batch reads.
9. ReadableStream.from() in batch mode
ReadableStream.from() is amended to accept an options argument: ReadableStream.from(source, { type: "iter" }).
type option is "iter", batch-mode from performs the following:
- Let iterable be source, interpreted as an async iterable if it has
Symbol.asyncIterator, else as a sync iterable if it hasSymbol.iterator; otherwise throw aTypeError. -
Return a new iter readable stream whose batch sequence is produced by lazily iterating iterable:
- Each value yielded by iterable that is an array is interpreted as a batch: its elements are the chunks. Each element must be a
Uint8Array; otherwise the stream is errored with aTypeError. - Each yielded value that is a
Uint8Arrayis interpreted as a single-chunk batch. - Any other yielded value errors the stream with a
TypeError. - Iteration is demand-driven: at most one iteration step of iterable is outstanding at a time, and steps are taken only to satisfy reads. Implementations MAY read ahead up to the byte budget.
- When iterable completes, the stream closes. If iteration throws, the stream errors with the thrown value.
- If the stream is cancelled, the iterator’s
return()method is invoked, per existingReadableStream.from()semantics.
- Each value yielded by iterable that is an array is interpreted as a batch: its elements are the chunks. Each element must be a
When the type option is absent or undefined, ReadableStream.from() behaves exactly as currently specified (each yielded value is a single chunk of a default stream). No inspection-based batch detection is performed: an async iterable of Uint8Array[] passed without the option produces a default stream whose chunks are arrays, exactly as today.
Note: Batch-mode from() supplies the pull model for iter-type streams; type: "iter" construction with start supplies the push model. This mirrors the [ITER-STREAMS] division between Stream.from() and Stream.push(). Note the difference in element handling: Stream.from() normalizes forgivingly (strings encoded, buffers wrapped); batch-mode from() is strict, accepting only Uint8Array chunks. Applications wanting the forgiving behavior can compose the two specifications: ReadableStream.from(Stream.from(input), { type: "iter" }).
10. The toAsyncStreamable hook
ReadableStream.prototype gains a method keyed by Symbol.for('Stream.toAsyncStreamable'):
ReadableStream stream:
- If stream is locked, throw a
TypeError. - Return the result of
getReader({ mode: "iter" })on stream.
Because [ITER-STREAMS] Stream.from() consults Symbol.for('Stream.toAsyncStreamable') before the iteration protocols, this single method upgrades every iterable-streams entry point to batched reads for any ReadableStream:
// All of these consume the ReadableStream via batched reads: await Stream. text( webReadableStream); Stream. pull( webReadableStream, gzip); await Stream. pipeTo( webReadableStream, writer);
Note: For value-oriented streams, the returned reader yields batches of non-byte values, which the [ITER-STREAMS] normalization rules will reject with a TypeError when consumed by its byte-oriented utilities — the same outcome as consuming such a stream through the Symbol.asyncIterator fallback.
11. Iter writable streams
11.1. Construction
An iter writable stream is constructed with type: "iter" in the underlying sink. The underlying sink’s type member is currently reserved by the Streams Standard (any value throws a RangeError); this specification defines its first value.
const ws= new WritableStream({ type: 'iter' , async start( source) { // source is the WritableStreamIterController — an AsyncIterable<Uint8Array[]> try { for await ( const chunksof source) { await socket. writev( chunks); } await socket. close(); // iteration completed: stream is closing } finally { // runs on completion, abort (iteration throws), or sink failure } } }, { highWaterMark: 65536 });
Constructor rules, applied when the underlying sink’s type is "iter":
-
startis required; if absent, throw aTypeError. The consumption loop is the sink. -
If
write,close, orabortis present, throw aTypeError. The per-chunk callback lifecycle is replaced by the iteration protocol: data arrives as batches from iteration; closure is observed as iteration completion; abort is observed as the iteration throwing. -
The queuing strategy provides the byte budget per § 5.3 Internal state: the slots buffer; a
sizefunction throws aRangeError. -
The underlying sink’s
backpressuremember selects the policy; only"strict"and"unbounded"are permitted —"drop-oldest"and"drop-newest"throw aRangeError. See § 11.3 Backpressure policy. -
startis invoked with theWritableStreamIterControlleras its argument.
11.2. The controller is the consumption side
The WritableStreamIterController conforms to the [ITER-STREAMS] ByteReadableStream structural type. Its async iterator:
-
Yields, per step, a batch containing all chunks currently in the slots buffer — draining every available slot and concatenating their contents in order. This coalescing is where batching is regained even when producers wrote chunk-at-a-time; boundaries are not preserved (§ 5.1 Batch boundaries are not semantic).
-
If the buffer is empty, waits for data.
-
Completes (
done) after the end sentinel — enqueued when the stream is closed viawriter.close()or a writer’send()— has been reached and all prior data yielded. -
Throws the abort reason at the suspended (or next) iteration step when the stream is aborted.
Sink lifecycle maps onto start’s settlement:
-
The stream’s close request is fulfilled when
start’s returned promise fulfills after iteration has completed. Sink flush work after the loop (in the example above,socket.close()) therefore delays close fulfillment, exactly as a default sink’sclose()callback would. -
If
start’s promise rejects — the loop threw, or the sink failed — the stream is errored with the rejection value.
During a fused pipe, the controller’s iteration is bound to the pipe’s composed source rather than the slots buffer; see § 14 Piping.
11.3. Backpressure policy
Iter writable streams support only the "strict" and "unbounded" policies. The dropping policies are excluded: data written to a WritableStream is committed toward a sink, and silently discarding committed data is not a meaningful sink behavior. (The dropping policies remain available where [ITER-STREAMS] defines them — push streams and broadcasts — and on iter readable streams, whose controller is a push-model writer.)
The policy is a property of the stream, configured at construction. It governs all producers: iter writers natively, and default writers through the facade.
11.4. The default writer facade
Default writers (and every operation built on them, including mixed-type piping) interoperate with iter writable streams through the facade:
-
write(chunk)accepts aUint8Arrayor a string (UTF-8 encoded); any other chunk rejects with aTypeError. Accepted chunks enter the slots buffer as single-chunk slots, subject to the budget and policy. -
desiredSizeis the byte budget minus buffered bytes (possibly negative).readyresolves when buffered bytes are below the budget, cycling per the Streams Standard’s conventions; its transitions coincide with [ITER-STREAMS] drain notification. -
close()enqueues the end sentinel; the close promise fulfills per § 11.2 The controller is the consumption side. -
abort(reason)aborts the stream: pending facade writes reject, and the controller’s iteration throws reason.
12. Iter writers
12.1. Acquisition
A WritableStreamIterWriter is acquired by calling getWriter({ mode: "iter" }) on any WritableStream, or by its constructor. Acquisition locks the stream exactly as acquiring a default writer does.
The backpressure option is permitted only when the stream is not an iter writable stream (see § 12.3 On any other WritableStream: adapted); on an iter writable stream the policy belongs to the stream, and specifying the option throws a TypeError.
12.2. On an iter writable stream: native
On an iter writable stream, the writer’s operations bind directly to the stream’s slots buffer, budget, and policy — the Writer semantics of [ITER-STREAMS] with no adaptation. writev() batches occupy single slots and reach the sink’s iteration whole (subject to coalescing). end() is equivalent to closing the stream and resolves with the total bytes written through this writer. fail(reason) aborts the stream.
12.3. On any other WritableStream: adapted
On a default WritableStream, the writer adapts the Writer contract onto the default machinery:
Writer surface
| Default WritableStream mechanism
|
|---|---|
canWrite
| true if desiredSize > 0; false if ≤ 0; null if the stream is closed, closing, or errored
|
| drainable protocol | derived from the ready promise, wrapped to the true / false (closed) / reject (errored) contract
|
write(chunk)
| strings UTF-8 encoded; forwarded as a single write |
writev(chunks)
| forwarded as sequential writes, in order; the returned promise settles when all have settled. Atomicity is best-effort: if a write fails mid-batch, the stream is already errored and no further chunks are written |
writeSync() / writevSync()
| if desiredSize > 0, forward (per write/writev above) and return true; otherwise return false and write nothing — the try-fallback signal
|
end()
| close(); resolves with total bytes written through this writer
|
endSync()
| returns −1 unless the writer is already closing or closed (sink close is asynchronous); the try-fallback signal to await end()
|
fail(reason)
| abort(reason). Iter-level pending write promises reject synchronously with reason; the abort itself settles per Streams Standard semantics
|
The backpressure option (default "strict") selects the admission policy the adapter applies in front of the stream’s queue: "strict" tolerates one un-awaited pending write past exhaustion before rejecting with a RangeError; "unbounded" queues without limit. The dropping policies throw a TypeError, per § 11.3 Backpressure policy.
Note: This adaptation is what makes Stream.pipeTo(source, transforms, ws.getWriter({ mode: "iter" })) fully correct for any WritableStream. A default writer half-satisfies the [ITER-STREAMS] pipe destination duck-type today (it has write but not writev, end, or fail), silently losing batching, close propagation, and error propagation. The iter writer completes the contract.
13. Iter transform streams
13.1. Construction
A batch-native TransformStream is constructed using the transformer dictionary’s readableType and writableType members — reserved by the Streams Standard, given their first values here:
const ts= new TransformStream({ readableType: 'iter' , writableType: 'iter' , async * transform( source, { signal}) { // source is AsyncIterable<Uint8Array[] | null> — null is the flush signal for await ( const chunksof source) { if ( chunks=== null ) { yield finalize(); break ; } yield process( chunks); } } });
Constructor rules:
-
readableTypeandwritableTypemust both be"iter"or both absent. Mixed combinations throw aRangeError(reserved for future work). -
When both are
"iter": the readable side is an iter readable stream; the writable side is an iter writable stream; and the transformer follows the contract below.start,flush, andcancelmembers throw aTypeErrorif present — startup work belongs to the transform itself, flush is expressed by thenullflush signal, and cancellation is delivered through the abort signal and the iteration protocols. -
The transformer may provide exactly one of
transformortransformBatch; providing both throws aTypeError. Providing neither yields an identity transform (batch pass-through).
13.2. The transformer contract
The contract is the [ITER-STREAMS] transform contract:
-
**
transform(source, options)** — the stateful form, with the exact signature of an [ITER-STREAMS] stateful transform: source is anAsyncIterable<Uint8Array[] | null>(the writable side’s data, with a finalnullflush signal appended after end-of-stream), options carries the requiredAbortSignal, and the return value is an async iterable whose yields are normalized per the [ITER-STREAMS] transform output rules and become the readable side’s batches.Note: A transformer dictionary of this form — an object with a function-valued
transformproperty — is an [ITER-STREAMS] stateful transform. The same object can be passed toStream.pull()or supplied to aTransformStream; the two specifications converge on one contract by construction. -
**
transformBatch(chunks, options)** — the stateless form, with the semantics of an [ITER-STREAMS] stateless transform: invoked once per batch withUint8Array[], and once withnullas the flush signal; the return value is normalized per the same output rules.Note: The stateless form uses a distinct member name rather than
transformbecause [ITER-STREAMS] detects "object with a function-valuedtransformproperty" as a stateful transform. Reusingtransformfor a per-batch function would make the dictionary misparse when handed to iterable-streams APIs.
13.3. Laziness and composition
The transform executes pull-through: no transform code runs until the readable side is consumed. The writable side’s slots buffer is drained only as the transform’s source iterable is pulled, which happens only as the transform’s output is pulled. Backpressure spans the transform: a slow consumer of the readable side propagates, through the transform, to the writable side’s budget and its producers.
In a chain of iter-type transform streams connected by fused pipes, this composes into a single lazily evaluated pipeline — the [ITER-STREAMS] compose-transform-pipeline model expressed through web streams plumbing. See § 14 Piping.
14. Piping
14.1. Type dispatch
The Streams Standard’s pipe-to algorithm gains a type dispatch as its first step:
-
If the source is an iter readable stream and the destination is an iter writable stream, the pipe is a fused pipe, specified in § 14.2 The fused pipe.
-
Otherwise, the existing algorithm applies unchanged, acquiring a default reader and a default writer; the facades make it correct for every mixed combination.
pipeThrough() requires no changes: it is pipe-to plus returning the readable side, and each leg dispatches independently.
14.2. The fused pipe
- Lock rs and ws for the duration of the pipe, exactly as the existing algorithm does. Internally, rs is consumed as batches (as if through an iter reader).
- Bind ws’s
WritableStreamIterControlleriteration to rs’s batch sequence: while the pipe is active, the controller’s current upstream is the source’s batch sequence instead of ws’s own slots buffer. Piped data does not enter ws’s buffer. - Data flows only as the sink iterates: each sink iteration step pulls the next batch from rs, which drains rs’s slots buffer and notifies its producer’s drain waiters. Backpressure is coupled end to end with no intermediate elasticity.
- Batches may be split or merged in flight; the flattened chunk sequence is preserved (§ 5.1 Batch boundaries are not semantic).
-
Shutdown propagation, byte-for-byte with the existing algorithm’s constraints:
Event Default behavior With prevent*flagrs errors the sink’s iteration throws the reason; ws is errored; the pipe promise rejects preventAbort: the current upstream detaches; the sink’s iteration suspends awaiting ws’s own buffer; the pipe promise rejects rs closes ws is closed as if by a writer’s close(): the end sentinel follows the piped data; the sink’s iteration completes; the pipe promise fulfills after close fulfillmentpreventClose: the current upstream detaches; the sink’s iteration suspends awaiting ws’s buffer; the pipe promise fulfills ws errors (sink loop throws; startrejects)rs is cancelled with the reason; the pipe promise rejects preventCancel: rs is not cancelled; the pipe promise rejects signal aborted as in the existing algorithm: error both sides with the abort reason, subject to preventAbort and preventCancel; the pipe promise rejects with the reason - On pipe completion for any reason that leaves ws writable, the current upstream reverts to ws’s slots buffer, so subsequently acquired writers feed the same, still-running sink iteration — the sink observes one continuous sequence stitched across pipe and non-pipe phases.
Note: Nothing in the fused pipe is a mere optimization license; the laziness is normative. An implementation MUST NOT eagerly drain the source into destination-side buffering during a fused pipe — the absence of intermediate elasticity is observable to the source-side producer through its budget, and it is the point.
Note: A chain such as rs.pipeThrough(a).pipeThrough(b).pipeTo(ws), with every stage iter-type, collapses hop by hop: each fused pipe binds a sink iteration to an upstream batch sequence, and each iter transform connects its legs lazily (§ 13.3 Laziness and composition). The result is a single consumer-driven pipeline from ws’s sink back to rs’s producer budget, semantically equivalent to a single [ITER-STREAMS] pull pipeline.
14.3. Mixed-type pipes
Mixed-type pipes use the existing algorithm through the facades. Two consequences worth noting:
-
Piping any source into an iter writable stream recovers batching at the destination despite the chunk-at-a-time facade: chunks land in the slots buffer individually, and the sink’s iteration drains all available slots per step (§ 11.2 The controller is the consumption side).
-
Piping a source of non-byte chunks into an iter writable stream errors the destination with a
TypeErrorat the first such chunk (the facade’s chunk contract), which then propagates per the existing algorithm’s error rules.
15. Modifications to the Streams Standard
This section enumerates the amendments this specification makes to [STREAMS]. A runtime implementing both specifications behaves as if the Streams Standard were modified as follows:
-
**
ReadableStreamReaderMode**: add the enumeration value"iter". -
**
getReader()**: when the options'modeis"iter", return a newReadableStreamIterReaderfor the stream (§ 7.1 Acquisition). No controller-type restriction applies. -
**
ReadableStreamType**: add the enumeration value"iter". -
**
ReadableStreamconstructor**: when the underlying source’stypeis"iter", set up aReadableStreamIterControllerper § 8.1 Construction, including thestart-required, no-pull, no-sizerules and thebackpressuremember. -
**
ReadableStream.from()**: accept a second argument, aReadableStreamFromOptions; when itstypeis"iter", behave per § 9 ReadableStream.from() in batch mode. -
**
ReadableStream.prototype**: add theSymbol.for('Stream.toAsyncStreamable')method defined in § 10 The toAsyncStreamable hook. -
**Underlying sink
type**: replace the unconditionalRangeErrorfor a presenttypemember with: if the value is"iter", set up aWritableStreamIterControllerper § 11.1 Construction; for any other non-undefinedvalue, throw aRangeErroras today. -
**
getWriter()**: accept an optionalWritableStreamGetWriterOptionsargument; whenmodeis"iter", return a newWritableStreamIterWriterper § 12 Iter writers.getWriter()with no arguments is unchanged. -
**Transformer
readableType/writableType**: replace the unconditionalRangeErrorfor present members with: if both are"iter", construct the transform per § 13 Iter transform streams; any other present, non-undefinedcombination throws aRangeErroras today. -
Pipe-to: prepend the type dispatch of § 14.1 Type dispatch; when both ends are iter-type, the pipe is the fused pipe of § 14.2 The fused pipe.
-
Default reader and writer interoperation with iter-type streams per § 8.4 The default reader facade and § 11.4 The default writer facade.
No other Streams Standard behavior is modified. In particular, async iteration of a ReadableStream itself remains chunk-at-a-time; tee() is unchanged (operating through the default facade on iter streams); and the "bytes" type, BYOB machinery, and default streams are untouched.
16. Security considerations
-
Zero-copy sharing: Chunks pass through iter-type streams without copying or detaching. A producer that mutates a
Uint8Arrayafter writing it can observe or influence a consumer, and vice versa. This matches [ITER-STREAMS]; applications crossing trust boundaries should copy explicitly. This differs from the"bytes"stream type, which transfers buffers. -
Unbounded buffering: The
"unbounded"policy permits unlimited pending writes; producers using it are responsible for their own memory discipline, as in [ITER-STREAMS]. -
Budget as a signal: Byte budgets and drain timing are producer-observable. Implementations that adjust budgets dynamically (as [ITER-STREAMS] permits) should consider whether budget changes leak cross-origin or cross-tenant information in multi-tenant runtimes.
17. Open issues
17.1. Mixed transform leg types
readableType: "iter" with a default writable side (or the converse) is currently a RangeError. Mixed legs are coherent in principle — the facades would carry them — but the transformer contract for a mixed transform is unclear. Deferred.
17.2. Stateless transformer member name
transformBatch was chosen because transform is claimed by the stateful form for [ITER-STREAMS] shape compatibility. Better names may exist.
17.3. Budget floor
This specification mirrors the [ITER-STREAMS] 16384-byte budget floor by permitting implementations to clamp smaller highWaterMark values. Whether small explicit budgets should instead be honored (useful for testing) is an open question for both specifications.
17.4. Upstreaming
The amendments in § 15 Modifications to the Streams Standard are confined to enum values, reserved dictionary members, and a type dispatch in pipe-to, specifically to keep an upstreaming path into [STREAMS] open. Whether and when to propose that is a process question outside this document.
Index
Terms defined by this specification
- async iterator, in § 7.3
-
backpressure
- dict-member for UnderlyingSink, in § 6.6
- dict-member for UnderlyingSource, in § 6.6
- dict-member for WritableStreamGetWriterOptions, in § 6.6
- batch, in § 4.2
- batch-mode from, in § 9
-
canWrite
- attribute for ReadableStreamIterController, in § 6.3
- attribute for WritableStreamIterWriter, in § 6.5
- closed, in § 6.5
-
constructor(stream)
- constructor for ReadableStreamIterReader, in § 6.2
- constructor for WritableStreamIterWriter, in § 6.5
- current upstream, in § 14.2
-
end()
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
end(options)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
endSync()
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
- facade, in § 4.5
-
fail()
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
fail(reason)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
- flattened chunk sequence, in § 5.1
- fused pipe, in § 4.6
- Iterable Web Streams Extensions, in § 4.1
- iter readable stream, in § 4.3
- iter writable stream, in § 4.4
- IterWriteOptions, in § 6.1
- mode, in § 6.6
- per chunk, in § 5.3
- read(), in § 7.2
- ReadableStreamFromOptions, in § 6.6
- ReadableStreamIterController, in § 6.3
- ReadableStreamIterReader, in § 6.2
- ReadableStreamIterReader(stream), in § 6.2
- ReadableStream toAsyncStreamable method, in § 10
-
releaseLock()
- method for ReadableStreamIterReader, in § 7.4
- method for WritableStreamIterWriter, in § 6.5
-
signal
- attribute for WritableStreamIterController, in § 6.4
- dict-member for IterWriteOptions, in § 6.1
- slots buffer, in § 5.3
- type, in § 6.6
- WritableStreamGetWriterOptions, in § 6.6
- WritableStreamIterController, in § 6.4
- WritableStreamIterWriter, in § 6.5
- WritableStreamIterWriter(stream), in § 6.5
-
write(chunk)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
write(chunk, options)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
writeSync(chunk)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
writev(chunks)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
writev(chunks, options)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
-
writevSync(chunks)
- method for ReadableStreamIterController, in § 6.3
- method for WritableStreamIterWriter, in § 6.5
Terms defined by reference
-
[DOM] defines the following terms:
- AbortSignal
-
[STREAMS] defines the following terms:
- ReadableStream
- ReadableStreamGenericReader
- ReadableStreamReadResult
- TransformStream
- UnderlyingSink
- UnderlyingSource
- WritableStream
- cancel()
- highWaterMark
- locked
- size
-
[WEBIDL] defines the following terms:
- DOMString
- Promise
- RangeError
- TypeError
- USVString
- Uint8Array
- any
- boolean
- long long
- sequence
- undefined
- unsigned long long
IDL Index
dictionary {IterWriteOptions AbortSignal ; }; [Exposed=*]signal interface {ReadableStreamIterReader (constructor ReadableStream );stream Promise <ReadableStreamReadResult >read ();undefined releaseLock (); };ReadableStreamIterReader includes ReadableStreamGenericReader ; [Exposed=*]interface {ReadableStreamIterController readonly attribute boolean ?;canWrite Promise <undefined >((write Uint8Array or USVString ),chunk optional IterWriteOptions = {});options Promise <undefined >(writev sequence <(Uint8Array or USVString )>,chunks optional IterWriteOptions = {});options boolean ((writeSync Uint8Array or USVString ));chunk boolean (writevSync sequence <(Uint8Array or USVString )>);chunks Promise <unsigned long long >(end optional IterWriteOptions = {});options long long ();endSync undefined (fail optional any ); }; [Exposed=*]reason interface {WritableStreamIterController readonly attribute AbortSignal ; }; [Exposed=*]signal interface {WritableStreamIterWriter (constructor WritableStream );stream readonly attribute boolean ?;canWrite readonly attribute Promise <undefined >;closed Promise <undefined >((write Uint8Array or USVString ),chunk optional IterWriteOptions = {});options Promise <undefined >(writev sequence <(Uint8Array or USVString )>,chunks optional IterWriteOptions = {});options boolean ((writeSync Uint8Array or USVString ));chunk boolean (writevSync sequence <(Uint8Array or USVString )>);chunks Promise <unsigned long long >(end optional IterWriteOptions = {});options long long ();endSync undefined (fail optional any );reason undefined (); };releaseLock dictionary {WritableStreamGetWriterOptions DOMString ;mode DOMString ; };backpressure dictionary {ReadableStreamFromOptions DOMString ; };type partial dictionary UnderlyingSource {DOMString = "strict"; };backpressure partial dictionary UnderlyingSink {DOMString = "strict"; };backpressure
Copyright & Software License
Ecma International
Rue du Rhone 114
CH-1204 Geneva
Tel: +41 22 849 6000
Fax: +41 22 849 6001
Web: https://ecma-international.org/
Copyright Notice
© 2026 Ecma International
This draft document may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published, and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this section are included on all such copies and derivative works. However, this document itself may not be modified in any way, including by removing the copyright notice or references to Ecma International, except as needed for the purpose of developing any document or deliverable produced by Ecma International.
This disclaimer is valid only prior to final version of this document. After approval all rights on the standard are reserved by Ecma International.
The limited permissions are granted through the standardization phase and will not be revoked by Ecma International or its successors or assigns during this time.
This document and the information contained herein is provided on an "AS IS" basis and ECMA INTERNATIONAL DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Software License
All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.