Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061// Start a demo/apps/<name> app under real Node, the oracle the golden scripts record:// its package.json `main`, else server.cjs/server.mjs/server.js, runs with// NODE_ENV=production and its public/ mtimes pinned first (scripts/fixed-mtime.mjs).import { spawn } from "node:child_process";import { connect } from "node:net";import { existsSync, readFileSync } from "node:fs";import { join } from "node:path";import { pinMtimes } from "./fixed-mtime.mjs";import { oracleEnv } from "./pinned-node.mjs";
function entryOf(dir) { const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")); const candidates = [pkg.main, "server.cjs", "server.mjs", "server.js"].filter(Boolean); const found = candidates.find((c) => existsSync(join(dir, c))); if (!found) throw new Error(`no entry in ${dir} (tried ${candidates.join(", ")})`); return found;}
/** Whether something already accepts connections on `port`. */function taken(port) { return new Promise((resolve) => { const s = connect(port, "127.0.0.1"); s.once("connect", () => (s.destroy(), resolve(true))); s.once("error", () => resolve(false)); });}
/** Resolves once the app answers on `port`, run by the `node` binary. `stop()` kills it * (if still running) and resolves with its stderr, the app's directory replaced by /app * as the kernel mounts it. Refuses a port something else holds: the readiness probe * would take that server's answer for the app's. */export async function startApp(appDir, port, node = process.execPath) { if (await taken(port)) throw new Error(`port ${port} is already in use; the app needs it free to record`); pinMtimes(join(appDir, "public")); const app = spawn(node, [entryOf(appDir)], { cwd: appDir, env: oracleEnv({ NODE_ENV: "production" }), stdio: ["ignore", "pipe", "pipe"], }); let stderr = ""; app.stderr.setEncoding("utf8").on("data", (c) => (stderr += c)); const exited = new Promise((r) => app.once("exit", r)); const stop = async () => { if (app.exitCode === null && app.signalCode === null) app.kill(); await exited; return stderr.replaceAll(appDir, "/app"); }; const deadline = Date.now() + 10_000; while (Date.now() < deadline) { if (app.exitCode !== null) throw new Error(`app exited with ${app.exitCode} before listening:\n${stderr}`); try { await fetch(`http://127.0.0.1:${port}/`, { method: "HEAD" }); return { stop }; } catch { await new Promise((r) => setTimeout(r, 50)); } } await stop(); throw new Error(`nothing listened on ${port} within 10s`);}