mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
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.
97 lines
2.7 KiB
TypeScript
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));
|
|
}
|