Repository: nodejs/node
Pull request: stream: rename `Duplex.toWeb()` type option to `readableType`

The webstreams standard uses the name `readableType` for a source property which specifies the `type` of the readable component of a constructed readable-writable pair.[^1] Although this is only future-proofed nomenclature at the moment, we should probably follow this convention here, for consistency and just in case we also need to support WritableStream types in the future.

This paradigm would also be consistent with `Duplex.toWeb()` taking `readableStrategy` and `writableStrategy` options in the future, if so desired.

Because `Duplex.toWeb()` is no longer marked as experimental, I believe that removing `options.type` entirely would need a full deprecation cycle, so I've just turned it into a silent alias for simplicity.

Refs: #58664

[^1]: https://streams.spec.whatwg.org/#dictdef-transformer

Changed files:
filename: doc/api/deprecations.md
patch: @@ -4415,6 +4415,21 @@ import { opendir } from 'node:fs/promises';
 }
 ```
 
+### DEP0201: Passing `options.type` to `Duplex.toWeb()`
+
+<!-- YAML
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/61632
+    description: Documentation-only deprecation.
+-->
+
+Type: Documentation-only
+
+Passing the `type` option to [`Duplex.toWeb()`][] is deprecated. To specify the
+type of the readable half of the constructed readable-writable pair, use the
+`readableType` option instead.
+
 [DEP0142]: #dep0142-repl_builtinlibs
 [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
 [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
@@ -4433,6 +4448,7 @@ import { opendir } from 'node:fs/promises';
 [`Buffer.isBuffer()`]: buffer.md#static-method-bufferisbufferobj
 [`Cipheriv`]: crypto.md#class-cipheriv
 [`Decipheriv`]: crypto.md#class-decipheriv
+[`Duplex.toWeb()`]: stream.md#streamduplextowebstreamduplex-options
 [`Error.isError`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/isError
 [`REPLServer.clearBufferedCommand()`]: repl.md#replserverclearbufferedcommand
 [`ReadStream.open()`]: fs.md#class-fsreadstream

filename: doc/api/stream.md
patch: @@ -3219,7 +3219,8 @@ changes:
       If no value is provided, the size will be `1` for all the chunks.
       * `chunk` {any}
       * Returns: {number}
-  * `type` {string} Must be 'bytes' or undefined.
+  * `type` {string} Specifies the type of the created `ReadableStream`. Must be
+    `'bytes'` or undefined.
 * Returns: {ReadableStream}
 
 ### `stream.Writable.fromWeb(writableStream[, options])`
@@ -3398,9 +3399,13 @@ duplex.once('readable', () => console.log('readable', duplex.read()));
 <!-- YAML
 added: v17.0.0
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/61632
+    description: Added the 'readableType' option to specify the ReadableStream
+                 type. The 'type' option is deprecated.
   - version: v25.4.0
     pr-url: https://github.com/nodejs/node/pull/58664
-    description: Add 'type' option to specify 'bytes'.
+    description: Added the 'type' option to specify the ReadableStream type.
   - version:
       - v24.0.0
       - v22.17.0
@@ -3410,7 +3415,9 @@ changes:
 
 * `streamDuplex` {stream.Duplex}
 * `options` {Object}
-  * `type` {string} Must be 'bytes' or undefined.
+  * `readableType` {string} Specifies the type of the `ReadableStream` half of
+    the created readable-writable pair. Must be `'bytes'` or undefined.
+    (`options.type` is a deprecated alias for this option.)
 * Returns: {Object}
   * `readable` {ReadableStream}
   * `writable` {WritableStream}

filename: lib/internal/webstreams/adapters.js
patch: @@ -624,7 +624,7 @@ function newStreamReadableFromReadableStream(readableStream, options = kEmptyObj
 
 /**
  * @param {Duplex} duplex
- * @param {{ type?: 'bytes' }} [options]
+ * @param {{ readableType?: 'bytes' }} [options]
  * @returns {ReadableWritablePair}
  */
 function newReadableWritablePairFromDuplex(duplex, options = kEmptyObject) {
@@ -641,9 +641,15 @@ function newReadableWritablePairFromDuplex(duplex, options = kEmptyObject) {
 
   validateObject(options, 'options');
 
+  const readableOptions = {
+    __proto__: null,
+    // DEP0201: 'options.type' is a deprecated alias for 'options.readableType'
+    type: options.readableType ?? options.type,
+  };
+
   if (isDestroyed(duplex)) {
     const writable = new WritableStream();
-    const readable = new ReadableStream({ type: options.type });
+    const readable = new ReadableStream({ type: readableOptions.type });
     writable.close();
     readable.cancel();
     return { readable, writable };
@@ -659,8 +665,8 @@ function newReadableWritablePairFromDuplex(duplex, options = kEmptyObject) {
 
   const readable =
     isReadable(duplex) ?
-      newReadableStreamFromStreamReadable(duplex, options) :
-      new ReadableStream({ type: options.type });
+      newReadableStreamFromStreamReadable(duplex, readableOptions) :
+      new ReadableStream({ type: readableOptions.type });
 
   if (!isReadable(duplex))
     readable.cancel();

filename: test/parallel/test-stream-duplex.js
patch: @@ -147,10 +147,14 @@ process.on('exit', () => {
     })
   });
 
-  const { writable, readable } = Duplex.toWeb(duplex, { type: 'bytes' });
+  const { writable, readable } = Duplex.toWeb(duplex, { readableType: 'bytes' });
   writable.getWriter().write(dataToWrite);
   const data = new Uint8Array(dataToRead.length);
   readable.getReader({ mode: 'byob' }).read(data).then(common.mustCall((result) => {
     assert.deepStrictEqual(Buffer.from(result.value), dataToRead);
   }));
+
+  // Ensure that the originally-named `options.type` still works as an alias for `options.readableType`
+  // `getReader({ mode: 'byob' })` throws if the underlying ReadableStream is not a byte stream
+  Duplex.toWeb(duplex, { type: 'bytes' }).readable.getReader({ mode: 'byob' });
 }