Files
scriptc/tests/fixtures/fetch/static-web-streams/main.mts
T
Chris Tate 22357e8bed Lower ReadableStream tee, pipeTo and pipeThrough in TypeScript
In TypeScript, tee(), pipeTo() and pipeThrough() on a ReadableStream were
refused with SC2020, and the WritableStream constructor and a
TransformStream with a transformer had no lowering, while JavaScript
programs reach all of them through the web stream classes
(scr_webstreams.c).

A TypeScript program that uses one of them now builds its ReadableStream,
WritableStream and TransformStream values with those classes, as
JavaScript does, and %main installs them, so a native stream such as a
Response body answers the three methods through its web view. A program
that uses only getReader(), cancel(), locked and the identity transform
keeps the native streams and builds to the same binary.

The fallback declarations gain UnderlyingSink and Transformer, the
WritableStream constructor takes a sink and the TransformStream
constructor a transformer and queuing strategies, and their controllers
map to checked-dynamic handles. The fetch profile lists the three methods
as static with the new static-web-streams fetch fixture as their
differential evidence, and keeps them in the set that links the classes.
The surface manifest is regenerated, and the surface-manifest probe that
kept tee() and pipeTo() fenced now fences values() and the async
iterator.

Adds the static-web-streams fixture and the corpus program
web-streams-typed-pipes.ts, with its corpus cost and order baseline.
2026-10-28 22:18:11 -05:00

97 lines
2.7 KiB
TypeScript

// ReadableStream tee(), pipeTo() and pipeThrough() in a static build: a
// fetched body (whole and chunked) tee'd, piped into a WritableStream and
// through a TransformStream, constructed streams through a transformer,
// and Node's TypeErrors for locked streams and errors through a pipe.
const base = process.argv[2]!;
const describe = (e: unknown): string => (e instanceof Error ? `${e.name}: ${e.message}` : String(e));
const decoder = new TextDecoder();
async function collect(stream: ReadableStream<Uint8Array>): Promise<string> {
const reader = stream.getReader();
let text = "";
for (;;) {
const result = await reader.read();
if (result.done) break;
text += decoder.decode(result.value, { stream: true });
}
return text + decoder.decode();
}
const fetched = await fetch(`${base}/text`);
const [left, right] = fetched.body!.tee();
console.log("tee", await collect(left), await collect(right), fetched.bodyUsed);
const chunked = await fetch(`${base}/chunked`);
const parts: string[] = [];
await chunked.body!.pipeTo(
new WritableStream<Uint8Array>({
write(chunk) {
parts.push(decoder.decode(chunk, { stream: true }));
},
close() {
parts.push("<close>");
},
}),
);
console.log("pipeTo", parts.join("").length > 0, parts[parts.length - 1], chunked.bodyUsed);
const upper = await fetch(`${base}/text`);
const shouted = upper.body!.pipeThrough(
new TransformStream<Uint8Array, Uint8Array>({
transform(chunk, controller) {
controller.enqueue(new TextEncoder().encode(decoder.decode(chunk).toUpperCase()));
},
}),
);
console.log("pipeThrough", await collect(shouted));
const words = new ReadableStream<string>({
start(controller) {
controller.enqueue("alpha");
controller.enqueue("beta");
controller.close();
},
});
const counted = words.pipeThrough(
new TransformStream<string, number>({
transform(chunk, controller) {
controller.enqueue(chunk.length);
},
flush(controller) {
controller.enqueue(-1);
},
}),
);
const counts: number[] = [];
const reader = counted.getReader();
for (;;) {
const result = await reader.read();
if (result.done) break;
counts.push(result.value);
}
console.log("constructed", counts.join(" "));
const locked = await fetch(`${base}/text`);
locked.body!.getReader();
try {
locked.body!.tee();
} catch (e) {
console.log("tee locked", describe(e));
}
try {
await locked.body!.pipeTo(new WritableStream<Uint8Array>());
} catch (e) {
console.log("pipeTo locked", describe(e));
}
const failing = new ReadableStream<string>({
start(controller) {
controller.error(new TypeError("source failed"));
},
});
try {
await failing.pipeTo(new WritableStream<string>());
} catch (e) {
console.log("pipeTo error", describe(e));
}