UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

146 lines (144 loc) • 5.12 kB
let node_fs = require("node:fs"); let node_path = require("node:path"); let effect = require("effect"); let node_os = require("node:os"); let node_fs_promises = require("node:fs/promises"); let node_child_process = require("node:child_process"); //#region src/process-registry.ts function getRegistryDir() { return (0, node_path.join)((0, node_os.homedir)(), ".cache", "everything-dev"); } function ensureRegistryDir() { const path = getRegistryPath(); const dir = (0, node_path.join)(path, ".."); if (!(0, node_fs.existsSync)(dir)) (0, node_fs.mkdirSync)(dir, { recursive: true }); } function readRegistry() { const path = getRegistryPath(); if (!(0, node_fs.existsSync)(path)) return []; try { const raw = JSON.parse((0, node_fs.readFileSync)(path, "utf-8")); if (!Array.isArray(raw)) return []; return raw.filter((entry) => entry && typeof entry === "object" && typeof entry.pid === "number" && typeof entry.configDir === "string" && typeof entry.role === "string"); } catch { return []; } } function isPidAlive(pid) { try { process.kill(pid, 0); return true; } catch (err) { return err.code !== "ESRCH"; } } /** * Identity of a process generation, stable across PID reuse: Linux reads the * kernel's starttime tick from /proc, macOS falls back to `ps -o lstart=`. * Null when the generation cannot be determined (the caller then treats the * claim as unverifiable and keeps legacy behavior). */ function processStartTime(pid) { if (pid <= 1) return null; try { const stat = (0, node_fs.readFileSync)(`/proc/${pid}/stat`, "utf-8"); const close = stat.lastIndexOf(")"); const starttime = stat.slice(close + 2).split(" ")[19]; return starttime ? `proc:${starttime}` : null; } catch { try { const text = (0, node_child_process.spawnSync)("ps", [ "-o", "lstart=", "-p", String(pid) ], { timeout: 5e3 }).stdout?.toString().trim(); return text ? `ps:${text}` : null; } catch { return null; } } } function isEntryLive(entry) { if (entry.pid <= 1) return false; if (!(0, node_fs.existsSync)(entry.configDir)) return false; if (!isPidAlive(entry.pid)) return false; if (entry.processStart !== void 0) return processStartTime(entry.pid) === entry.processStart; return true; } function pruneDead(entries) { return entries.filter(isEntryLive); } function pruneDeadEffect(entries) { return effect.Effect.forEach(entries, (entry) => { if (entry.pid <= 1) return effect.Effect.succeed(null); return effect.Effect.gen(function* () { if (!(yield* effect.Effect.promise(() => (0, node_fs_promises.access)(entry.configDir).then(() => true).catch(() => false)))) return null; if (!isPidAlive(entry.pid)) return null; if (entry.processStart !== void 0) { if ((yield* effect.Effect.promise(() => Promise.resolve(processStartTime(entry.pid)))) !== entry.processStart) return null; } return entry; }); }, { concurrency: "unbounded" }).pipe(effect.Effect.map((results) => results.filter((e) => e !== null))); } function writeRegistry(entries) { ensureRegistryDir(); const path = getRegistryPath(); const tmpPath = `${path}.tmp`; (0, node_fs.writeFileSync)(tmpPath, `${JSON.stringify(entries, null, 2)}\n`); (0, node_fs.renameSync)(tmpPath, path); } function registerStandalone(entry) { const live = pruneDead(readRegistry()); const full = { ...entry, role: "standalone", processStart: processStartTime(entry.pid) ?? entry.processStart }; const withoutSelf = live.filter((existing) => existing.pid !== entry.pid); withoutSelf.push(full); writeRegistry(withoutSelf); return full; } function updateChildPids(pid, childPids) { const live = pruneDead(readRegistry()); const entry = live.find((existing) => existing.pid === pid); if (!entry) return; entry.childPids = childPids; writeRegistry(live); } function unregisterPid(pid) { const entries = readRegistry(); const next = pruneDead(entries).filter((entry) => entry.pid !== pid); if (next.length === entries.length) return; writeRegistry(next); } function getRegistryPath() { return process.env.BO_PID_REGISTRY_PATH ?? (0, node_path.join)(getRegistryDir(), "pids.json"); } function claimedPorts() { return new Set(claimedPortOwners().keys()); } /** Port → the live registry entry claiming it, for occupancy diagnostics. */ function claimedPortOwners() { const live = pruneDead(readRegistry()); const out = /* @__PURE__ */ new Map(); for (const entry of live) for (const port of Object.values(entry.ports)) if (typeof port === "number") out.set(port, entry); return out; } //#endregion exports.claimedPortOwners = claimedPortOwners; exports.claimedPorts = claimedPorts; exports.getRegistryPath = getRegistryPath; exports.isEntryLive = isEntryLive; exports.isPidAlive = isPidAlive; exports.processStartTime = processStartTime; exports.pruneDead = pruneDead; exports.pruneDeadEffect = pruneDeadEffect; exports.readRegistry = readRegistry; exports.registerStandalone = registerStandalone; exports.unregisterPid = unregisterPid; exports.updateChildPids = updateChildPids; exports.writeRegistry = writeRegistry; //# sourceMappingURL=process-registry.cjs.map