import { QuickJSWorker } from "../dist"; describe("Module API", () => { let worker: QuickJSWorker; beforeEach(() => { worker = new QuickJSWorker(); }); afterEach(async () => { if (worker && !worker.isClosed()) await worker.close(); }); it("supports module.register + module.import", async () => { await worker.module.register("app:math", ` export const value = 21; export function double(n) { return n * 2; } `); const math = await worker.module.import<{ value: number; double(n: number): Promise }>("app:math"); expect(math.value).toBe(21); await expect(math.double(21)).resolves.toBe(42); }); it("supports module.eval without moduleReturn", async () => { const mod = await worker.module.eval<{ name: string; upper(v: string): Promise; }>(` export const name = "quickjs"; export function upper(v) { return String(v).toUpperCase(); } `); expect(mod.name).toBe("quickjs"); await expect(mod.upper("hello")).resolves.toBe("HELLO"); }); it("supports cjs wrapping for module.eval", async () => { const mod = await worker.module.eval<{ default: { answer: number } }>( ` module.exports.answer = 42; `, { cjs: true }, ); expect(mod.default.answer).toBe(42); }); it("supports top-level await in module.eval", async () => { const mod = await worker.module.eval<{ out: number }>(` const v = await Promise.resolve(42); export const out = v; `); expect(mod.out).toBe(42); }); it("supports module.clear and pinned module names", async () => { const pinned = await worker.module.eval<{ out: number }>("export const out = 77;", { moduleName: "named:pin", }); expect(pinned.out).toBe(77); await expect(worker.module.import<{ out: number }>("named:pin")).resolves.toMatchObject({ out: 77 }); await worker.module.register("named:clearable", "export const ok = 1;"); await expect(worker.module.clear("named:clearable")).resolves.toBe(true); await expect(worker.module.import("named:clearable")).rejects.toBeDefined(); }); it("supports constructor-provided modules as objects and maps", async () => { await worker.close(); const objectWorker = new QuickJSWorker({ modules: { "named:startup": "export const boot = 1;", }, }); try { await expect(objectWorker.module.import<{ boot: number }>("named:startup")).resolves.toMatchObject({ boot: 1 }); } finally { await objectWorker.close(); } const mapWorker = new QuickJSWorker({ modules: new Map([ ["intent_bootstrap", `export const boot = "ok";`], ]), }); try { await expect(mapWorker.module.import<{ boot: string }>("intent_bootstrap")).resolves.toMatchObject({ boot: "ok" }); } finally { await mapWorker.close(); } worker = new QuickJSWorker(); }); it("allows declared modules while imports:false blocks everything else", async () => { await worker.close(); worker = new QuickJSWorker({ imports: false, modules: { "named:only": "export const ok = 1;", }, }); await expect(worker.module.import<{ ok: number }>("named:only")).resolves.toMatchObject({ ok: 1 }); await expect(worker.module.import("named:missing")).rejects.toBeDefined(); await expect(worker.module.eval<{ out: number }>(` import { ok } from "named:only"; export const out = ok; `)).resolves.toMatchObject({ out: 1 }); await expect(worker.module.eval(` import "./not-allowed.js"; export const out = 0; `)).rejects.toBeDefined(); }); });