@@ -6402,7 +6402,7 @@ reason.
64026402 1. Perform ! [$SetUpTransformStreamDefaultController$] (|stream|, |controller|,
64036403 |transformAlgorithmWrapper|, |flushAlgorithmWrapper|).
64046404 1. Return |stream|.
6405- </dfn >
6405+ </div >
64066406
64076407<p algorithm> To <dfn export lt="create an identity TransformStream|creating an identity
64086408TransformStream"> create an identity {{TransformStream}} </dfn> , return the result of
@@ -6473,6 +6473,81 @@ base {{TransformStream}} class provides. The most common driver for such a wrapp
64736473custom [=constructor operation=] , but if your conceptual transform stream isn't meant to be
64746474constructed, then using {{TransformStream}} directly is fine.
64756475
6476+ <h3 id="other-specs-pairs">Other stream pairs</h3>
6477+
6478+ Apart from [=transform streams=] , discussed above, specifications often create pairs of [=readable
6479+ stream|readable=] and [=writable stream|writable=] streams. This section gives some guidance for
6480+ such situations.
6481+
6482+ In all such cases, specifications should use the names `readable` and `writable` for the two
6483+ properties exposing the streams in question. They should not use other names (such as
6484+ `input`/`output` or `readableStream`/`writableStream`), and they should not use methods or other
6485+ non-property means of access to the streams.
6486+
6487+ <h4 id="other-specs-duplex">Duplex streams</h4>
6488+
6489+ The most common readable/writable pair is a <dfn export>duplex stream</dfn> , where the readable and
6490+ writable streams represent two sides of a single shared resource, such as a socket, connection, or
6491+ device.
6492+
6493+ The trickiest thing to consider when specifying duplex streams is how to handle operations like
6494+ [=cancel a readable stream|canceling=] the readable side, or closing or [=abort a writable
6495+ stream|aborting=] the writable side. It might make sense to leave duplex streams "half open", with
6496+ such operations one one side not impacting the other side. Or it might be best to carry over their
6497+ effects to the other side, e.g. by specifying that your readable side's
6498+ <var ignore> [=ReadableStream/create/cancelAlgorithm=] </var> will [=WritableStream/close=] the
6499+ writable side.
6500+
6501+ <p class="example" id="example-basic-duplex"> A basic example of a duplex stream, created through
6502+ JavaScript instead of through specification prose, is found in [[#example-both]] . It illustrates
6503+ this carry-over behavior.
6504+
6505+ Another consideration is how to handle the creation of duplex streams which need to be acquired
6506+ asynchronously, e.g. via establishing a connection. The preferred pattern here is to have a
6507+ constructible class with a promise-returning property that fulfills with the actual duplex stream
6508+ object. That duplex stream object can also then expose any information that is only available
6509+ asynchronously, e.g. connection data. The container class can then provide convenience APIs, such as
6510+ a function to close the entire connection instead of only closing individual sides.
6511+
6512+ <p class="example" id="example-duplex-with-container"> An example of this more complex type of duplex
6513+ stream is the still-being-specified `WebSocketStream`. See its <a
6514+ href="https://github.com/ricea/websocketstream-explainer/blob/master/README.md"> explainer</a> and <a
6515+ href="https://docs.google.com/document/d/1La1ehXw76HP6n1uUeks-WJGFgAnpX2tCjKts7QFJ57Y/edit#"> design
6516+ notes</a> .
6517+
6518+ Because duplex streams obey the `readable`/`writable` property contract, they can be used with
6519+ {{ReadableStream/pipeThrough()}} . This doesn't always make sense, but it could in cases where the
6520+ underlying resource is in fact performing some sort of transformation.
6521+
6522+ <p class="example" id="example-duplex-pipethrough"> For an arbitrary WebSocket, piping through a
6523+ WebSocket-derived duplex stream doesn't make sense. However, if the WebSocket server is specifically
6524+ written so that it responds to incoming messages by sending the same data back in some transformed
6525+ form, then this could be useful and convenient.
6526+
6527+ <h4 id="other-specs-endpoints">Endpoint pairs</h4>
6528+
6529+ Another type of readable/writable pair is a <dfn export>endpoint pair</dfn> . In these cases the
6530+ readable and writable streams represent the two ends of a longer pipeline, with the intention that
6531+ web developer code insert [=transform streams=] into the middle of them.
6532+
6533+ <div class="example" id="example-endpoint-pair-usage">
6534+ Assuming we had a web-platform-provided function `createEndpointPair()`, web developers would write
6535+ code like so:
6536+
6537+ <xmp highlight="js">
6538+ const { readable, writable } = createEndpointPair();
6539+ readable.pipeThrough(new TransformStream(...)).pipeTo(writable);
6540+ </xmp>
6541+ </div>
6542+
6543+ <p class="example" id="example-endpoint-pair-webrtc"><cite> WebRTC Insertable Media using
6544+ Streams</cite> is an example of this technique, with its `sender.createEncodedStreams()` and
6545+ `receiver.createEncodedStreams()` methods.
6546+ <!-- TODO cite it and cross-link to it https://github.com/tobie/specref/issues/620 -->
6547+
6548+ Despite such endpoint pairs obeying the `readable`/`writable` property contract, it never makes
6549+ sense to pass them to {{ReadableStream/pipeThrough()}} .
6550+
64766551<h2 id="creating-examples">Examples of creating streams</h2>
64776552
64786553<div class="non-normative">
0 commit comments