import { spawn } from "node:child_process"; import { existsSync } from "node:fs"; import { validateRuntime } from "./runtime-config.mjs"; if (existsSync(".env.local")) process.loadEnvFile(".env.local"); const dev = process.argv[2] === "dev"; const { workerHeap } = validateRuntime(process.env, { production: !dev }); const children = []; let stopping = false; function run(args) { const c = spawn(process.execPath, args, { stdio: "inherit", env: process.env, windowsHide: true, }); children.push(c); c.on("error", (error) => { if (!stopping) { console.error(`Unable to start MineGeo process: ${error.message}`); stop(1); } }); c.on("exit", (code, signal) => { if (!stopping) { const reason = code === null ? `signal ${signal ?? "unknown"}` : `code ${code}`; console.error( `MineGeo process exited (${reason}). Shutting down companion process.`, ); stop(code === 0 ? 1 : (code ?? 1)); } }); return c; } function stop(code = 0) { if (stopping) return; stopping = true; const running = (child) => child.exitCode === null && child.signalCode === null; for (const c of children) { if (running(c)) c.kill("SIGTERM"); } const forceStop = setTimeout(() => { for (const c of children) { if (running(c)) c.kill("SIGKILL"); } process.exit(code); }, 8_000); Promise.all( children.map( (c) => new Promise((resolve) => { if (!running(c)) resolve(); else c.once("close", resolve); }), ), ).then(() => { clearTimeout(forceStop); process.exitCode = code; }); } process.on("SIGINT", () => stop()); process.on("SIGTERM", () => stop()); run([ "node_modules/next/dist/bin/next", dev ? "dev" : "start", "--hostname", process.env.MINEGEO_HOST || "127.0.0.1", ]); run([ `--max-old-space-size=${workerHeap}`, "--import", "tsx", "worker/main.ts", ]); if (!dev && process.env.MINEGEO_RUN_BOTS === "true") { run(["scripts/moderator-bots.mjs"]); }