/** * Local stack: Next.js web + IMAP worker in one process group. * Usage: pnpm dev / pnpm dev:stack */ import { spawn } from "node:child_process"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const children = []; function start(label, args) { const child = spawn("pnpm", args, { cwd: root, stdio: "inherit", shell: true, env: process.env, }); child.on("exit", (code, signal) => { console.error(`[dev-stack] ${label} exited code=${code} signal=${signal}`); shutdown(code ?? 1); }); children.push(child); return child; } let shuttingDown = false; function shutdown(code = 0) { if (shuttingDown) return; shuttingDown = true; for (const child of children) { if (!child.killed) { try { child.kill("SIGTERM"); } catch { /* ignore */ } } } // Give children a moment, then force-exit so orphaned cmd.exe on Windows dies with parent intent setTimeout(() => process.exit(code), 500).unref(); } process.on("SIGINT", () => shutdown(0)); process.on("SIGTERM", () => shutdown(0)); console.log("[dev-stack] starting web (next) + worker …"); // Use dev:web so this is never recursive with package.json "dev" = this script start("web", ["run", "dev:web"]); start("worker", ["run", "worker"]);