mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-25 06:24:27 +08:00
68 lines
4.8 KiB
JavaScript
68 lines
4.8 KiB
JavaScript
import { ReadableStream, WritableStream, TransformStream } from "node:stream/web";
|
|
import { out, deferred, result, caught, sameError, launch } from "./helpers.js";
|
|
|
|
export function reportTee() { launch(async () => {
|
|
{
|
|
const object = { n: 1 }, source = ReadableStream.from(["a", object, undefined, "last"]);
|
|
const [a, b] = source.tee(); out("tee-lock", source.locked, a.locked, b.locked, caught(() => source.getReader()));
|
|
const collect = async s => { const got = []; for await (const v of s) got.push(v); return got; };
|
|
const left = await collect(a), right = await collect(b);
|
|
out("tee-values", left.length, right.length, left.map(String).join(","), right.map(String).join(","), left[1] === right[1], left[1] === object, source.locked, a.locked, b.locked);
|
|
}
|
|
{
|
|
const gate = deferred(), entered = deferred(); let pulls = 0;
|
|
const source = new ReadableStream({ async pull(c) { pulls++; if (pulls === 1) { entered.resolve(); await gate.promise; } c.enqueue(pulls); if (pulls === 3) c.close(); } }, { highWaterMark: 0 });
|
|
const [a, b] = source.tee(), ra = a.getReader(), rb = b.getReader();
|
|
const first = ra.read(), other = rb.read(); await entered.promise;
|
|
out("serialized-tee", pulls); gate.resolve();
|
|
out("tee-first", (await first).value, (await other).value);
|
|
const restA = [], restB = [];
|
|
for (;;) { const v = await ra.read(); if (v.done) break; restA.push(v.value); }
|
|
for (;;) { const v = await rb.read(); if (v.done) break; restB.push(v.value); }
|
|
out("tee-rest", pulls, restA.join(","), restB.join(",")); ra.releaseLock(); rb.releaseLock();
|
|
}
|
|
for (const firstBranch of [0, 1]) {
|
|
const reason = new Error("one-branch"), source = ReadableStream.from([1, 2, 3]), branches = source.tee();
|
|
let canceled = false; const cancel = branches[firstBranch].cancel(reason).then(() => { canceled = true; });
|
|
await new Promise(resolve => setTimeout(resolve, 0)); out("one-cancel-waits", firstBranch, canceled);
|
|
const got = []; for await (const v of branches[1 - firstBranch]) got.push(v); await cancel;
|
|
out("one-cancel-drain", firstBranch, got.join(","), canceled, source.locked);
|
|
}
|
|
for (const reverse of [false, true]) for (const fails of [false, true]) {
|
|
const gate = deferred(), entered = deferred(), reasons = [{ side: "left" }, { side: "right" }], failure = new Error("cancel-failure"); let combined;
|
|
const source = new ReadableStream({ cancel(reason) { combined = reason; entered.resolve(); return gate.promise; } }, { highWaterMark: 0 }), [a, b] = source.tee();
|
|
let left, right;
|
|
if (reverse) { right = result(b.cancel(reasons[1])); left = result(a.cancel(reasons[0])); } else { left = result(a.cancel(reasons[0])); right = result(b.cancel(reasons[1])); }
|
|
await entered.promise;
|
|
out("both-cancel", reverse, fails, Array.isArray(combined), combined[0] === reasons[0], combined[1] === reasons[1], source.locked);
|
|
if (fails) gate.reject(failure); else gate.resolve();
|
|
out("both-result", await left, await right, (await a.getReader().read()).done, (await b.getReader().read()).done);
|
|
}
|
|
{
|
|
const reason = new Error("source-error"); let c;
|
|
const source = new ReadableStream({ start(controller) { c = controller; } }, { highWaterMark: 0 }), [a, b] = source.tee();
|
|
const ra = a.getReader(), rb = b.getReader(), left = sameError(ra.read(), reason), right = sameError(rb.read(), reason);
|
|
c.error(reason); out("tee-error", await left, await right, await sameError(ra.closed, reason), await sameError(rb.closed, reason), source.locked);
|
|
}
|
|
{
|
|
const reason = new Error("idle-error"); let c;
|
|
const source = new ReadableStream({ start(controller) { c = controller; } }, { highWaterMark: 0 }), [a, b] = source.tee();
|
|
const cancel = a.cancel("one"); c.error(reason); await cancel;
|
|
out("cancel-error", (await a.getReader().read()).done, await sameError(b.getReader().read(), reason));
|
|
}
|
|
for (const state of ["closed", "errored"]) {
|
|
const reason = new Error("initial"), source = new ReadableStream({ start(c) { if (state === "closed") c.close(); else c.error(reason); } });
|
|
const [a, b] = source.tee(); out("initial-tee", state, await result(a.getReader().read()), await result(b.getReader().read()), source.locked);
|
|
}
|
|
{
|
|
const source = ReadableStream.from(["a", "b"]), [a, b] = source.tee(), got = [];
|
|
const left = a.pipeThrough(new TransformStream({ transform(v, c) { c.enqueue(v.toUpperCase()); } })).pipeTo(new WritableStream({ write(v) { got.push(v); } }));
|
|
const right = b.pipeTo(new WritableStream({ write(v) { got.push(v + "!"); } })); await Promise.all([left, right]);
|
|
out("tee-pipelines", got.sort().join(","), a.locked, b.locked, source.locked);
|
|
}
|
|
out("tee-brand", caught(() => ReadableStream.prototype.tee.call({})));
|
|
{
|
|
const source = new ReadableStream(), reader = source.getReader(); out("tee-locked", caught(() => source.tee())); reader.releaseLock();
|
|
}
|
|
}); }
|