mirror of
https://github.com/HeyPuter/node-worker.git
synced 2026-11-13 18:34:31 -10:00
137 lines
3.9 KiB
JavaScript
137 lines
3.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import path from "node:path";
|
|
import { rollup } from "rollup";
|
|
import typescript from "@rollup/plugin-typescript";
|
|
|
|
const root = path.resolve(import.meta.dirname, "..");
|
|
const entry = "\0wire-test-entry";
|
|
const bundle = await rollup({
|
|
input: "wire-test-entry",
|
|
plugins: [
|
|
{
|
|
name: "wire-test-entry",
|
|
resolveId(id) {
|
|
return id === "wire-test-entry" ? entry : null;
|
|
},
|
|
load(id) {
|
|
if (id !== entry) return null;
|
|
return `export { PortEndpoint } from ${JSON.stringify(path.join(root, "src/wire/endpoint.ts"))};
|
|
export { makeDispatcher } from ${JSON.stringify(path.join(root, "src/wire/router.ts"))};`;
|
|
},
|
|
},
|
|
typescript({
|
|
tsconfig: path.join(root, "tsconfig.worker.json"),
|
|
declaration: false,
|
|
}),
|
|
],
|
|
});
|
|
const { output } = await bundle.generate({ format: "es" });
|
|
await bundle.close();
|
|
const { PortEndpoint, makeDispatcher } = await import(
|
|
`data:text/javascript;base64,${Buffer.from(output[0].code).toString("base64")}`
|
|
);
|
|
|
|
const left = new PortEndpoint();
|
|
const right = new PortEndpoint();
|
|
const channel = new MessageChannel();
|
|
left.attach(channel.port1);
|
|
right.attach(channel.port2);
|
|
right.router.register(
|
|
7,
|
|
makeDispatcher(7, async (request) => ({
|
|
value: { echoed: request.value },
|
|
}))
|
|
);
|
|
// Node loads its own MessageEvent implementation on the first port message.
|
|
// Warm that platform path before mutating prototypes; the test targets our wire.
|
|
await left.call(7, { op: "warmup", value: 0 });
|
|
|
|
const targets = [
|
|
[JSON, "stringify"],
|
|
[JSON, "parse"],
|
|
[Map.prototype, "get"],
|
|
[Map.prototype, "set"],
|
|
[Map.prototype, "has"],
|
|
[Map.prototype, "delete"],
|
|
[Map.prototype, "clear"],
|
|
[Map.prototype, "forEach"],
|
|
[MessagePort.prototype, "postMessage"],
|
|
[MessagePort.prototype, "start"],
|
|
[DataView.prototype, "getUint16"],
|
|
[DataView.prototype, "getUint32"],
|
|
[DataView.prototype, "setUint16"],
|
|
[DataView.prototype, "setUint32"],
|
|
[TextEncoder.prototype, "encode"],
|
|
[TextDecoder.prototype, "decode"],
|
|
[Uint8Array.prototype, "set"],
|
|
[Uint8Array.prototype, "subarray"],
|
|
[Array.prototype, "push"],
|
|
[Array.prototype, "map"],
|
|
[Array.prototype, "slice"],
|
|
[Promise.prototype, "then"],
|
|
[Promise.prototype, "catch"],
|
|
];
|
|
const originals = targets.map(([object, name]) => [object, name, object[name]]);
|
|
const intercepted = () => {
|
|
throw new Error("internal call intercepted");
|
|
};
|
|
let result;
|
|
const deadline = setTimeout(
|
|
() => left.close(new Error("wire timed out")),
|
|
2000
|
|
);
|
|
try {
|
|
for (const [object, name] of targets) object[name] = intercepted;
|
|
result = await left.call(7, { op: "probe", value: 42 });
|
|
} finally {
|
|
clearTimeout(deadline);
|
|
for (const [object, name, original] of originals) object[name] = original;
|
|
left.close();
|
|
right.close();
|
|
}
|
|
assert.deepEqual(result.decoded.header.result, {
|
|
ok: true,
|
|
value: { echoed: 42 },
|
|
});
|
|
const chunkSizes = [131_072, 262_144, 524_288];
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
for (const size of chunkSizes) controller.enqueue(new Uint8Array(size));
|
|
controller.close();
|
|
},
|
|
});
|
|
// Reattach for the stream test after the poisoned-prototype test closed both ends.
|
|
const streamChannel = new MessageChannel();
|
|
const streamSender = new PortEndpoint();
|
|
const streamReceiver = new PortEndpoint();
|
|
streamSender.attach(streamChannel.port1);
|
|
streamReceiver.attach(streamChannel.port2);
|
|
streamReceiver.router.register(
|
|
8,
|
|
makeDispatcher(8, async (_request, _parts, attachments) => ({
|
|
transfer: [attachments[0]],
|
|
}))
|
|
);
|
|
const transferred = await streamSender.call(
|
|
8,
|
|
{ op: "stream" },
|
|
{ transfer: [stream] }
|
|
);
|
|
const reader = transferred.attachments[0].getReader();
|
|
let bytes = 0;
|
|
for (;;) {
|
|
const next = await reader.read();
|
|
if (next.done) break;
|
|
bytes += next.value.byteLength;
|
|
}
|
|
assert.equal(
|
|
bytes,
|
|
chunkSizes.reduce((a, b) => a + b, 0)
|
|
);
|
|
streamSender.close();
|
|
streamReceiver.close();
|
|
console.log("wire survives builtin and prototype replacement");
|
|
console.log(
|
|
"stream attachment transfers 917504 bytes without framing or buffering limits"
|
|
);
|