everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
146 lines (144 loc) • 5.12 kB
JavaScript
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