Repository: nodejs/node
Pull request:  stream: add brotli support to CompressionStream and DecompressionStream 

It has shipped in Safari [18.4](https://webkit.org/blog/16574/webkit-features-in-safari-18-4/#web-api), I don't see a reason to hold off until the spec is written.

Refs: https://github.com/whatwg/compression/issues/34

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: doc/api/globals.md
patch: @@ -330,6 +330,9 @@ with the [`--no-experimental-websocket`][] CLI flag.
 <!-- YAML
 added: v18.0.0
 changes:
+ - version: REPLACEME
+   pr-url: https://github.com/nodejs/node/pull/59464
+   description: format now accepts `brotli` value.
  - version:
     - v23.11.0
     - v22.15.0
@@ -445,6 +448,9 @@ A browser-compatible implementation of {CustomEvent}.
 <!-- YAML
 added: v18.0.0
 changes:
+ - version: REPLACEME
+   pr-url: https://github.com/nodejs/node/pull/59464
+   description: format now accepts `brotli` value.
  - version:
     - v23.11.0
     - v22.15.0

filename: doc/api/webstreams.md
patch: @@ -1480,14 +1480,17 @@ changes:
 <!-- YAML
 added: v17.0.0
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/59464
+    description: format now accepts `brotli` value.
   - version:
     - v21.2.0
     - v20.12.0
     pr-url: https://github.com/nodejs/node/pull/50097
     description: format now accepts `deflate-raw` value.
 -->
 
-* `format` {string} One of `'deflate'`, `'deflate-raw'`, or `'gzip'`.
+* `format` {string} One of `'deflate'`, `'deflate-raw'`, `'gzip'`, or `'brotli'`.
 
 #### `compressionStream.readable`
 
@@ -1520,14 +1523,17 @@ changes:
 <!-- YAML
 added: v17.0.0
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/59464
+    description: format now accepts `brotli` value.
   - version:
     - v21.2.0
     - v20.12.0
     pr-url: https://github.com/nodejs/node/pull/50097
     description: format now accepts `deflate-raw` value.
 -->
 
-* `format` {string} One of `'deflate'`, `'deflate-raw'`, or `'gzip'`.
+* `format` {string} One of `'deflate'`, `'deflate-raw'`, `'gzip'`, or `'brotli'`.
 
 #### `decompressionStream.readable`
 

filename: lib/internal/webstreams/compression.js
patch: @@ -28,6 +28,7 @@ const formatConverter = createEnumConverter('CompressionFormat', [
   'deflate',
   'deflate-raw',
   'gzip',
+  'brotli',
 ]);
 
 /**
@@ -40,7 +41,7 @@ class CompressionStream {
   #transform;
 
   /**
-   * @param {'deflate'|'deflate-raw'|'gzip'} format
+   * @param {'deflate'|'deflate-raw'|'gzip'|'brotli'} format
    */
   constructor(format) {
     format = formatConverter(format, {
@@ -57,6 +58,9 @@ class CompressionStream {
       case 'gzip':
         this.#handle = lazyZlib().createGzip();
         break;
+      case 'brotli':
+        this.#handle = lazyZlib().createBrotliCompress();
+        break;
     }
     this.#transform = newReadableWritablePairFromDuplex(this.#handle);
   }
@@ -90,7 +94,7 @@ class DecompressionStream {
   #transform;
 
   /**
-   * @param {'deflate'|'deflate-raw'|'gzip'} format
+   * @param {'deflate'|'deflate-raw'|'gzip'|'brotli'} format
    */
   constructor(format) {
     format = formatConverter(format, {
@@ -111,6 +115,9 @@ class DecompressionStream {
           rejectGarbageAfterEnd: true,
         });
         break;
+      case 'brotli':
+        this.#handle = lazyZlib().createBrotliDecompress();
+        break;
     }
     this.#transform = newReadableWritablePairFromDuplex(this.#handle);
 

filename: test/parallel/test-whatwg-webstreams-compression.js
patch: @@ -41,7 +41,7 @@ async function test(format) {
   ]);
 }
 
-Promise.all(['gzip', 'deflate', 'deflate-raw'].map((i) => test(i))).then(common.mustCall());
+Promise.all(['gzip', 'deflate', 'deflate-raw', 'brotli'].map((i) => test(i))).then(common.mustCall());
 
 [1, 'hello', false, {}].forEach((i) => {
   assert.throws(() => new CompressionStream(i), {