From 91009bfde4df47f36ea21674b9e5f7d3b3b85ffd Mon Sep 17 00:00:00 2001 From: suuuuuuminnnnnn Date: Sat, 21 Feb 2026 13:07:13 +0900 Subject: [PATCH 1/6] stream: accept ArrayBuffer in CompressionStream and DecompressionStream --- lib/internal/webstreams/adapters.js | 7 ++ ...st-webstreams-compression-buffer-source.js | 71 +++++++++++++++++++ test/wpt/status/compression.json | 3 - 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-webstreams-compression-buffer-source.js diff --git a/lib/internal/webstreams/adapters.js b/lib/internal/webstreams/adapters.js index 8d5c1d9fc6d9d0..40037385143ab1 100644 --- a/lib/internal/webstreams/adapters.js +++ b/lib/internal/webstreams/adapters.js @@ -53,6 +53,10 @@ const { Buffer, } = require('buffer'); +const { + isArrayBuffer, +} = require('internal/util/types'); + const { AbortError, ErrnoException, @@ -213,6 +217,9 @@ function newWritableStreamFromStreamWritable(streamWritable) { start(c) { controller = c; }, write(chunk) { + if (isArrayBuffer(chunk)) { + chunk = new Uint8Array(chunk); + } if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) { backpressurePromise = PromiseWithResolvers(); return SafePromisePrototypeFinally( diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js new file mode 100644 index 00000000000000..e41a0ac2a279e2 --- /dev/null +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -0,0 +1,71 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { DecompressionStream, CompressionStream } = require('stream/web'); + +// Minimal gzip-compressed bytes for "hello" +const compressedGzip = new Uint8Array([ + 31, 139, 8, 0, 0, 0, 0, 0, 0, 3, + 203, 72, 205, 201, 201, 7, 0, 134, 166, 16, 54, 5, 0, 0, 0, +]); + +async function testDecompressionAcceptsArrayBuffer() { + const ds = new DecompressionStream('gzip'); + const reader = ds.readable.getReader(); + const writer = ds.writable.getWriter(); + + const writePromise = writer.write(compressedGzip.buffer); + writer.close(); + + const chunks = []; + let done = false; + while (!done) { + const { value, done: d } = await reader.read(); + if (value) chunks.push(value); + done = d; + } + await writePromise; + const out = Buffer.concat(chunks.map((c) => Buffer.from(c))); + assert.strictEqual(out.toString(), 'hello'); +} + +async function testCompressionRoundTripWithArrayBuffer() { + const cs = new CompressionStream('gzip'); + const ds = new DecompressionStream('gzip'); + + const csWriter = cs.writable.getWriter(); + const csReader = cs.readable.getReader(); + const dsWriter = ds.writable.getWriter(); + const dsReader = ds.readable.getReader(); + + const input = new TextEncoder().encode('hello').buffer; + + await csWriter.write(input); + csWriter.close(); + + const compressed = []; + let done = false; + while (!done) { + const { value, done: d } = await csReader.read(); + if (value) compressed.push(value); + done = d; + } + + for (const chunk of compressed) await dsWriter.write(chunk); + dsWriter.close(); + + const out = []; + done = false; + while (!done) { + const { value, done: d } = await dsReader.read(); + if (value) out.push(value); + done = d; + } + const result = Buffer.concat(out.map((c) => Buffer.from(c))); + assert.strictEqual(result.toString(), 'hello'); +} + +Promise.all([ + testDecompressionAcceptsArrayBuffer(), + testCompressionRoundTripWithArrayBuffer(), +]).then(common.mustCall()); diff --git a/test/wpt/status/compression.json b/test/wpt/status/compression.json index be073427810f0d..619add6fbc25a9 100644 --- a/test/wpt/status/compression.json +++ b/test/wpt/status/compression.json @@ -5,9 +5,6 @@ "decompression-bad-chunks.tentative.any.js": { "skip": "Execution \"hangs\", ArrayBuffer and TypedArray is not accepted and throws, instead of rejects during writer.write" }, - "decompression-buffersource.tentative.any.js": { - "skip": "ArrayBuffer and TypedArray is not accepted and throws, instead of rejects during writer.write" - }, "compression-with-detach.tentative.window.js": { "requires": ["crypto"] }, From b0bbe51e56dde97c21d3c149f0c3cb77f8226572 Mon Sep 17 00:00:00 2001 From: suuuuuuminnnnnn Date: Sat, 21 Feb 2026 18:20:17 +0900 Subject: [PATCH 2/6] stream: guard ArrayBuffer normalization and simplify test --- lib/internal/webstreams/adapters.js | 2 +- .../test-webstreams-compression-buffer-source.js | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/internal/webstreams/adapters.js b/lib/internal/webstreams/adapters.js index 40037385143ab1..ee8ad0da1f75e7 100644 --- a/lib/internal/webstreams/adapters.js +++ b/lib/internal/webstreams/adapters.js @@ -217,7 +217,7 @@ function newWritableStreamFromStreamWritable(streamWritable) { start(c) { controller = c; }, write(chunk) { - if (isArrayBuffer(chunk)) { + if (!streamWritable.writableObjectMode && isArrayBuffer(chunk)) { chunk = new Uint8Array(chunk); } if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) { diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js index e41a0ac2a279e2..37877c889bbaf9 100644 --- a/test/parallel/test-webstreams-compression-buffer-source.js +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -43,16 +43,7 @@ async function testCompressionRoundTripWithArrayBuffer() { await csWriter.write(input); csWriter.close(); - const compressed = []; - let done = false; - while (!done) { - const { value, done: d } = await csReader.read(); - if (value) compressed.push(value); - done = d; - } - - for (const chunk of compressed) await dsWriter.write(chunk); - dsWriter.close(); + await cs.readable.pipeTo(ds.writable); const out = []; done = false; From 25e32d0e4c2bba7a03df3506ae6802a0a3af19a8 Mon Sep 17 00:00:00 2001 From: suuuuuuminnnnnn Date: Sat, 21 Feb 2026 19:11:37 +0900 Subject: [PATCH 3/6] test: migrate webstreams compression buffer-source test to node:test --- ...st-webstreams-compression-buffer-source.js | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js index 37877c889bbaf9..e80238bb8905af 100644 --- a/test/parallel/test-webstreams-compression-buffer-source.js +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -1,6 +1,7 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); +const test = require('node:test'); const { DecompressionStream, CompressionStream } = require('stream/web'); // Minimal gzip-compressed bytes for "hello" @@ -9,7 +10,7 @@ const compressedGzip = new Uint8Array([ 203, 72, 205, 201, 201, 7, 0, 134, 166, 16, 54, 5, 0, 0, 0, ]); -async function testDecompressionAcceptsArrayBuffer() { +test('DecompressionStream accepts ArrayBuffer chunks', async () => { const ds = new DecompressionStream('gzip'); const reader = ds.readable.getReader(); const writer = ds.writable.getWriter(); @@ -27,15 +28,13 @@ async function testDecompressionAcceptsArrayBuffer() { await writePromise; const out = Buffer.concat(chunks.map((c) => Buffer.from(c))); assert.strictEqual(out.toString(), 'hello'); -} +}); -async function testCompressionRoundTripWithArrayBuffer() { +test('CompressionStream round-trip with ArrayBuffer input', async () => { const cs = new CompressionStream('gzip'); const ds = new DecompressionStream('gzip'); const csWriter = cs.writable.getWriter(); - const csReader = cs.readable.getReader(); - const dsWriter = ds.writable.getWriter(); const dsReader = ds.readable.getReader(); const input = new TextEncoder().encode('hello').buffer; @@ -46,7 +45,7 @@ async function testCompressionRoundTripWithArrayBuffer() { await cs.readable.pipeTo(ds.writable); const out = []; - done = false; + let done = false; while (!done) { const { value, done: d } = await dsReader.read(); if (value) out.push(value); @@ -54,9 +53,4 @@ async function testCompressionRoundTripWithArrayBuffer() { } const result = Buffer.concat(out.map((c) => Buffer.from(c))); assert.strictEqual(result.toString(), 'hello'); -} - -Promise.all([ - testDecompressionAcceptsArrayBuffer(), - testCompressionRoundTripWithArrayBuffer(), -]).then(common.mustCall()); +}); From c664603ce7074539783d63e5e2abc01bfeece838 Mon Sep 17 00:00:00 2001 From: suuuuuuminnnnnn Date: Sat, 21 Feb 2026 20:16:47 +0900 Subject: [PATCH 4/6] test: simplify webstreams compression regression test --- .../parallel/test-webstreams-compression-buffer-source.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js index e80238bb8905af..217705ddc9559d 100644 --- a/test/parallel/test-webstreams-compression-buffer-source.js +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -18,13 +18,7 @@ test('DecompressionStream accepts ArrayBuffer chunks', async () => { const writePromise = writer.write(compressedGzip.buffer); writer.close(); - const chunks = []; - let done = false; - while (!done) { - const { value, done: d } = await reader.read(); - if (value) chunks.push(value); - done = d; - } + const chunks = await Array.fromAsync(ds.readable); await writePromise; const out = Buffer.concat(chunks.map((c) => Buffer.from(c))); assert.strictEqual(out.toString(), 'hello'); From aa1901cdbd063c93f6110655e8777e083562b2b7 Mon Sep 17 00:00:00 2001 From: suuuuuuminnnnnn Date: Sat, 21 Feb 2026 20:27:11 +0900 Subject: [PATCH 5/6] test: remove unused reader variable in compression buffer-source test --- test/parallel/test-webstreams-compression-buffer-source.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js index 217705ddc9559d..fd8a01c33fd5b3 100644 --- a/test/parallel/test-webstreams-compression-buffer-source.js +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -12,7 +12,6 @@ const compressedGzip = new Uint8Array([ test('DecompressionStream accepts ArrayBuffer chunks', async () => { const ds = new DecompressionStream('gzip'); - const reader = ds.readable.getReader(); const writer = ds.writable.getWriter(); const writePromise = writer.write(compressedGzip.buffer); From d791f39e400e16a186e7f1bc3d1b715c66062ff3 Mon Sep 17 00:00:00 2001 From: suuuuuuminnnnnn Date: Sat, 21 Feb 2026 22:14:53 +0900 Subject: [PATCH 6/6] test: simplify round-trip test using Array.fromAsync --- .../test-webstreams-compression-buffer-source.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js index fd8a01c33fd5b3..3304a8e64f3175 100644 --- a/test/parallel/test-webstreams-compression-buffer-source.js +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -28,7 +28,6 @@ test('CompressionStream round-trip with ArrayBuffer input', async () => { const ds = new DecompressionStream('gzip'); const csWriter = cs.writable.getWriter(); - const dsReader = ds.readable.getReader(); const input = new TextEncoder().encode('hello').buffer; @@ -37,13 +36,7 @@ test('CompressionStream round-trip with ArrayBuffer input', async () => { await cs.readable.pipeTo(ds.writable); - const out = []; - let done = false; - while (!done) { - const { value, done: d } = await dsReader.read(); - if (value) out.push(value); - done = d; - } + const out = await Array.fromAsync(ds.readable); const result = Buffer.concat(out.map((c) => Buffer.from(c))); assert.strictEqual(result.toString(), 'hello'); });