From fc80faca6609575e90d036a2b15e206e94e06694 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sat, 5 Mar 2022 16:15:07 +0100 Subject: [PATCH] stream: graduate web streams from experimental and expose as globals --- .eslintrc.js | 17 +++ doc/api/globals.md | 153 ++++++++++++++++++++++++ doc/api/webstreams.md | 86 ++++++++++--- lib/.eslintrc.yaml | 34 ++++++ lib/internal/blob.js | 1 + lib/internal/bootstrap/browser.js | 54 +++++++++ test/common/index.js | 21 ++++ test/parallel/test-bootstrap-modules.js | 3 + test/parallel/test-global-webstreams.js | 24 ++++ test/wpt/test-streams.js | 104 ---------------- 10 files changed, 376 insertions(+), 121 deletions(-) create mode 100644 test/parallel/test-global-webstreams.js diff --git a/.eslintrc.js b/.eslintrc.js index d44e3f1414f438..f6de7559cb4fa1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -317,11 +317,28 @@ module.exports = { 'node-core/no-duplicate-requires': 'error', }, globals: { + ByteLengthQueuingStrategy: 'readable', + CompressionStream: 'readable', + CountQueuingStrategy: 'readable', Crypto: 'readable', CryptoKey: 'readable', + DecompressionStream: 'readable', fetch: 'readable', FormData: 'readable', + ReadableStream: 'readable', + ReadableStreamDefaultReader: 'readable', + ReadableStreamBYOBReader: 'readable', + ReadableStreamBYOBRequest: 'readable', + ReadableByteStreamController: 'readable', + ReadableStreamDefaultController: 'readable', Response: 'readable', + TextDecoderStream: 'readable', + TextEncoderStream: 'readable', + TransformStream: 'readable', + TransformStreamDefaultController: 'readable', SubtleCrypto: 'readable', + WritableStream: 'readable', + WritableStreamDefaultWriter: 'readable', + WritableStreamDefaultController: 'readable', }, }; diff --git a/doc/api/globals.md b/doc/api/globals.md index 5d9e81d9afcfff..8732c61fb9a3b0 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -229,6 +229,14 @@ added: v0.1.103 Used to handle binary data. See the [buffer section][]. +## Class: `ByteLengthQueuingStrategy` + + + +A browser-compatible implementation of [`ByteLengthQueuingStrategy`][]. + ## `__dirname` This variable may appear to be global but is not. See [`__dirname`][]. @@ -295,6 +303,14 @@ added: v0.0.1 [`clearTimeout`][] is described in the [timers][] section. +## Class: `CompressionStream` + + + +A browser-compatible implementation of [`CompressionStream`][]. + ## `console` + +A browser-compatible implementation of [`CountQueuingStrategy`][]. + ## `Crypto` + +A browser-compatible implementation of [`DecompressionStream`][]. + ## `Event` + +A browser-compatible implementation of [`ReadableByteStreamController`][]. + +## Class: `ReadableStream` + + + +A browser-compatible implementation of [`ReadableStream`][]. + +## Class: `ReadableStreamBYOBReader` + + + +A browser-compatible implementation of [`ReadableStreamBYOBReader`][]. + +## Class: `ReadableStreamBYOBRequest` + + + +A browser-compatible implementation of [`ReadableStreamBYOBRequest`][]. + +## Class: `ReadableStreamDefaultController` + + + +A browser-compatible implementation of [`ReadableStreamDefaultController`][]. + +## Class: `ReadableStreamDefaultReader` + + + +A browser-compatible implementation of [`ReadableStreamDefaultReader`][]. + ## `require()` This variable may appear to be global but is not. See [`require()`][]. @@ -615,6 +695,14 @@ added: v11.0.0 The WHATWG `TextDecoder` class. See the [`TextDecoder`][] section. +## Class: `TextDecoderStream` + + + +A browser-compatible implementation of [`TextDecoderStream`][]. + ## `TextEncoder` + +A browser-compatible implementation of [`TextEncoderStream`][]. + +## Class: `TransformStream` + + + +A browser-compatible implementation of [`TransformStream`][]. + +## Class: `TransformStreamDefaultController` + + + +A browser-compatible implementation of [`TransformStreamDefaultController`][]. + ## `URL` + +A browser-compatible implementation of [`WritableStream`][]. + +## Class: `WritableStreamDefaultController` + + + +A browser-compatible implementation of [`WritableStreamDefaultController`][]. + +## Class: `WritableStreamDefaultWriter` + + + +A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. + [Web Crypto API]: webcrypto.md [`--experimental-global-webcrypto`]: cli.md#--experimental-global-webcrypto [`--no-experimental-fetch`]: cli.md#--no-experimental-fetch [`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController +[`ByteLengthQueuingStrategy`]: webstreams.md#class-bytelengthqueuingstrategy +[`CompressionStream`]: webstreams.md#class-compressionstream +[`CountQueuingStrategy`]: webstreams.md#class-countqueuingstrategy [`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException +[`DecompressionStream`]: webstreams.md#class-decompressionstream [`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api [`MessageChannel`]: worker_threads.md#class-messagechannel [`MessageEvent`]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/MessageEvent [`MessagePort`]: worker_threads.md#class-messageport +[`ReadableByteStreamController`]: webstreams.md#class-readablebytestreamcontroller +[`ReadableStreamBYOBReader`]: webstreams.md#class-readablestreambyobreader +[`ReadableStreamBYOBRequest`]: webstreams.md#class-readablestreambyobrequest +[`ReadableStreamDefaultController`]: webstreams.md#class-readablestreamdefaultcontroller +[`ReadableStreamDefaultReader`]: webstreams.md#class-readablestreamdefaultreader +[`ReadableStream`]: webstreams.md#class-readablestream +[`TextDecoderStream`]: webstreams.md#class-textdecoderstream [`TextDecoder`]: util.md#class-utiltextdecoder +[`TextEncoderStream`]: webstreams.md#class-textencoderstream [`TextEncoder`]: util.md#class-utiltextencoder +[`TransformStreamDefaultController`]: webstreams.md#class-transformstreamdefaultcontroller +[`TransformStream`]: webstreams.md#class-transformstream [`URLSearchParams`]: url.md#class-urlsearchparams [`URL`]: url.md#class-url +[`WritableStreamDefaultController`]: webstreams.md#class-writablestreamdefaultcontroller +[`WritableStreamDefaultWriter`]: webstreams.md#class-writablestreamdefaultwriter +[`WritableStream`]: webstreams.md#class-writablestream [`__dirname`]: modules.md#__dirname [`__filename`]: modules.md#__filename [`buffer.atob()`]: buffer.md#bufferatobdata diff --git a/doc/api/webstreams.md b/doc/api/webstreams.md index cde2addc7d09fa..a445d0ca1f35a7 100644 --- a/doc/api/webstreams.md +++ b/doc/api/webstreams.md @@ -2,25 +2,17 @@ -> Stability: 1 - Experimental - -An implementation of the [WHATWG Streams Standard][]. + -```mjs -import { - ReadableStream, - WritableStream, - TransformStream, -} from 'node:stream/web'; -``` +> Stability: 2 - Stable -```cjs -const { - ReadableStream, - WritableStream, - TransformStream, -} = require('stream/web'); -``` +An implementation of the [WHATWG Streams Standard][]. ## Overview @@ -101,6 +93,10 @@ const stream = new ReadableStream({ #### `new ReadableStream([underlyingSource [, strategy]])` @@ -387,6 +383,10 @@ port2.postMessage(stream, [stream]); By default, calling `readableStream.getReader()` with no arguments @@ -454,6 +454,10 @@ Releases this reader's lock on the underlying {ReadableStream}. The `ReadableStreamBYOBReader` is an alternative consumer for @@ -701,6 +705,10 @@ Signals an error that causes the {ReadableStream} to error and close. When using `ReadableByteStreamController` in byte-oriented @@ -747,6 +755,10 @@ added: v16.5.0 The `WritableStream` is a destination to which stream data is sent. @@ -863,6 +875,10 @@ port2.postMessage(stream, [stream]); #### `new WritableStreamDefaultWriter(stream)` @@ -950,6 +966,10 @@ Appends a new chunk of data to the {WritableStream}'s queue. The `WritableStreamDefaultController` manage's the {WritableStream}'s @@ -980,6 +1000,10 @@ with currently pending writes canceled. A `TransformStream` consists of a {ReadableStream} and a {WritableStream} that @@ -1082,6 +1106,10 @@ port2.postMessage(stream, [stream]); The `TransformStreamDefaultController` manages the internal state @@ -1132,6 +1160,10 @@ to be abruptly closed with an error. #### `new ByteLengthQueuingStrategy(options)` @@ -1165,6 +1197,10 @@ added: v16.5.0 #### `new CountQueuingStrategy(options)` @@ -1198,6 +1234,10 @@ added: v16.5.0 #### `new TextEncoderStream()` @@ -1238,6 +1278,10 @@ added: v16.6.0 #### `new TextDecoderStream([encoding[, options]])` @@ -1309,6 +1353,10 @@ added: v16.6.0 #### `new CompressionStream(format)` @@ -1339,6 +1387,10 @@ added: v17.0.0 #### `new DecompressionStream(format)` diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index bb9edf87e1fb13..c1635e4cf080af 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -43,6 +43,14 @@ rules: message: Use `const { BroadcastChannel } = require('internal/worker/io');` instead of the global. - name: Buffer message: Use `const { Buffer } = require('buffer');` instead of the global. + - name: ByteLengthQueuingStrategy + message: Use `const { ByteLengthQueuingStrategy } = require('internal/webstreams/queuingstrategies')` instead of the global. + - name: CompressionStream + message: Use `const { CompressionStream } = require('internal/webstreams/compression')` instead of the global. + - name: CountQueuingStrategy + message: Use `const { CountQueuingStrategy } = require('internal/webstreams/queuingstrategies')` instead of the global. + - name: DecompressionStream + message: Use `const { DecompressionStream } = require('internal/webstreams/compression')` instead of the global. - name: DOMException message: Use lazy function `const { lazyDOMException } = require('internal/util');` instead of the global. - name: Event @@ -63,6 +71,18 @@ rules: message: Use `const { MessageEvent } = require('internal/worker/io');` instead of the global. - name: MessagePort message: Use `const { MessagePort } = require('internal/worker/io');` instead of the global. + - name: ReadableStream + message: Use `const { ReadableStream } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamDefaultReader + message: Use `const { ReadableStreamDefaultReader } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamBYOBReader + message: Use `const { ReadableStreamBYOBReader } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamBYOBRequest + message: Use `const { ReadableStreamBYOBRequest } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableByteStreamController + message: Use `const { ReadableByteStreamController } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamDefaultController + message: Use `const { ReadableStreamDefaultController } = require('internal/webstreams/readablestream')` instead of the global. - name: Request message: Use `const { Request } = require('internal/deps/undici/undici');` instead of the global. - name: Response @@ -73,8 +93,16 @@ rules: message: Use `const { SharedArrayBuffer } = globalThis;` instead of the global. - name: TextDecoder message: Use `const { TextDecoder } = require('internal/encoding');` instead of the global. + - name: TextDecoderStream + message: Use `const { TextDecoderStream } = require('internal/webstreams/encoding')` instead of the global. - name: TextEncoder message: Use `const { TextEncoder } = require('internal/encoding');` instead of the global. + - name: TextEncoderStream + message: Use `const { TextEncoderStream } = require('internal/webstreams/encoding')` instead of the global. + - name: TransformStream + message: Use `const { TransformStream } = require('internal/webstreams/transformstream')` instead of the global. + - name: TransformStreamDefaultController + message: Use `const { TransformStreamDefaultController } = require('internal/webstreams/transformstream')` instead of the global. - name: URL message: Use `const { URL } = require('internal/url');` instead of the global. - name: URLSearchParams @@ -83,6 +111,12 @@ rules: # disabled with --jitless CLI flag. - name: WebAssembly message: Use `const { WebAssembly } = globalThis;` instead of the global. + - name: WritableStream + message: Use `const { WritableStream } = require('internal/webstreams/writablestream')` instead of the global. + - name: WritableStreamDefaultWriter + message: Use `const { WritableStreamDefaultWriter } = require('internal/webstreams/writablestream')` instead of the global. + - name: WritableStreamDefaultController + message: Use `const { WritableStreamDefaultController } = require('internal/webstreams/writablestream')` instead of the global. - name: atob message: Use `const { atob } = require('buffer');` instead of the global. - name: btoa diff --git a/lib/internal/blob.js b/lib/internal/blob.js index 19ba47f1752c59..68b8d6f89e2042 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -88,6 +88,7 @@ function lazyURL(id) { } function lazyReadableStream(options) { + // eslint-disable-next-line no-global-assign ReadableStream ??= require('internal/webstreams/readablestream').ReadableStream; return new ReadableStream(options); diff --git a/lib/internal/bootstrap/browser.js b/lib/internal/bootstrap/browser.js index 68dac3b1ca4b8c..49d69b939687ca 100644 --- a/lib/internal/bootstrap/browser.js +++ b/lib/internal/bootstrap/browser.js @@ -116,3 +116,57 @@ function defineReplacableAttribute(target, name, value) { value, }); } + +// Web Streams API +const { + TransformStream, + TransformStreamDefaultController, +} = require('internal/webstreams/transformstream'); + +const { + WritableStream, + WritableStreamDefaultController, + WritableStreamDefaultWriter, +} = require('internal/webstreams/writablestream'); + +const { + ReadableStream, + ReadableStreamDefaultReader, + ReadableStreamBYOBReader, + ReadableStreamBYOBRequest, + ReadableByteStreamController, + ReadableStreamDefaultController, +} = require('internal/webstreams/readablestream'); + +const { + ByteLengthQueuingStrategy, + CountQueuingStrategy, +} = require('internal/webstreams/queuingstrategies'); + +const { + TextEncoderStream, + TextDecoderStream, +} = require('internal/webstreams/encoding'); + +const { + CompressionStream, + DecompressionStream, +} = require('internal/webstreams/compression'); + +exposeInterface(globalThis, 'ReadableStream', ReadableStream); +exposeInterface(globalThis, 'ReadableStreamDefaultReader', ReadableStreamDefaultReader); +exposeInterface(globalThis, 'ReadableStreamBYOBReader', ReadableStreamBYOBReader); +exposeInterface(globalThis, 'ReadableStreamBYOBRequest', ReadableStreamBYOBRequest); +exposeInterface(globalThis, 'ReadableByteStreamController', ReadableByteStreamController); +exposeInterface(globalThis, 'ReadableStreamDefaultController', ReadableStreamDefaultController); +exposeInterface(globalThis, 'TransformStream', TransformStream); +exposeInterface(globalThis, 'TransformStreamDefaultController', TransformStreamDefaultController); +exposeInterface(globalThis, 'WritableStream', WritableStream); +exposeInterface(globalThis, 'WritableStreamDefaultWriter', WritableStreamDefaultWriter); +exposeInterface(globalThis, 'WritableStreamDefaultController', WritableStreamDefaultController); +exposeInterface(globalThis, 'ByteLengthQueuingStrategy', ByteLengthQueuingStrategy); +exposeInterface(globalThis, 'CountQueuingStrategy', CountQueuingStrategy); +exposeInterface(globalThis, 'TextEncoderStream', TextEncoderStream); +exposeInterface(globalThis, 'TextDecoderStream', TextDecoderStream); +exposeInterface(globalThis, 'CompressionStream', CompressionStream); +exposeInterface(globalThis, 'DecompressionStream', DecompressionStream); diff --git a/test/common/index.js b/test/common/index.js index b69d726e8ef323..8aee35758374c1 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -309,6 +309,27 @@ if (hasCrypto && global.crypto) { knownGlobals.push(global.CryptoKey); knownGlobals.push(global.SubtleCrypto); } +if (global.ReadableStream) { + knownGlobals.push( + global.ReadableStream, + global.ReadableStreamDefaultReader, + global.ReadableStreamBYOBReader, + global.ReadableStreamBYOBRequest, + global.ReadableByteStreamController, + global.ReadableStreamDefaultController, + global.TransformStream, + global.TransformStreamDefaultController, + global.WritableStream, + global.WritableStreamDefaultWriter, + global.WritableStreamDefaultController, + global.ByteLengthQueuingStrategy, + global.CountQueuingStrategy, + global.TextEncoderStream, + global.TextDecoderStream, + global.CompressionStream, + global.DecompressionStream, + ); +} function allowGlobals(...allowlist) { knownGlobals = knownGlobals.concat(allowlist); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index d29e2de66bdf4d..68ba2450897bc1 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -140,8 +140,11 @@ const expectedModules = new Set([ 'NativeModule internal/validators', 'NativeModule internal/vm/module', 'NativeModule internal/webstreams/adapters', + 'NativeModule internal/webstreams/compression', + 'NativeModule internal/webstreams/encoding', 'NativeModule internal/webstreams/queuingstrategies', 'NativeModule internal/webstreams/readablestream', + 'NativeModule internal/webstreams/transformstream', 'NativeModule internal/webstreams/util', 'NativeModule internal/webstreams/writablestream', 'NativeModule internal/worker/io', diff --git a/test/parallel/test-global-webstreams.js b/test/parallel/test-global-webstreams.js new file mode 100644 index 00000000000000..ab20e376b718ab --- /dev/null +++ b/test/parallel/test-global-webstreams.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const webstreams = require('stream/web'); + +assert.strictEqual(ReadableStream, webstreams.ReadableStream); +assert.strictEqual(ReadableStreamDefaultReader, webstreams.ReadableStreamDefaultReader); +assert.strictEqual(ReadableStreamBYOBReader, webstreams.ReadableStreamBYOBReader); +assert.strictEqual(ReadableStreamBYOBRequest, webstreams.ReadableStreamBYOBRequest); +assert.strictEqual(ReadableByteStreamController, webstreams.ReadableByteStreamController); +assert.strictEqual(ReadableStreamDefaultController, webstreams.ReadableStreamDefaultController); +assert.strictEqual(TransformStream, webstreams.TransformStream); +assert.strictEqual(TransformStreamDefaultController, webstreams.TransformStreamDefaultController); +assert.strictEqual(WritableStream, webstreams.WritableStream); +assert.strictEqual(WritableStreamDefaultWriter, webstreams.WritableStreamDefaultWriter); +assert.strictEqual(WritableStreamDefaultController, webstreams.WritableStreamDefaultController); +assert.strictEqual(ByteLengthQueuingStrategy, webstreams.ByteLengthQueuingStrategy); +assert.strictEqual(CountQueuingStrategy, webstreams.CountQueuingStrategy); +assert.strictEqual(TextEncoderStream, webstreams.TextEncoderStream); +assert.strictEqual(TextDecoderStream, webstreams.TextDecoderStream); +assert.strictEqual(CompressionStream, webstreams.CompressionStream); +assert.strictEqual(DecompressionStream, webstreams.DecompressionStream); diff --git a/test/wpt/test-streams.js b/test/wpt/test-streams.js index 987676d8c49125..43c212247c41e7 100644 --- a/test/wpt/test-streams.js +++ b/test/wpt/test-streams.js @@ -5,112 +5,8 @@ const { WPTRunner } = require('../common/wpt'); const runner = new WPTRunner('streams'); -// Set Node.js flags required for the tests. -runner.setFlags(['--expose-internals']); - // Set a script that will be executed in the worker before running the tests. runner.setInitScript(` - let { - ReadableStream, - ReadableStreamDefaultReader, - ReadableStreamBYOBReader, - ReadableStreamBYOBRequest, - ReadableByteStreamController, - ReadableStreamDefaultController, - TransformStream, - TransformStreamDefaultController, - WritableStream, - WritableStreamDefaultWriter, - WritableStreamDefaultController, - ByteLengthQueuingStrategy, - CountQueuingStrategy, - } = require('stream/web'); - - const { internalBinding } = require('internal/test/binding'); - const { DOMException } = internalBinding('messaging'); - global.DOMException = DOMException; - - Object.defineProperties(global, { - ReadableStream: { - value: ReadableStream, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamDefaultReader: { - value: ReadableStreamDefaultReader, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamBYOBReader: { - value: ReadableStreamBYOBReader, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamBYOBRequest: { - value: ReadableStreamBYOBRequest, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableByteStreamController: { - value: ReadableByteStreamController, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamDefaultController: { - value: ReadableStreamDefaultController, - configurable: true, - writable: true, - enumerable: false, - }, - TransformStream: { - value: TransformStream, - configurable: true, - writable: true, - enumerable: false, - }, - TransformStreamDefaultController: { - value: TransformStreamDefaultController, - configurable: true, - writable: true, - enumerable: false, - }, - WritableStream: { - value: WritableStream, - configurable: true, - writable: true, - enumerable: false, - }, - WritableStreamDefaultWriter: { - value: WritableStreamDefaultWriter, - configurable: true, - writable: true, - enumerable: false, - }, - WritableStreamDefaultController: { - value: WritableStreamDefaultController, - configurable: true, - writable: true, - enumerable: false, - }, - ByteLengthQueuingStrategy: { - value: ByteLengthQueuingStrategy, - configurable: true, - writable: true, - enumerable: false, - }, - CountQueuingStrategy: { - value: CountQueuingStrategy, - configurable: true, - writable: true, - enumerable: false, - }, - }); - // Simulate global postMessage for enqueue-with-detached-buffer.window.js function postMessage(value, origin, transferList) { const mc = new MessageChannel();