import path from "path"; import fs from "fs"; import os from "os"; import { spawn } from "child_process"; import { QuickJSWorker } from "../dist"; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); describe("Production timer compatibility", () => { it("keeps the process alive for timer-driven work after eval resolves", async () => { const markerFile = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "qjs-timer-")), "markers.log"); const script = ` const fs = require("fs"); const { QuickJSWorker } = require(${JSON.stringify(path.join(__dirname, "../dist"))}); const worker = new QuickJSWorker(); worker.on("message", (msg) => { if (msg === "timer-fired") { fs.appendFileSync(${JSON.stringify(markerFile)}, "TIMER_OK\\n"); worker.close().catch((error) => { fs.appendFileSync(${JSON.stringify(markerFile)}, "ERR:" + String(error && error.stack || error) + "\\n"); process.exitCode = 1; }); } }); worker.eval('setTimeout(() => postMessage("timer-fired"), 25); "scheduled";') .then(() => fs.appendFileSync(${JSON.stringify(markerFile)}, "EVAL_OK\\n")) .catch((error) => { fs.appendFileSync(${JSON.stringify(markerFile)}, "ERR:" + String(error && error.stack || error) + "\\n"); process.exitCode = 1; }); `; const child = spawn(process.execPath, ["-e", script], { cwd: path.join(__dirname, ".."), stdio: ["ignore", "ignore", "ignore"], }); const exitCode = await new Promise((resolve, reject) => { const timeout = setTimeout(() => { child.kill("SIGKILL"); reject(new Error("child timed out")); }, 1500); child.once("error", (error) => { clearTimeout(timeout); reject(error); }); child.once("exit", (code) => { clearTimeout(timeout); resolve(code ?? 0); }); }); const markers = fs.existsSync(markerFile) ? fs.readFileSync(markerFile, "utf8") : ""; expect(exitCode).toBe(0); expect(markers).toContain("EVAL_OK"); expect(markers).toContain("TIMER_OK"); expect(markers).not.toContain("ERR:"); }, 20_000); it("cancels pending timers on close", async () => { const worker = new QuickJSWorker(); const seen: string[] = []; worker.on("message", (msg) => seen.push(String(msg))); await expect(worker.eval(` setTimeout(() => postMessage("late"), 40); setInterval(() => postMessage("interval"), 5); "scheduled" `)).resolves.toBe("scheduled"); await worker.close(); await sleep(80); expect(seen).toEqual([]); }, 20_000); });