Add Node 24 Web Stream pipelines and streaming encoding (#660)

This commit is contained in:
Chris Tate
2026-10-04 05:36:27 -05:00
committed by GitHub
parent 99613bce5a
commit 3aad3e8ebe
31 changed files with 1013 additions and 275 deletions
+1 -1
View File
@@ -207,7 +207,7 @@ Uncaught exceptions print `Uncaught <value>` to stderr instead of Node.js's stac
In a TypeScript `--dynamic` build, `const host: any = globalThis` or `(globalThis as any).TextDecoder` accesses the engine's global object. Its Node globals use those shims even without an embedded npm package. For example, the engine's `TextDecoder` supports UTF-8 labels only. Optional capabilities such as `fetch` are installed only when the build links their bridge.
Embedded Web Streams support default readable streams, writable streams and their writers/controllers, count and byte-length queuing strategies, transform streams, and UTF-8 decoder streams. Writable sinks serialize queued writes, wait for asynchronous start/close/abort callbacks, and expose queue backpressure through `ready` and `desiredSize`. Transform streams still run without readable-side backpressure. Byte streams, BYOB readers, `tee()`, `pipeTo()`, stream transfer, and piping with AbortSignal remain unsupported. These embedded APIs do not broaden the separate static Web Streams surface.
Embedded Web Streams support default readable and writable streams, their writer and transform-controller APIs, count and byte-length queuing strategies, transform streams, and UTF-8 encoder/decoder streams. Writable sinks serialize queued writes and wait for asynchronous start/close/abort callbacks. Transforms honor readable-side backpressure and asynchronous start/transform/flush/cancel callbacks. Default-stream `pipeTo()` and `pipeThrough()` honor `preventClose`, `preventAbort`, `preventCancel`, and AbortSignal, propagate cleanup errors, and release their locks when piping settles. Byte streams, BYOB readers, `tee()`, and stream transfer remain unsupported. These embedded APIs do not broaden the separate static Web Streams surface.
Static tasks drain before engine jobs run at event-loop quiescence. A static `await` racing a package promise can therefore resolve in a different order from Node.js.
@@ -2,6 +2,6 @@
"schemaVersion": 3,
"nodeVersion": "24.15.0",
"nodeCommit": "848430679556aed0bd073f2bc263331ad84fa119",
"artifactVersion": "60361b544f92e9e4378a",
"artifactVersion": "ab49b98a2d4f6853980e",
"rowCount": 3662
}
+49 -49
View File
@@ -491,9 +491,9 @@
},
"dynamic": {
"supported": 0,
"partial": 38,
"partial": 40,
"refused": 0,
"not-implemented": 54,
"not-implemented": 52,
"by-design": 0,
"unreviewed": 0,
"not-applicable": 0
@@ -1331,9 +1331,9 @@
},
"dynamic": {
"supported": 0,
"partial": 27,
"partial": 40,
"refused": 0,
"not-implemented": 61,
"not-implemented": 48,
"by-design": 0,
"unreviewed": 0,
"not-applicable": 0
@@ -44202,9 +44202,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "Verified absent from the embedded engine's web/global compatibility layer.",
"verification": "verified-absence"
"status": "partial",
"detail": "Provided by the embedded engine's web/global compatibility layer.",
"verification": "test-backed"
}
},
{
@@ -44258,9 +44258,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "Verified absent from the embedded engine's web/global compatibility layer.",
"verification": "verified-absence"
"status": "partial",
"detail": "Provided by the embedded engine's web/global compatibility layer.",
"verification": "test-backed"
}
},
{
@@ -94335,9 +94335,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -94363,9 +94363,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -95651,9 +95651,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -95679,9 +95679,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -95707,9 +95707,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Available through the embedded Node compatibility shim; exact member and overload coverage may be narrower.",
"verification": "test-backed"
}
},
{
@@ -95735,9 +95735,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -95763,9 +95763,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -95791,9 +95791,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -95819,9 +95819,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -96015,9 +96015,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Available through the embedded Node compatibility shim; exact member and overload coverage may be narrower.",
"verification": "test-backed"
}
},
{
@@ -96043,9 +96043,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -96071,9 +96071,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -96099,9 +96099,9 @@
"verification": "registry-gap"
},
"dynamic": {
"status": "not-implemented",
"detail": "This API is not implemented by the dynamic-island shim.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented embedded module-loader subset.",
"verification": "test-backed"
}
},
{
+8 -3
View File
@@ -19,6 +19,11 @@
"wasi": "by-design"
},
"features": [
{"chapter": "webstreams", "symbols": ["readableStream.pipeTo", "readableStream.pipeThrough"], "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts", "tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts", "tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts"]},
{"chapter": "webstreams", "parent": "Class: `TransformStream`", "symbols": ["readable", "writable"], "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts", "tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts"]},
{"chapter": "webstreams", "symbols": ["transformStreamDefaultController.enqueue", "transformStreamDefaultController.error", "transformStreamDefaultController.terminate"], "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"]},
{"chapter": "webstreams", "parent": "Class: `TransformStreamDefaultController`", "symbols": ["desiredSize"], "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"]},
{"chapter": "webstreams", "parent": "Class: `TextEncoderStream`", "symbols": ["encoding", "readable", "writable"], "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"]},
{"chapter": "webstreams", "symbols": ["writableStream.abort", "writableStream.close", "writableStream.getWriter", "writableStreamDefaultWriter.abort", "writableStreamDefaultWriter.close", "writableStreamDefaultWriter.releaseLock", "writableStreamDefaultWriter.write", "writableStreamDefaultController.error"], "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-queues/main.ts", "tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts", "tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"]},
{"chapter": "webstreams", "parent": "Class: `WritableStream`", "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-queues/main.ts", "tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts", "tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"], "symbols": ["locked"]},
{"chapter": "webstreams", "parent": "Class: `WritableStreamDefaultWriter`", "status": "partial", "evidence": ["tests/fixtures/npm/cases/node24-web-stream-queues/main.ts", "tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts", "tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"], "symbols": ["closed", "desiredSize", "ready"]},
@@ -109,15 +114,15 @@
"stream": { "exports": ["Stream", "Readable", "Writable", "Duplex", "Transform", "PassThrough", "pipeline", "finished", "addAbortSignal", "promises", "isErrored", "isDestroyed", "isReadable", "isWritable"], "members": ["stream.Readable.from", "readable.read", "readable.push", "readable.unshift", "readable.pause", "readable.resume", "readable.isPaused", "readable.setEncoding", "readable.pipe", "readable.unpipe", "readable.destroy", "writable.write", "writable.end", "writable.cork", "writable.uncork", "writable.setDefaultEncoding", "writable.destroy", "transform.destroy"], "refusedMembers": ["readable.wrap"], "evidence": ["tests/fixtures/npm/cases/stream-shims/main.ts"] },
"stream/promises": { "exports": ["pipeline", "finished"], "evidence": ["tests/fixtures/npm/cases/stream-shims/main.ts"] },
"stream/consumers": { "exports": ["text", "buffer", "arrayBuffer", "json", "blob"], "evidence": ["tests/fixtures/npm/cases/stream-shims/main.ts"] },
"stream/web": { "exports": ["ReadableStream", "TransformStream", "TextDecoderStream", "WritableStream", "WritableStreamDefaultWriter", "WritableStreamDefaultController", "CountQueuingStrategy", "ByteLengthQueuingStrategy"], "evidence": ["tests/fixtures/npm/cases/web-streams/main.ts", "tests/fixtures/npm/cases/node24-web-stream-queues/main.ts", "tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts", "tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"] },
"stream/web": {"exports": ["ReadableStream", "TransformStream", "TextDecoderStream", "WritableStream", "WritableStreamDefaultWriter", "WritableStreamDefaultController", "CountQueuingStrategy", "ByteLengthQueuingStrategy", "TransformStreamDefaultController", "TextEncoderStream"], "evidence": ["tests/fixtures/npm/cases/web-streams/main.ts", "tests/fixtures/npm/cases/node24-web-stream-queues/main.ts", "tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts", "tests/fixtures/npm/cases/node24-web-stream-errors/main.ts", "tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts", "tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts", "tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts", "tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts", "tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"]},
"crypto": { "exports": ["createHash", "createHmac", "hash", "Hash", "Hmac", "randomBytes", "randomFillSync", "randomFill", "randomInt", "randomUUID", "getRandomValues", "timingSafeEqual", "pbkdf2", "pbkdf2Sync", "getHashes", "getCiphers", "getCurves", "webcrypto", "subtle", "constants", "KeyObject", "createCipheriv", "createDecipheriv", "createSign", "createVerify", "createDiffieHellman", "createECDH", "createPublicKey", "createPrivateKey", "createSecretKey", "diffieHellman", "generateKeyPair", "generateKeyPairSync", "generateKey", "generateKeySync", "sign", "verify", "publicEncrypt", "publicDecrypt", "privateEncrypt", "privateDecrypt", "scrypt", "scryptSync", "hkdf", "hkdfSync", "X509Certificate", "Certificate", "checkPrime", "checkPrimeSync", "generatePrime", "generatePrimeSync", "secureHeapUsed", "setEngine", "setFips", "getFips"], "refused": ["KeyObject", "createCipheriv", "createDecipheriv", "createSign", "createVerify", "createDiffieHellman", "createECDH", "createPublicKey", "createPrivateKey", "createSecretKey", "diffieHellman", "generateKeyPair", "generateKeyPairSync", "generateKey", "generateKeySync", "sign", "verify", "publicEncrypt", "publicDecrypt", "privateEncrypt", "privateDecrypt", "X509Certificate", "Certificate", "checkPrime", "checkPrimeSync", "generatePrime", "generatePrimeSync", "secureHeapUsed", "setEngine"], "members": ["hash.copy", "hash.digest", "hash.update", "hmac.digest", "hmac.update"], "evidence": ["tests/fixtures/npm/cases/crypto-shims/main.ts", "tests/fixtures/npm/cases/node24-crypto-kdf/main.ts"] },
"util": { "exports": ["getSystemErrorName", "getSystemErrorMessage", "getSystemErrorMap", "parseEnv", "format", "formatWithOptions", "inspect", "inherits", "promisify", "callbackify", "deprecate", "debuglog", "debug", "types", "isDeepStrictEqual", "stripVTControlCharacters", "styleText", "parseArgs", "toUSVString", "_extend", "TextEncoder", "TextDecoder", "MIMEType", "MIMEParams", "isArray"], "evidence": ["tests/fixtures/npm/cases/util-shims/main.ts"] },
"util/types": { "exports": ["isAnyArrayBuffer", "isArrayBufferView", "isArgumentsObject", "isArrayBuffer", "isAsyncFunction", "isBigInt64Array", "isBigUint64Array", "isBooleanObject", "isBoxedPrimitive", "isBigIntObject", "isCryptoKey", "isDataView", "isDate", "isExternal", "isFloat16Array", "isFloat32Array", "isFloat64Array", "isGeneratorFunction", "isGeneratorObject", "isInt8Array", "isInt16Array", "isInt32Array", "isKeyObject", "isMap", "isMapIterator", "isModuleNamespaceObject", "isNativeError", "isNumberObject", "isPromise", "isProxy", "isRegExp", "isSet", "isSetIterator", "isSharedArrayBuffer", "isStringObject", "isSymbolObject", "isTypedArray", "isUint8Array", "isUint8ClampedArray", "isUint16Array", "isUint32Array", "isWeakMap", "isWeakSet"], "evidence": ["tests/fixtures/npm/cases/util-shims/main.ts", "tests/fixtures/npm/cases/node24-util-types/main.ts"] }
},
"globals": {
"supported": ["atob", "btoa", "clearImmediate", "clearInterval", "clearTimeout", "queueMicrotask", "setImmediate", "setInterval", "setTimeout", "structuredClone", "AbortController", "AbortSignal", "Blob", "CustomEvent", "DOMException", "Event", "EventTarget", "File", "Headers", "MessageChannel", "MessageEvent", "MessagePort", "Request", "Response", "TextDecoder", "TextDecoderStream", "TextEncoder", "TransformStream", "URLSearchParams", "ReadableStream", "WritableStream", "WritableStreamDefaultWriter", "WritableStreamDefaultController", "CountQueuingStrategy", "ByteLengthQueuingStrategy"],
"supported": ["atob", "btoa", "clearImmediate", "clearInterval", "clearTimeout", "queueMicrotask", "setImmediate", "setInterval", "setTimeout", "structuredClone", "AbortController", "AbortSignal", "Blob", "CustomEvent", "DOMException", "Event", "EventTarget", "File", "Headers", "MessageChannel", "MessageEvent", "MessagePort", "Request", "Response", "TextDecoder", "TextDecoderStream", "TextEncoder", "TextEncoderStream", "TransformStream", "TransformStreamDefaultController", "URLSearchParams", "ReadableStream", "WritableStream", "WritableStreamDefaultWriter", "WritableStreamDefaultController", "CountQueuingStrategy", "ByteLengthQueuingStrategy"],
"npmSupported": ["Buffer", "URL"],
"absent": ["BroadcastChannel", "CloseEvent", "CompressionStream", "CryptoKey", "DecompressionStream", "EventSource", "FormData", "Navigator", "ReadableByteStreamController", "ReadableStreamBYOBReader", "ReadableStreamBYOBRequest", "Storage", "SubtleCrypto", "TextEncoderStream", "TransformStreamDefaultController", "URLPattern", "WebAssembly", "WebSocket"],
"absent": ["BroadcastChannel", "CloseEvent", "CompressionStream", "CryptoKey", "DecompressionStream", "EventSource", "FormData", "Navigator", "ReadableByteStreamController", "ReadableStreamBYOBReader", "ReadableStreamBYOBRequest", "Storage", "SubtleCrypto", "URLPattern", "WebAssembly", "WebSocket"],
"evidence": ["tests/harness/web-globals.test.ts"]
}
}
@@ -14,10 +14,10 @@
"implement": 80
},
"dynamic": {
"audit-partial": 885,
"verify-gap": 2145,
"audit-partial": 900,
"verify-gap": 2132,
"replace-refusal": 112,
"implement": 208
"implement": 206
}
},
"priorities": {
@@ -45608,11 +45608,11 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "implement",
"verification": "verified-absence",
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-global-absent:TextEncoderStream",
"source": "island-global:TextEncoderStream",
"tests": [
"tests/harness/web-globals.test.ts"
]
@@ -45678,11 +45678,11 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "implement",
"verification": "verified-absence",
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-global-absent:TransformStreamDefaultController",
"source": "island-global:TransformStreamDefaultController",
"tests": [
"tests/harness/web-globals.test.ts"
]
@@ -104244,7 +104244,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -104372,12 +104377,16 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.readableStream.pipeThrough",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts"
]
}
}
},
@@ -104405,12 +104414,16 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.readableStream.pipeTo",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts"
]
}
}
},
@@ -105376,7 +105389,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -105562,7 +105580,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -105859,7 +105882,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -105971,7 +105999,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -106000,12 +106033,15 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.readable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts"
]
}
}
},
@@ -106033,12 +106069,15 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.writable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts"
]
}
}
},
@@ -106066,12 +106105,22 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-export:node:stream/web.TransformStreamDefaultController",
"tests": [
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
},
@@ -106099,12 +106148,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.transformStreamDefaultController.enqueue",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
}
}
},
@@ -106132,12 +106183,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.transformStreamDefaultController.error",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
}
}
},
@@ -106165,12 +106218,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.transformStreamDefaultController.terminate",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
}
}
},
@@ -106198,12 +106253,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.desiredSize",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
}
}
},
@@ -106240,7 +106297,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -106352,7 +106414,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -106455,12 +106522,22 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-export:node:stream/web.TextEncoderStream",
"tests": [
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
},
@@ -106488,12 +106565,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.encoding",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
},
@@ -106521,12 +106600,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.readable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
},
@@ -106554,12 +106635,14 @@
"tests": []
},
"dynamic": {
"status": "not-implemented",
"action": "verify-gap",
"verification": "registry-gap",
"confidence": "low",
"source": "island-unmatched:webstreams",
"tests": []
"status": "partial",
"action": "audit-partial",
"verification": "test-backed",
"confidence": "high",
"source": "island-feature:webstreams.writable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
},
@@ -106596,7 +106679,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
}
}
@@ -540,9 +540,9 @@
},
"dynamic": {
"supported": 0,
"partial": 38,
"partial": 40,
"refused": 0,
"not-implemented": 54,
"not-implemented": 52,
"by-design": 0,
"unreviewed": 0,
"not-applicable": 0
@@ -1452,9 +1452,9 @@
},
"dynamic": {
"supported": 0,
"partial": 27,
"partial": 40,
"refused": 0,
"not-implemented": 61,
"not-implemented": 48,
"by-design": 0,
"unreviewed": 0,
"not-applicable": 0
@@ -61620,8 +61620,8 @@
"evidence": "compiler-unmatched:globals"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-global-absent:TextEncoderStream",
"status": "partial",
"evidence": "island-global:TextEncoderStream",
"tests": [
"tests/harness/web-globals.test.ts"
]
@@ -61680,8 +61680,8 @@
"evidence": "compiler-unmatched:globals"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-global-absent:TransformStreamDefaultController",
"status": "partial",
"evidence": "island-global:TransformStreamDefaultController",
"tests": [
"tests/harness/web-globals.test.ts"
]
@@ -122757,9 +122757,14 @@
"status": "partial",
"evidence": "derived:descendants",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/web-streams/main.ts"
]
},
@@ -123063,7 +123068,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -123198,8 +123208,13 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.readableStream.pipeThrough",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts"
]
},
"anchorSource": "exact"
},
@@ -123225,8 +123240,13 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.readableStream.pipeTo",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts"
]
},
"anchorSource": "exact"
},
@@ -124021,7 +124041,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -124209,7 +124234,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -124466,7 +124496,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -124563,7 +124598,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -124617,8 +124657,12 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.readable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts"
]
},
"anchorSource": "ancestor"
},
@@ -124644,8 +124688,12 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.writable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts"
]
},
"anchorSource": "ancestor"
},
@@ -124671,8 +124719,19 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-export:node:stream/web.TransformStreamDefaultController",
"tests": [
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
},
@@ -124698,8 +124757,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.transformStreamDefaultController.enqueue",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
},
"anchorSource": "exact"
},
@@ -124725,8 +124787,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.transformStreamDefaultController.error",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
},
"anchorSource": "exact"
},
@@ -124752,8 +124817,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.transformStreamDefaultController.terminate",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
},
"anchorSource": "exact"
},
@@ -124779,8 +124847,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.desiredSize",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
]
},
"anchorSource": "ancestor"
},
@@ -124812,7 +124883,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -124909,7 +124985,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -125000,8 +125081,19 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-export:node:stream/web.TextEncoderStream",
"tests": [
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
},
@@ -125027,8 +125119,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.encoding",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "ancestor"
},
@@ -125054,8 +125149,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.readable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "ancestor"
},
@@ -125081,8 +125179,11 @@
"evidence": "compiler-unmatched:webstreams"
},
"dynamic": {
"status": "not-implemented",
"evidence": "island-unmatched:webstreams"
"status": "partial",
"evidence": "island-feature:webstreams.writable",
"tests": [
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "ancestor"
},
@@ -125114,7 +125215,12 @@
"tests/fixtures/npm/cases/web-streams/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-queues/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-lifecycle/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
"tests/fixtures/npm/cases/node24-web-stream-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts",
"tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
]
},
"anchorSource": "exact"
@@ -13811,6 +13811,36 @@
"<repo>/tests/fixtures/npm/cases/node24-web-stream-errors/main.ts"
],
"diags": []
},
"<repo>/tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts": {
"order": [
"<repo>/tests/fixtures/npm/cases/node24-web-stream-pipes/main.ts"
],
"diags": []
},
"<repo>/tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts": {
"order": [
"<repo>/tests/fixtures/npm/cases/node24-web-stream-pipe-errors/main.ts"
],
"diags": []
},
"<repo>/tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts": {
"order": [
"<repo>/tests/fixtures/npm/cases/node24-web-stream-pipe-abort/main.ts"
],
"diags": []
},
"<repo>/tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts": {
"order": [
"<repo>/tests/fixtures/npm/cases/node24-web-stream-transforms/main.ts"
],
"diags": []
},
"<repo>/tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts": {
"order": [
"<repo>/tests/fixtures/npm/cases/node24-web-stream-encoding/main.ts"
],
"diags": []
}
}
}
+2 -1
View File
@@ -7536,7 +7536,8 @@ static const char isl_modules_bootstrap[] =
" const g = globalThis;\n"
" const w = {\n"
" ReadableStream: g.ReadableStream, TransformStream: g.TransformStream,\n"
" TextDecoderStream: g.TextDecoderStream,\n"
" TextDecoderStream: g.TextDecoderStream, TextEncoderStream: g.TextEncoderStream,\n"
" TransformStreamDefaultController: g.TransformStreamDefaultController,\n"
" WritableStream: g.WritableStream, WritableStreamDefaultWriter: g.WritableStreamDefaultWriter,\n"
" WritableStreamDefaultController: g.WritableStreamDefaultController,\n"
" CountQueuingStrategy: g.CountQueuingStrategy, ByteLengthQueuingStrategy: g.ByteLengthQueuingStrategy,\n"
+2 -1
View File
@@ -131,7 +131,8 @@ static const struct {
"text,buffer,arrayBuffer,json,blob"},
{"node:stream/web",
"ReadableStream,TransformStream,TextDecoderStream,WritableStream,WritableStreamDefaultWri"
"ter,WritableStreamDefaultController,CountQueuingStrategy,ByteLengthQueuingStrategy"},
"ter,WritableStreamDefaultController,CountQueuingStrategy,ByteLengthQueuingStrategy,Trans"
"formStreamDefaultController,TextEncoderStream"},
{"node:crypto",
"createHash,createHmac,hash,Hash,Hmac,randomBytes,randomFillSync,randomFill,randomInt,ran"
"domUUID,getRandomValues,timingSafeEqual,pbkdf2,pbkdf2Sync,getHashes,getCiphers,getCurves"
+246 -73
View File
@@ -426,6 +426,7 @@ static const char web_prelude[] =
" g[C.name] = C;\n"
" }\n"
"\n"
" const readableStreams = new WeakSet();\n"
" class ReadableStream {\n"
" constructor(source, strategy) {\n"
" if (source === undefined) source = {};\n"
@@ -439,7 +440,7 @@ static const char web_prelude[] =
" this._highWaterMark = sizing.highWaterMark;\n"
" this._size = sizing.size;\n"
" this._queueSize = 0;\n"
" this._src = source;\n"
" this._src = source; readableStreams.add(this);\n"
" this._queue = [];\n"
" this._state = 'readable';\n"
" this._storedError = undefined;\n"
@@ -540,7 +541,7 @@ static const char web_prelude[] =
" this._closeRequested = true;\n"
" this._finishClose();\n"
" const src = this._src;\n"
" return Promise.resolve(src.cancel ? src.cancel(reason) : undefined).then(() => undefined);\n"
" return writableInvoke(src.cancel, src, [reason]).then(() => undefined);\n"
" }\n"
" getReader(options) {\n"
" if (options !== undefined && options !== null && options.mode !== undefined) {\n"
@@ -687,96 +688,237 @@ static const char web_prelude[] =
" });\n"
" }\n"
" pipeThrough(transform, options) {\n"
" if (transform === null || typeof transform !== 'object' || !transform.writable || !transform.readable) {\n"
" throw new TypeError('pipeThrough requires a { writable, readable } pair');\n"
" }\n"
" if (options !== undefined && options !== null && options.signal !== undefined) {\n"
" throw new Error('scriptc: AbortSignal is not supported by island streams yet');\n"
" }\n"
" pump(this, transform.writable);\n"
" return transform.readable;\n"
" if (!readableStreams.has(this)) throw streamInvalidThis('ReadableStream');\n"
" const readable = transform?.readable;\n"
" if (!readableStreams.has(readable)) throw streamArgument('transform.readable', 'an instance of ReadableStream', readable);\n"
" const writable = transform?.writable;\n"
" if (!writableStreams.has(writable)) throw streamArgument('transform.writable', 'an instance of WritableStream', writable);\n"
" const opts = pipeOptions(options); pipeLocks(this, writable);\n"
" pipeStreams(this, writable, opts).catch(() => {});\n"
" return readable;\n"
" }\n"
" pipeTo() {\n"
" throw new Error('ReadableStream.pipeTo is not supported in the scriptc island (use pipeThrough or a reader)');\n"
" pipeTo(destination, options) {\n"
" try {\n"
" if (!readableStreams.has(this)) throw streamInvalidThis('ReadableStream');\n"
" if (!writableStreams.has(destination)) throw streamArgument('transform.writable', 'an instance of WritableStream', destination);\n"
" const opts = pipeOptions(options); pipeLocks(this, destination);\n"
" return pipeStreams(this, destination, opts);\n"
" } catch (e) { return Promise.reject(e); }\n"
" }\n"
" tee() {\n"
" throw new Error('ReadableStream.tee is not supported in the scriptc island');\n"
" }\n"
" }\n"
"\n"
" const pump = (rs, ws) => {\n"
" const reader = rs.getReader();\n"
" const writer = ws.getWriter();\n"
" const step = () =>\n"
" reader.read().then((r) => {\n"
" if (r.done) return writer.close();\n"
" return writer.write(r.value).then(step);\n"
" const pipeOptions = (options) => {\n"
" if (options !== undefined && options !== null && typeof options !== 'object' && typeof options !== 'function') throw streamArgument('options', 'of type object', options);\n"
" const preventAbort = options?.preventAbort, preventCancel = options?.preventCancel, preventClose = options?.preventClose, signal = options?.signal;\n"
" if (signal !== undefined && (signal === null || typeof signal !== 'object' || !('aborted' in signal))) throw streamArgument('options.signal', 'an instance of AbortSignal', signal);\n"
" return { preventAbort: !!preventAbort, preventCancel: !!preventCancel, preventClose: !!preventClose, signal };\n"
" };\n"
" const pipeLocks = (source, dest) => {\n"
" if (source._locked) throw streamInvalidState('The ReadableStream is locked');\n"
" if (writableStreams.get(dest).writer !== undefined) throw streamInvalidState('The WritableStream is locked');\n"
" };\n"
" const pipeStreams = (source, dest, { preventClose, preventAbort, preventCancel, signal }) => {\n"
" const reader = ReadableStream.prototype.getReader.call(source), writer = new WritableStreamDefaultWriter(dest), sink = writableStreams.get(dest);\n"
" const completion = streamDeferred();\n"
" let shuttingDown = false, currentWrite = Promise.resolve(), listening = false;\n"
" const finalize = (rejected, error) => {\n"
" writer.releaseLock(); reader.releaseLock();\n"
" if (listening) signal.removeEventListener('abort', abort);\n"
" if (rejected) completion.reject(error); else completion.resolve();\n"
" };\n"
" const waitForWrites = async () => {\n"
" let write;\n"
" do { write = currentWrite; await write; } while (write !== currentWrite);\n"
" };\n"
" const shutdown = (action, rejected = false, error) => {\n"
" if (shuttingDown) return;\n"
" shuttingDown = true;\n"
" const waiting = sink.state === 'writable' && !sink.closeRequest ? waitForWrites() : Promise.resolve();\n"
" waiting.then(() => action ? action() : undefined).then(\n"
" () => finalize(rejected, error), (e) => finalize(true, e));\n"
" };\n"
" const abort = () => {\n"
" const reason = signal.reason;\n"
" shutdown(() => {\n"
" const actions = [];\n"
" if (!preventAbort && sink.state === 'writable') actions.push(writer.abort(reason));\n"
" if (!preventCancel && source._state === 'readable') actions.push(reader.cancel(reason));\n"
" return Promise.all(actions);\n"
" }, true, reason);\n"
" };\n"
" const writeChunk = (chunk) => { currentWrite = writer.write(chunk); currentWrite.catch(() => {}); };\n"
" // Buffered reads register their write before source.closed observers run. Pending\n"
" // reads queue the write during enqueue, ahead of a subsequent close notification.\n"
" const step = async () => {\n"
" if (shuttingDown) return true;\n"
" if (sink.backpressure) { await writer.ready; if (shuttingDown) return true; }\n"
" if (source._state === 'readable' && source._queue.length) {\n"
" while (source._queue.length) {\n"
" if (shuttingDown) return true;\n"
" const queued = source._queue.shift();\n"
" source._queueSize = Math.max(0, source._queueSize - queued.size);\n"
" if (source._closeRequested && !source._queue.length) source._finishClose();\n"
" writeChunk(queued.chunk);\n"
" if (sink.backpressure || sink.state !== 'writable' || sink.closeRequest) break;\n"
" }\n"
" source._maybePull();\n"
" return source._state === 'closed';\n"
" }\n"
" if (source._state === 'closed') return true;\n"
" if (source._state === 'errored') throw source._storedError;\n"
" return new Promise((resolve, reject) => {\n"
" source._reads.push({ resolve(r) {\n"
" if (r.done) resolve(true);\n"
" else Promise.resolve().then(() => { writeChunk(r.value); resolve(false); });\n"
" }, reject });\n"
" source._maybePull();\n"
" });\n"
" step().catch((e) => {\n"
" writer.abort(e).catch(() => {});\n"
" reader.cancel(e).catch(() => {});\n"
" };\n"
" if (signal !== undefined) {\n"
" if (signal.aborted) { abort(); return completion.promise; }\n"
" signal.addEventListener('abort', abort, { once: true, [Symbol.for('scriptc.events.resistStopPropagation')]: true });\n"
" listening = true;\n"
" }\n"
" (async () => { while (!await step()); })().catch(() => {});\n"
" const sourceError = (e) => shutdown(preventAbort ? undefined : () => writer.abort(e), true, e);\n"
" const sinkError = (e) => shutdown(preventCancel ? undefined : () => reader.cancel(e), true, e);\n"
" const sourceClose = () => shutdown(preventClose ? undefined : () => {\n"
" if (sink.closeRequest || sink.state === 'closed') return;\n"
" if (sink.state === 'errored') throw sink.error;\n"
" return writer.close();\n"
" });\n"
" if (source._state === 'errored') sourceError(source._storedError); else reader.closed.catch(sourceError);\n"
" if (sink.state === 'errored') sinkError(sink.error); else writer.closed.catch(sinkError);\n"
" if (source._state === 'closed') sourceClose(); else reader.closed.then(sourceClose, () => {});\n"
" if (sink.closeRequest || sink.state === 'closed') {\n"
" const e = streamInvalidState('Destination WritableStream is closed');\n"
" shutdown(preventCancel ? undefined : () => reader.cancel(e), true, e);\n"
" }\n"
" return completion.promise;\n"
" };\n"
"\n"
" class TransformStream {\n"
" constructor(transformer, ws, rs) {\n"
" if (transformer === undefined || transformer === null) transformer = {};\n"
" if (transformer.readableType !== undefined || transformer.writableType !== undefined) {\n"
" throw new RangeError('readableType/writableType are not supported in the scriptc island');\n"
" const transformStreams = new WeakMap(), transformControllers = new WeakMap();\n"
" const transformToken = Symbol('TransformStreamDefaultController');\n"
" const transformPressure = (s, pressure) => {\n"
" if (s.change) s.change.resolve();\n"
" s.change = streamDeferred(); s.backpressure = pressure;\n"
" };\n"
" const transformClear = (s) => { s.transform = s.flush = s.cancel = undefined; };\n"
" const transformUnblock = (s) => { if (s.backpressure) transformPressure(s, false); };\n"
" const transformErrorWritable = (s, e) => {\n"
" transformClear(s);\n"
" const w = writableStreams.get(s.writable);\n"
" if (w.state === 'writable') { writableClear(w); writableError(w, e); }\n"
" transformUnblock(s);\n"
" };\n"
" const transformError = (s, e) => { s.readable._errorStream(e); transformErrorWritable(s, e); };\n"
" const transformFinish = (s, kind, reason) => {\n"
" if (s.finish) return s.finish;\n"
" const d = streamDeferred(); s.finish = d.promise;\n"
" const result = writableInvoke(kind === 'close' ? s.flush : s.cancel, s.transformer, kind === 'close' ? [s.controller] : [reason]);\n"
" transformClear(s);\n"
" result.then(() => {\n"
" if (kind === 'cancel') {\n"
" const w = writableStreams.get(s.writable);\n"
" if (w.state === 'errored') { d.reject(w.error); return; }\n"
" transformErrorWritable(s, reason);\n"
" } else {\n"
" if (s.readable._state === 'errored') { d.reject(s.readable._storedError); return; }\n"
" if (kind === 'close') { if (s.readable._state === 'readable' && !s.readable._closeRequested) s.readable._controller.close(); }\n"
" else s.readable._errorStream(reason);\n"
" }\n"
" const t = transformer;\n"
" const self = this;\n"
" let rc = null;\n"
" this._readable = new ReadableStream({\n"
" start(c) { rc = c; },\n"
" cancel(reason) { writableError(writableStreams.get(self._writable), reason); },\n"
" }, rs == null ? { highWaterMark: 0 } : rs);\n"
" const tc = {\n"
" enqueue(chunk) { rc.enqueue(chunk); },\n"
" error(e) {\n"
" self._readable._errorStream(e);\n"
" writableError(writableStreams.get(self._writable), e);\n"
" },\n"
" terminate() {\n"
" if (self._readable._state === 'readable' && !self._readable._closeRequested) rc.close();\n"
" writableError(writableStreams.get(self._writable), new TypeError('The transform stream has been terminated'));\n"
" },\n"
" get desiredSize() { return rc.desiredSize; },\n"
" };\n"
" this._controller = tc;\n"
" d.resolve();\n"
" }, (e) => {\n"
" if (kind === 'cancel') transformErrorWritable(s, e); else s.readable._errorStream(e);\n"
" d.reject(e);\n"
" }).catch((e) => d.reject(e));\n"
" return s.finish;\n"
" };\n"
" class TransformStreamDefaultController {\n"
" constructor(token, stream) {\n"
" if (token !== transformToken) throw streamError(TypeError, 'ERR_ILLEGAL_CONSTRUCTOR', 'Illegal constructor');\n"
" transformControllers.set(this, stream);\n"
" }\n"
" get desiredSize() {\n"
" const s = transformControllers.get(this);\n"
" if (!s) throw streamInvalidThis('TransformStreamDefaultController');\n"
" return s.readable._controller.desiredSize;\n"
" }\n"
" enqueue(chunk) {\n"
" const s = transformControllers.get(this);\n"
" if (!s) throw streamInvalidThis('TransformStreamDefaultController');\n"
" const r = s.readable;\n"
" if (r._state !== 'readable' || r._closeRequested) throw streamInvalidState('Unable to enqueue');\n"
" try { r._controller.enqueue(chunk); }\n"
" catch (e) { transformErrorWritable(s, e); throw r._storedError; }\n"
" if (!r._reads.length && r._controller.desiredSize <= 0 && !s.backpressure) transformPressure(s, true);\n"
" }\n"
" error(e) {\n"
" const s = transformControllers.get(this);\n"
" if (!s) throw streamInvalidThis('TransformStreamDefaultController');\n"
" transformError(s, e);\n"
" }\n"
" terminate() {\n"
" const s = transformControllers.get(this);\n"
" if (!s) throw streamInvalidThis('TransformStreamDefaultController');\n"
" if (s.readable._state === 'readable' && !s.readable._closeRequested) s.readable._controller.close();\n"
" transformErrorWritable(s, streamInvalidState('TransformStream has been terminated'));\n"
" }\n"
" }\n"
" class TransformStream {\n"
" constructor(transformer = {}, ws, rs) {\n"
" if (transformer === null || (typeof transformer !== 'object' && typeof transformer !== 'function')) throw streamArgument('transformer', 'of type object', transformer);\n"
" for (const [name, value] of [['writableStrategy', ws], ['readableStrategy', rs]]) {\n"
" if (value !== undefined && value !== null && typeof value !== 'object' && typeof value !== 'function') throw streamArgument(name, 'of type object', value);\n"
" }\n"
" const readableType = transformer.readableType, writableType = transformer.writableType, start = transformer.start;\n"
" if (readableType !== undefined) throw streamInvalidValue('transformer.readableType', readableType);\n"
" if (writableType !== undefined) throw streamInvalidValue('transformer.writableType', writableType);\n"
" const readableSizing = streamStrategy(rs, 0), writableSizing = streamStrategy(ws, 1);\n"
" const s = { transformer, finish: undefined, backpressure: true };\n"
" transformPressure(s, true); transformStreams.set(this, s);\n"
" let resolveStart;\n"
" const startGate = new Promise((resolve) => { resolveStart = resolve; });\n"
" this._writable = new WritableStream({\n"
" s.writable = new WritableStream({\n"
" start() { return startGate; },\n"
" /* Eager transform: writes run the transformer immediately (no\n"
" * readable-side backpressure) — order-exact, documented. */\n"
" write(chunk) {\n"
" let r;\n"
" try { r = t.transform ? t.transform(chunk, tc) : tc.enqueue(chunk); } catch (e) { tc.error(e); throw e; }\n"
" return Promise.resolve(r).catch((e) => { tc.error(e); throw e; });\n"
" async write(chunk) {\n"
" if (s.backpressure) {\n"
" await s.change.promise;\n"
" const w = writableStreams.get(s.writable);\n"
" if (w.state === 'erroring' || w.state === 'errored') throw w.error;\n"
" }\n"
" try { if (s.transform) await s.transform.call(transformer, chunk, s.controller); }\n"
" catch (e) { transformError(s, e); throw e; }\n"
" },\n"
" close() {\n"
" let r;\n"
" try { r = t.flush ? t.flush(tc) : undefined; } catch (e) { tc.error(e); throw e; }\n"
" return Promise.resolve(r).then(\n"
" () => {\n"
" if (self._readable._state === 'readable' && !self._readable._closeRequested) rc.close();\n"
" },\n"
" (e) => { tc.error(e); throw e; },\n"
" );\n"
" },\n"
" abort(e) { self._readable._errorStream(e); },\n"
" }, ws);\n"
" const startResult = t.start ? t.start(tc) : undefined; // synchronous invocation\n"
" resolveStart(Promise.resolve(startResult).catch((e) => { tc.error(e); throw e; }));\n"
" close() { return transformFinish(s, 'close'); },\n"
" abort(reason) { return transformFinish(s, 'abort', reason); },\n"
" }, writableSizing);\n"
" s.readable = new ReadableStream({\n"
" start() { return startGate; },\n"
" pull() { transformPressure(s, false); return s.change.promise; },\n"
" cancel(reason) { return transformFinish(s, 'cancel', reason); },\n"
" }, readableSizing);\n"
" s.controller = new TransformStreamDefaultController(transformToken, s);\n"
" const transform = transformer.transform, flush = transformer.flush, cancel = transformer.cancel;\n"
" for (const [name, fn] of [['transform', transform], ['flush', flush], ['cancel', cancel]]) {\n"
" if (fn && typeof fn !== 'function') throw streamArgument('transformer.' + name, 'of type function', fn);\n"
" }\n"
" s.transform = transform || ((chunk, controller) => controller.enqueue(chunk)); s.flush = flush; s.cancel = cancel;\n"
" const startResult = start === undefined ? undefined : start.call(transformer, s.controller);\n"
" resolveStart(Promise.resolve(startResult).catch((e) => { transformError(s, e); throw e; }));\n"
" }\n"
" get readable() { return this._readable; }\n"
" get writable() { return this._writable; }\n"
" get readable() { const s = transformStreams.get(this); if (!s) throw streamInvalidThis('TransformStream'); return s.readable; }\n"
" get writable() { const s = transformStreams.get(this); if (!s) throw streamInvalidThis('TransformStream'); return s.writable; }\n"
" }\n"
" for (const C of [TransformStream, TransformStreamDefaultController]) {\n"
" Object.defineProperty(C.prototype, Symbol.toStringTag, { value: C.name, configurable: true });\n"
" for (const name of Object.getOwnPropertyNames(C.prototype)) if (name !== 'constructor') Object.defineProperty(C.prototype, name, { enumerable: true });\n"
" g[C.name] = C;\n"
" }\n"
"\n"
" g.ReadableStream = ReadableStream;\n"
" g.TransformStream = TransformStream;\n"
"\n"
"\n"
" const textEncoders = new WeakSet();\n"
" class TextEncoder {\n"
@@ -916,6 +1058,37 @@ static const char web_prelude[] =
" }\n"
" }\n"
"\n"
" class TextEncoderStream {\n"
" #transform;\n"
" constructor() {\n"
" const encoder = new TextEncoder();\n"
" let pending = null;\n"
" this.#transform = new TransformStream({\n"
" transform(chunk, controller) {\n"
" chunk = String(chunk);\n"
" let text = '';\n"
" for (let i = 0; i < chunk.length; i++) {\n"
" const unit = chunk.charCodeAt(i), item = chunk[i];\n"
" if (pending !== null) {\n"
" const high = pending; pending = null;\n"
" if (unit >= 0xdc00 && unit <= 0xdfff) { text += high + item; continue; }\n"
" text += '\\ufffd';\n"
" }\n"
" if (unit >= 0xd800 && unit <= 0xdbff) pending = item;\n"
" else text += unit >= 0xdc00 && unit <= 0xdfff ? '\\ufffd' : item;\n"
" }\n"
" if (text) controller.enqueue(encoder.encode(text));\n"
" },\n"
" flush(controller) { if (pending !== null) controller.enqueue(new Uint8Array([0xef, 0xbf, 0xbd])); },\n"
" });\n"
" }\n"
" get encoding() { this.#transform; return 'utf-8'; }\n"
" get readable() { return this.#transform.readable; }\n"
" get writable() { return this.#transform.writable; }\n"
" }\n"
" for (const name of ['encoding', 'readable', 'writable']) Object.defineProperty(TextEncoderStream.prototype, name, { enumerable: true });\n"
" g.TextEncoderStream = TextEncoderStream;\n"
"\n"
" class TextDecoderStream extends g.TransformStream {\n"
" constructor(label, options) {\n"
" const dec = new TextDecoder(label, options);\n"
@@ -0,0 +1,4 @@
// @dynamic
// @stderr
import { reportEncoding } from "node24-web-stream-fixture/encoding.js";
reportEncoding();
@@ -0,0 +1 @@
{"type":"module"}
@@ -0,0 +1,4 @@
// @dynamic
// @stderr
import { reportPipeAbort } from "node24-web-stream-fixture/pipe-abort.js";
reportPipeAbort();
@@ -0,0 +1 @@
{"type":"module"}
@@ -0,0 +1,4 @@
// @dynamic
// @stderr
import { reportPipeErrors } from "node24-web-stream-fixture/pipe-errors.js";
reportPipeErrors();
@@ -0,0 +1 @@
{"type":"module"}
@@ -0,0 +1,4 @@
// @dynamic
// @stderr
import { reportPipes } from "node24-web-stream-fixture/pipes.js";
reportPipes();
@@ -0,0 +1 @@
{"type":"module"}
@@ -0,0 +1,4 @@
// @dynamic
// @stderr
import { reportTransforms } from "node24-web-stream-fixture/transforms.js";
reportTransforms();
@@ -0,0 +1 @@
{"type":"module"}
@@ -0,0 +1 @@
export declare function reportEncoding(): void;
+32
View File
@@ -0,0 +1,32 @@
import { ReadableStream, WritableStream, TextEncoderStream, TextDecoderStream, TransformStream } from "node:stream/web";
import { out, result, sameError, launch } from "./helpers.js";
export function reportEncoding() { launch(async () => {
const encode = async (values) => {
const got = [];
await ReadableStream.from(values).pipeThrough(new TextEncoderStream()).pipeTo(new WritableStream({ write(v) { got.push([...v].join(",")); } }));
return got.join(" | ");
};
const encoder = new TextEncoderStream();
out("encoder", TextEncoderStream === globalThis.TextEncoderStream, encoder.encoding, encoder.readable instanceof ReadableStream, encoder.writable instanceof WritableStream, encoder instanceof TransformStream, Object.prototype.toString.call(encoder), Object.keys(TextEncoderStream.prototype).join(","));
await encoder.writable.close();
out("ascii", await encode(["hello", "", " world"]));
out("surrogate", await encode(["A\ud83d", "", "\ude00B", "\ud800", "x\udc00", "\udbff"]));
out("surrogate-pairs", await encode(["\ud800", "\ud801", "\udc00", "\udc01"]));
out("empty", await encode(["", ""]));
out("coercion", await encode([undefined, null, false, 42, 3n, Symbol("x"), { toString() { return "object"; } }]));
{
const got = [];
await ReadableStream.from(["Hello ", "\ud83d", "\ude00", "!"]).pipeThrough(new TextEncoderStream()).pipeThrough(new TextDecoderStream()).pipeTo(new WritableStream({ write(v) { got.push(v); } }));
out("roundtrip", got.join(""));
}
{
const reason = new Error("coercion"), t = new TextEncoderStream(), w = t.writable.getWriter(), r = t.readable.getReader();
const read = sameError(r.read(), reason), write = sameError(w.write({ toString() { throw reason; } }), reason);
out("encoding-error", await write, await read, await sameError(w.closed, reason), await sameError(r.closed, reason));
}
for (const name of ["encoding", "readable", "writable"]) {
try { Object.getOwnPropertyDescriptor(TextEncoderStream.prototype, name).get.call({}); out("brand", name, false); }
catch (e) { out("brand", name, e instanceof TypeError); }
}
}); }
@@ -0,0 +1 @@
export declare function reportPipeAbort(): void;
@@ -0,0 +1,53 @@
import { ReadableStream, WritableStream, TransformStream } from "node:stream/web";
import { out, deferred, sameError, launch } from "./helpers.js";
export function reportPipeAbort() { launch(async () => {
for (const preventAbort of [false, true]) for (const preventCancel of [false, true]) {
const controller = new AbortController(), reason = new Error("pre-abort"), log = [];
controller.abort(reason);
const source = new ReadableStream({ cancel(e) { log.push("cancel:" + (e === reason)); } }, { highWaterMark: 0 });
const sink = new WritableStream({ write() { log.push("write"); }, abort(e) { log.push("abort:" + (e === reason)); } });
out("pre-abort", preventAbort, preventCancel, await sameError(source.pipeTo(sink, { signal: controller.signal, preventAbort, preventCancel }), reason), log.sort().join(","), source.locked, sink.locked);
if (preventCancel) await source.cancel(); if (preventAbort) await sink.close();
}
{
const controller = new AbortController(), source = new ReadableStream({ pull() { throw new Error("read before destination capacity"); } }, { highWaterMark: 0 });
let writes = 0;
const sink = new WritableStream({ write() { writes++; } }, { highWaterMark: 0 });
const p = source.pipeTo(sink, { signal: controller.signal });
await new Promise(resolve => setTimeout(resolve, 0)); controller.abort();
out("zero-sink", await sameError(p, controller.signal.reason), controller.signal.reason.name, writes, source.locked, sink.locked);
}
{
const entered = deferred(), controller = new AbortController(), reason = new Error("pending-read"), log = [];
controller.signal.addEventListener("abort", e => { log.push("stopped"); e.stopImmediatePropagation(); });
const source = new ReadableStream({ pull() { entered.resolve(); }, cancel(e) { log.push("cancel:" + (e === reason)); } }, { highWaterMark: 0 });
const sink = new WritableStream({ abort(e) { log.push("abort:" + (e === reason)); } });
const p = source.pipeTo(sink, { signal: controller.signal }); await entered.promise; controller.abort(reason);
out("read-abort", await sameError(p, reason), log.sort().join(","), source.locked, sink.locked);
}
{
const entered = deferred(), gate = deferred(), controller = new AbortController(), reason = new Error("pending-write"), log = [];
const source = new ReadableStream({ start(c) { c.enqueue("a"); c.enqueue("b"); }, cancel(e) { log.push("cancel:" + (e === reason)); } });
const sink = new WritableStream({ async write(v) { log.push("write:" + v); entered.resolve(); await gate.promise; log.push("written"); }, abort(e) { log.push("abort:" + (e === reason)); } });
const p = source.pipeTo(sink, { signal: controller.signal }); await entered.promise; controller.abort(reason);
await Promise.resolve(); out("write-abort-waits", log.join(","), source.locked, sink.locked);
gate.resolve(); out("write-abort", await sameError(p, reason), log.sort().join(","), source.locked, sink.locked);
}
for (const fails of ["abort", "cancel"]) {
const controller = new AbortController(), reason = new Error("reason"), cleanup = new Error("cleanup");
const source = new ReadableStream({ cancel() { if (fails === "cancel") return Promise.reject(cleanup); } });
const sink = new WritableStream({ abort() { if (fails === "abort") return Promise.reject(cleanup); } });
controller.abort(reason);
out("abort-cleanup", fails, await sameError(source.pipeTo(sink, { signal: controller.signal }), cleanup), source.locked, sink.locked);
}
{
const controller = new AbortController(), reason = new Error("through-abort"), entered = deferred();
const source = new ReadableStream({ pull() { entered.resolve(); }, cancel(e) { out("through-cancel", e === reason); } }, { highWaterMark: 0 });
const t = new TransformStream(), output = source.pipeThrough(t, { signal: controller.signal });
const reader = output.getReader(), read = sameError(reader.read(), reason);
await entered.promise; controller.abort(reason); out("through-abort", await read, await sameError(reader.closed, reason));
reader.releaseLock(); await new Promise(resolve => setTimeout(resolve, 0));
out("through-unlocked", source.locked, t.writable.locked);
}
}); }
@@ -0,0 +1 @@
export declare function reportPipeErrors(): void;
@@ -0,0 +1,46 @@
import { ReadableStream, WritableStream } from "node:stream/web";
import { out, deferred, result, sameError, launch } from "./helpers.js";
export function reportPipeErrors() { launch(async () => {
for (const preventAbort of [false, true]) for (const cleanupFails of [false, true]) {
const reason = new Error("source"), cleanup = new Error("abort"), log = [];
const source = new ReadableStream({ start(c) { c.error(reason); } });
const sink = new WritableStream({ abort(e) { log.push(e === reason); if (cleanupFails) throw cleanup; } });
const p = source.pipeTo(sink, { preventAbort });
out("source-error", preventAbort, cleanupFails, await sameError(p, cleanupFails && !preventAbort ? cleanup : reason), log.join(","), source.locked, sink.locked);
if (preventAbort) await sink.close();
}
for (const preventCancel of [false, true]) for (const cleanupFails of [false, true]) {
const reason = new Error("sink"), cleanup = new Error("cancel"), log = [];
const source = new ReadableStream({ start(c) { c.enqueue("chunk"); }, cancel(e) { log.push(e === reason); if (cleanupFails) throw cleanup; } });
const sink = new WritableStream({ write() { throw reason; } });
out("sink-error", preventCancel, cleanupFails, await sameError(source.pipeTo(sink, { preventCancel }), cleanupFails && !preventCancel ? cleanup : reason), log.join(","), source.locked, sink.locked);
if (preventCancel) await result(source.cancel());
}
{
const reason = new Error("already"), source = new ReadableStream({ cancel(e) { out("initial-cancel", e === reason); } });
const sink = new WritableStream({ start(c) { c.error(reason); } });
out("initial-error", await sameError(source.pipeTo(sink), reason), source.locked, sink.locked);
}
{
const entered = deferred(), gate = deferred(), log = []; let controller;
const reason = new Error("during-write");
const source = new ReadableStream({ start(c) { controller = c; c.enqueue("a"); } });
const sink = new WritableStream({ async write(v) { log.push(v); entered.resolve(); await gate.promise; log.push("written"); }, abort(e) { log.push("abort:" + (e === reason)); } });
const p = sameError(source.pipeTo(sink), reason); await entered.promise; controller.error(reason);
await Promise.resolve(); out("error-waits", log.join(","), source.locked, sink.locked);
gate.resolve(); out("error-done", await p, log.join(","), source.locked, sink.locked);
}
{
let controller; const entered = deferred(), reason = new Error("idle-destination");
const source = new ReadableStream({ pull() { entered.resolve(); }, cancel(e) { out("idle-cancel", e === reason); } }, { highWaterMark: 0 });
const sink = new WritableStream({ start(c) { controller = c; } });
const p = source.pipeTo(sink); await entered.promise; controller.error(reason);
out("idle-error", await sameError(p, reason), source.locked, sink.locked);
}
{
const reason = new Error("close"), source = ReadableStream.from(["last"]);
const sink = new WritableStream({ write(v) { out("close-last", v); }, close() { throw reason; } });
out("close-error", await sameError(source.pipeTo(sink), reason), source.locked, sink.locked);
}
}); }
+1
View File
@@ -0,0 +1 @@
export declare function reportPipes(): void;
+98
View File
@@ -0,0 +1,98 @@
import { ReadableStream, WritableStream, TransformStream } from "node:stream/web";
import { out, deferred, result, caught, launch } from "./helpers.js";
export function reportPipes() { launch(async () => {
for (const highWaterMark of [1, 4]) {
const log = [];
const source = new ReadableStream({ start(c) { for (const v of [1, 2, 3]) c.enqueue(v); c.close(); } });
const sink = new WritableStream({ write(v) { log.push(v); }, close() { log.push("closed"); } }, { highWaterMark });
const p = source.pipeTo(sink);
out("locks", highWaterMark, source.locked, sink.locked);
out("pipe", await p, log.join(","), source.locked, sink.locked);
}
{
let controller;
const source = new ReadableStream({ start(c) { controller = c; } }, { highWaterMark: 0 });
const got = [], sink = new WritableStream({ write(v) { got.push(v); }, close() { got.push("close"); } });
const p = source.pipeTo(sink);
controller.enqueue("last"); controller.close(); await p;
out("pending-last", got.join(","), source.locked, sink.locked);
}
{
const entered = deferred(), gate = deferred(), log = [];
const source = new ReadableStream({ start(c) { c.enqueue("last"); c.close(); } });
const sink = new WritableStream({ async write(v) { log.push(v); entered.resolve(); await gate.promise; log.push("written"); }, close() { log.push("closed"); } });
const p = source.pipeTo(sink); await entered.promise;
out("final-inflight", log.join(","), source.locked, sink.locked);
gate.resolve(); await p; out("final-settled", log.join(","), source.locked, sink.locked);
}
{
const entered = deferred(), gate = deferred(); let pulls = 0;
const source = new ReadableStream({ pull(c) { c.enqueue(++pulls); if (pulls === 3) c.close(); } }, { highWaterMark: 0 });
const got = [], sink = new WritableStream({ async write(v) { got.push(v); if (v === 1) { entered.resolve(); await gate.promise; } } });
const p = source.pipeTo(sink); await entered.promise;
out("pipe-demand", pulls, got.join(",")); gate.resolve(); await p;
out("pipe-drain", pulls, got.join(","));
}
for (const preventClose of [false, true]) {
const log = [], source = ReadableStream.from(["a", "b"]);
const sink = new WritableStream({ write(v) { log.push(v); }, close() { log.push("close"); } });
await source.pipeTo(sink, { preventClose });
out("prevent-close", preventClose, log.join(","), source.locked, sink.locked);
const w = sink.getWriter(); out("after-pipe", await result(w.write("later")));
if (preventClose) await w.close(); w.releaseLock();
}
{
const source = ReadableStream.from([1, 2]);
const twice = new TransformStream({ transform(v, c) { c.enqueue(v * 2); } });
const plus = new TransformStream({ transform(v, c) { c.enqueue(v + 1); }, flush(c) { c.enqueue("flush"); } });
const got = [], sink = new WritableStream({ write(v) { got.push(v); } });
const output = source.pipeThrough(twice).pipeThrough(plus);
out("chain-locked", source.locked, twice.writable.locked, twice.readable.locked, plus.writable.locked, output === plus.readable);
await output.pipeTo(sink);
out("chain", got.join(","), ...[source, twice.writable, twice.readable, plus.writable, plus.readable, sink].map(s => s.locked));
}
{
const source = ReadableStream.from(["kept-open"]), t = new TransformStream();
const output = source.pipeThrough(t, { preventClose: true }), reader = output.getReader();
out("through-prevent-close", (await reader.read()).value);
await new Promise(resolve => setTimeout(resolve, 0));
out("through-released", source.locked, t.writable.locked);
await t.writable.close(); out("through-manual-close", (await reader.read()).done); reader.releaseLock();
}
{
const got = [], source = ReadableStream.from(["internal"]), sink = new WritableStream({ write(v) { got.push(v); } });
source.getReader = sink.getWriter = () => { throw new Error("public acquisition override"); };
await source.pipeTo(sink); out("internal-locks", got.join(","), source.locked, sink.locked);
}
{
const order = [], source = new ReadableStream({ start(c) { c.close(); } }), sink = new WritableStream();
const options = Object.fromEntries(["preventAbort", "preventCancel", "preventClose", "signal"].map(name => [name, undefined]));
for (const name of Object.keys(options)) Object.defineProperty(options, name, { get() { order.push(name); return undefined; } });
await source.pipeTo(sink, options); out("option-order", order.join(","));
}
for (const options of [null, [], () => {}]) {
const source = new ReadableStream({ start(c) { c.close(); } });
out("option-object", await result(source.pipeTo(new WritableStream(), options)));
}
out("invalid-source", await result(ReadableStream.prototype.pipeTo.call({}, new WritableStream())));
out("invalid-destination", await result(new ReadableStream().pipeTo({})));
out("invalid-options", await result(new ReadableStream().pipeTo(new WritableStream(), 1)));
out("invalid-signal", await result(new ReadableStream().pipeTo(new WritableStream(), { signal: null })));
out("invalid-through", caught(() => new ReadableStream().pipeThrough({})), caught(() => new ReadableStream().pipeThrough({ readable: new ReadableStream(), writable: {} })));
{
const source = new ReadableStream(), sink = new WritableStream(), r = source.getReader();
out("locked-source", await result(source.pipeTo(sink)), caught(() => source.pipeThrough({ readable: new ReadableStream(), writable: sink })), sink.locked);
r.releaseLock(); const w = sink.getWriter();
out("locked-sink", await result(source.pipeTo(sink)), source.locked); w.releaseLock();
}
for (const closing of [false, true]) {
const gate = deferred(), reason = deferred();
const sink = new WritableStream({ close() { return gate.promise; } });
const close = sink.close(); if (!closing) { gate.resolve(); await close; }
const source = new ReadableStream({ cancel(e) { reason.resolve(e); } });
const p = source.pipeTo(sink);
out("closed-destination", closing, await result(p), (await reason.promise).code, source.locked, sink.locked);
gate.resolve(); await close;
}
}); }
@@ -0,0 +1 @@
export declare function reportTransforms(): void;
@@ -0,0 +1,77 @@
import { TransformStream, TransformStreamDefaultController, ByteLengthQueuingStrategy } from "node:stream/web";
import { out, deferred, result, caught, sameError, launch } from "./helpers.js";
export function reportTransforms() { launch(async () => {
out("controller-export", TransformStreamDefaultController === globalThis.TransformStreamDefaultController, caught(() => new TransformStreamDefaultController()));
out("controller-brand", ...["enqueue", "error", "terminate"].map(name => caught(() => TransformStreamDefaultController.prototype[name].call({}))), caught(() => Object.getOwnPropertyDescriptor(TransformStreamDefaultController.prototype, "desiredSize").get.call({})));
out("transform-brand", ...["readable", "writable"].map(name => caught(() => Object.getOwnPropertyDescriptor(TransformStream.prototype, name).get.call({}))));
{
let controller, transformed = 0;
const t = new TransformStream({ start(c) { controller = c; }, transform(v, c) { transformed++; c.enqueue(v); } });
const w = t.writable.getWriter(), r = t.readable.getReader();
const first = w.write("a"), second = w.write("b");
await new Promise(resolve => setTimeout(resolve, 0));
out("no-demand", transformed, controller.desiredSize, w.desiredSize, controller instanceof TransformStreamDefaultController, Object.prototype.toString.call(controller));
out("first-demand", (await r.read()).value); await first;
out("one-demand", transformed, controller.desiredSize);
out("second-demand", (await r.read()).value); await second;
await w.close(); out("demand-closed", (await r.read()).done, controller.desiredSize, await result(w.closed));
out("enqueue-closed", caught(() => controller.enqueue("late")));
}
{
const entered = deferred(); let controller, transformed = 0;
const t = new TransformStream({ start(c) { controller = c; }, transform(v, c) { transformed++; c.enqueue(v); if (transformed === 2) entered.resolve(); } }, undefined, new ByteLengthQueuingStrategy({ highWaterMark: 4 }));
const w = t.writable.getWriter(), r = t.readable.getReader();
const writes = [w.write(new Uint8Array(3)), w.write(new Uint8Array(2)), w.write(new Uint8Array(1))];
await entered.promise; await writes[1];
out("transform-capacity", transformed, controller.desiredSize);
out("sized-read", (await r.read()).value.byteLength); await writes[2];
out("transform-resumed", transformed, controller.desiredSize);
const close = w.close(), got = []; for (;;) { const v = await r.read(); if (v.done) break; got.push(v.value.byteLength); } await close;
out("sized-rest", got.join(","));
}
{
const start = deferred(), flush = deferred(), entered = deferred(), log = [];
const transformer = { start(c) { log.push("start:" + (this === transformer)); c.enqueue("start"); return start.promise; }, transform(v, c) { log.push("transform:" + (this === transformer)); c.enqueue(v); }, async flush(c) { log.push("flush:" + (this === transformer)); entered.resolve(); await flush.promise; c.enqueue("flush"); } };
const t = new TransformStream(transformer), w = t.writable.getWriter(), r = t.readable.getReader();
transformer.transform = () => { throw new Error("changed"); }; transformer.flush = transformer.transform;
const write = w.write("value"); out("start-buffer", (await r.read()).value, log.join(","));
const read = r.read(); start.resolve(); out("start-release", (await read).value); await write;
const close = w.close(); await entered.promise; out("flush-pending", log.join(","));
const final = r.read(); flush.resolve(); out("flush-value", (await final).value); await close; out("flush-done", (await r.read()).done);
}
for (const kind of ["error", "terminate", "cancel"]) {
let c; const reason = new Error("controller-error");
const t = new TransformStream({ start(controller) { c = controller; } });
const w = t.writable.getWriter(), r = t.readable.getReader();
const write = result(w.write("blocked")); await new Promise(resolve => setTimeout(resolve, 0));
if (kind === "error") c.error(reason);
if (kind === "terminate") c.terminate();
if (kind === "cancel") await r.cancel(reason);
out("unblock", kind, await write, await result(w.closed), await result(r.read()), c.desiredSize);
}
for (const kind of ["transform", "flush", "start"]) {
const reason = new Error(kind), t = new TransformStream({ [kind]() { return Promise.reject(reason); } });
const w = t.writable.getWriter(), r = t.readable.getReader(), read = sameError(r.read(), reason);
if (kind === "transform") out("transform-failure", await sameError(w.write("x"), reason));
if (kind === "flush") out("flush-failure", await sameError(w.close(), reason));
out("both-error", kind, await read, await sameError(w.closed, reason), await sameError(r.closed, reason));
}
for (const direction of ["readable", "writable"]) for (const fails of [false, true]) {
const entered = deferred(), gate = deferred(), reason = new Error("cancel"), failure = new Error("cancel-failure"), log = [];
const transformer = { cancel(e) { log.push(this === transformer && e === reason); entered.resolve(); return gate.promise; } };
const t = new TransformStream(transformer), w = t.writable.getWriter(), r = t.readable.getReader();
transformer.cancel = () => { throw new Error("changed"); };
const end = direction === "readable" ? r.cancel(reason) : w.abort(reason);
await entered.promise; out("cancel-wait", direction, fails, log.join(","));
if (fails) gate.reject(failure); else gate.resolve();
out("cancel-end", direction, fails, await result(end), await sameError(direction === "readable" ? w.closed : r.closed, fails ? failure : reason));
}
{
const entered = deferred(), gate = deferred(), reason = new Error("race"), log = [];
const t = new TransformStream({ async flush() { log.push("flush"); entered.resolve(); await gate.promise; }, cancel() { log.push("cancel"); } });
const w = t.writable.getWriter(), r = t.readable.getReader(), close = result(w.close());
await entered.promise; const cancel = result(r.cancel(reason)); gate.resolve();
out("flush-cancel-race", await close, await cancel, log.join(","), await result(w.closed), (await r.read()).done);
}
}); }
+5 -7
View File
@@ -136,16 +136,16 @@ describe(`island web globals (scriptc-only${sanitize ? ", sanitized" : ""})`, ()
expect(result.exitCode).toBe(0);
expect(result.stderr).toBe("");
expect(result.stdout.trimEnd()).toBe(
"function function function function function function function true false true true false false true",
"function function function function function function function true true true true false false true",
);
});
test("writable stream constructors and strategies are installed without an npm graph", async () => {
test("stream constructors and strategies are installed without an npm graph", async () => {
const out = await islandEval(
"web-writable-presence",
`[typeof WritableStream, typeof WritableStreamDefaultWriter, typeof WritableStreamDefaultController, typeof CountQueuingStrategy, typeof ByteLengthQueuingStrategy].join(' ')`,
`[typeof WritableStream, typeof WritableStreamDefaultWriter, typeof WritableStreamDefaultController, typeof CountQueuingStrategy, typeof ByteLengthQueuingStrategy, typeof TransformStreamDefaultController, typeof TextEncoderStream].join(' ')`,
);
expect(out).toBe("function function function function function");
expect(out).toBe("function function function function function function function");
});
test("structuredClone clones deep (cycles included) and validates options like Node", async () => {
@@ -201,14 +201,13 @@ describe(`island web globals (scriptc-only${sanitize ? ", sanitized" : ""})`, ()
expect(out).toBe("function function true true");
});
test("tee, pipeTo, BYOB readers, and byte streams are fenced with clear errors", async () => {
test("tee, BYOB readers, and byte streams are fenced with clear errors", async () => {
const out = await islandEval(
"web-fences",
`(() => {
const grab = (f) => { try { f(); return 'no-throw'; } catch (e) { return e.message; } };
return [
grab(() => new ReadableStream().tee()),
grab(() => new ReadableStream().pipeTo({})),
grab(() => new ReadableStream().getReader({ mode: 'byob' })),
grab(() => new ReadableStream({ type: 'bytes' })),
].join(' | ');
@@ -217,7 +216,6 @@ describe(`island web globals (scriptc-only${sanitize ? ", sanitized" : ""})`, ()
expect(out).toBe(
[
"ReadableStream.tee is not supported in the scriptc island",
"ReadableStream.pipeTo is not supported in the scriptc island (use pipeThrough or a reader)",
"BYOB readers are not supported in the scriptc island",
"byte streams (type: 'bytes') are not supported in the scriptc island",
].join(" | "),