import assert from "node:assert/strict"; import fs from "node:fs"; import vm from "node:vm"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); const ts = require("../web/node_modules/typescript"); const source = fs.readFileSync(new URL("../web/src/assembly.worker.ts", import.meta.url), "utf8"); const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }, }).outputText; const tick = () => new Promise(resolve => setImmediate(resolve)); function start() { const messages = [], loads = [], revoked = []; const scope = { postMessage: message => messages.push(message) }; const envelope = { FacadeSerialQueue: class {}, createFailure: (requestId, code, message) => ({ kind: "error", requestId, error: { code, message } }), }; const factory = () => new Promise((resolve, reject) => loads.push({ resolve, reject })); let sequence = 0; vm.runInNewContext(compiled, { exports: {}, self: scope, Blob, Error, fetch: async () => ({ ok: true, text: async () => "loader" }), URL: { createObjectURL: () => `blob:test-${++sequence}`, revokeObjectURL: url => revoked.push(url) }, require: name => name.startsWith("blob:") ? { default: factory } : name.includes("facade-envelope") ? envelope : {}, }); return { scope, messages, loads, revoked }; } const success = start(); await tick(); assert.equal(success.loads.length, 1); assert.equal(success.messages.length, 0, "ready waits for native initialization"); assert.equal(success.revoked.length, 0, "loader URL survives asynchronous factory initialization"); success.loads[0].resolve({}); await tick(); assert.equal(success.messages[0].kind, "ready"); assert.equal(success.revoked.length, 1); await success.scope.onmessage({ data: { kind: "reset", requestId: "reset-1" } }); await tick(); assert.equal(success.messages.length, 1, "reset waits for its runtime"); success.loads[1].reject(new Error("missing wasm")); await tick(); assert.equal(success.messages[1].requestId, "reset-1"); assert.equal(success.messages[1].error.code, "native_load_failed"); assert.equal(success.messages[1].error.message, "missing wasm"); const failure = start(); await tick(); failure.loads[0].reject(new Error("startup failed")); await tick(); assert.equal(failure.messages.length, 1); assert.equal(failure.messages[0].requestId, "startup"); assert.equal(failure.messages[0].error.code, "native_load_failed"); const reset = start(); await tick(); await reset.scope.onmessage({ data: { kind: "reset", requestId: "replacement" } }); await tick(); reset.loads[0].resolve({}); await tick(); assert.equal(reset.messages.length, 0, "obsolete initialization cannot announce readiness"); reset.loads[1].resolve({}); await tick(); assert.equal(reset.messages.length, 1); assert.equal(reset.messages[0].kind, "reset"); assert.equal(reset.messages[0].requestId, "replacement"); console.log("Assembly worker lifecycle: initialization, failure, reset and stale completion passed");