everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 6.16 kB
Source Map (JSON)
{"version":3,"file":"process-registry.mjs","names":[],"sources":["../src/process-registry.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { access } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Effect } from \"effect\";\n\nexport type ProcessRole = \"standalone\" | \"workspace-parent\" | \"workspace-child\";\n\nexport interface PidEntry {\n pid: number;\n configDir: string;\n parentPid?: number;\n role: ProcessRole;\n ports: Record<string, number>;\n budget?: { min: number; max: number };\n startedAt: number;\n description: string;\n}\n\nfunction getRegistryDir(): string {\n return join(homedir(), \".cache\", \"everything-dev\");\n}\n\nfunction ensureRegistryDir(): void {\n const path = getRegistryPath();\n const dir = join(path, \"..\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\nexport function readRegistry(): PidEntry[] {\n const path = getRegistryPath();\n if (!existsSync(path)) return [];\n try {\n const raw = JSON.parse(readFileSync(path, \"utf-8\"));\n if (!Array.isArray(raw)) return [];\n return raw.filter(\n (entry): entry is PidEntry =>\n entry &&\n typeof entry === \"object\" &&\n typeof entry.pid === \"number\" &&\n typeof entry.configDir === \"string\" &&\n typeof entry.role === \"string\",\n );\n } catch {\n return [];\n }\n}\n\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code !== \"ESRCH\";\n }\n}\n\nexport function pruneDead(entries: PidEntry[]): PidEntry[] {\n return entries.filter(\n (entry) => entry.pid > 1 && existsSync(entry.configDir) && isPidAlive(entry.pid),\n );\n}\n\nexport function pruneDeadEffect(entries: PidEntry[]): Effect.Effect<PidEntry[]> {\n return Effect.forEach(\n entries,\n (entry) => {\n if (entry.pid <= 1) return Effect.succeed(null);\n return Effect.gen(function* () {\n const dirExists = yield* Effect.promise(() =>\n access(entry.configDir)\n .then(() => true)\n .catch(() => false),\n );\n if (!dirExists) return null;\n if (!isPidAlive(entry.pid)) return null;\n return entry;\n });\n },\n { concurrency: \"unbounded\" },\n ).pipe(Effect.map((results) => results.filter((e): e is PidEntry => e !== null)));\n}\n\nexport function writeRegistry(entries: PidEntry[]): void {\n ensureRegistryDir();\n const path = getRegistryPath();\n const tmpPath = `${path}.tmp`;\n writeFileSync(tmpPath, `${JSON.stringify(entries, null, 2)}\\n`);\n renameSync(tmpPath, path);\n}\n\nexport function registerStandalone(entry: Omit<PidEntry, \"role\">): PidEntry {\n const live = pruneDead(readRegistry());\n const full: PidEntry = { ...entry, role: \"standalone\" };\n const withoutSelf = live.filter((existing) => existing.pid !== entry.pid);\n withoutSelf.push(full);\n writeRegistry(withoutSelf);\n return full;\n}\n\nexport function registerEntry(entry: PidEntry): void {\n const live = pruneDead(readRegistry());\n const withoutSelf = live.filter((existing) => existing.pid !== entry.pid);\n withoutSelf.push(entry);\n writeRegistry(withoutSelf);\n}\n\nexport function unregisterPid(pid: number): void {\n const live = pruneDead(readRegistry());\n const next = live.filter((entry) => entry.pid !== pid);\n if (next.length === live.length) return;\n writeRegistry(next);\n}\n\nexport function removeRegistryFile(): void {\n const path = getRegistryPath();\n if (existsSync(path)) {\n rmSync(path, { force: true });\n }\n}\n\nexport function getRegistryPath(): string {\n return process.env.BO_PID_REGISTRY_PATH ?? join(getRegistryDir(), \"pids.json\");\n}\n\nexport function claimedPorts(): Set<number> {\n const live = pruneDead(readRegistry());\n const out = new Set<number>();\n for (const entry of live) {\n for (const port of Object.values(entry.ports)) {\n if (typeof port === \"number\") out.add(port);\n }\n }\n return out;\n}\n"],"mappings":";;;;;;;AAmBA,SAAS,iBAAyB;CAChC,OAAO,KAAK,QAAQ,GAAG,UAAU,gBAAgB;AACnD;AAEA,SAAS,oBAA0B;CAEjC,MAAM,MAAM,KADC,gBACO,GAAG,IAAI;CAC3B,IAAI,CAAC,WAAW,GAAG,GACjB,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAEtC;AAEA,SAAgB,eAA2B;CACzC,MAAM,OAAO,gBAAgB;CAC7B,IAAI,CAAC,WAAW,IAAI,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,MAAM,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;EAClD,IAAI,CAAC,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC;EACjC,OAAO,IAAI,QACR,UACC,SACA,OAAO,UAAU,YACjB,OAAO,MAAM,QAAQ,YACrB,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,SAAS,QAC1B;CACF,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,WAAW,KAAsB;CAC/C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,KAAK;EACZ,OAAQ,IAA8B,SAAS;CACjD;AACF;AAEA,SAAgB,UAAU,SAAiC;CACzD,OAAO,QAAQ,QACZ,UAAU,MAAM,MAAM,KAAK,WAAW,MAAM,SAAS,KAAK,WAAW,MAAM,GAAG,CACjF;AACF;AAEA,SAAgB,gBAAgB,SAAgD;CAC9E,OAAO,OAAO,QACZ,UACC,UAAU;EACT,IAAI,MAAM,OAAO,GAAG,OAAO,OAAO,QAAQ,IAAI;EAC9C,OAAO,OAAO,IAAI,aAAa;GAM7B,IAAI,EAAC,OALoB,OAAO,cAC9B,OAAO,MAAM,SAAS,CAAC,CACpB,WAAW,IAAI,CAAC,CAChB,YAAY,KAAK,CACtB,IACgB,OAAO;GACvB,IAAI,CAAC,WAAW,MAAM,GAAG,GAAG,OAAO;GACnC,OAAO;EACT,CAAC;CACH,GACA,EAAE,aAAa,YAAY,CAC7B,CAAC,CAAC,KAAK,OAAO,KAAK,YAAY,QAAQ,QAAQ,MAAqB,MAAM,IAAI,CAAC,CAAC;AAClF;AAEA,SAAgB,cAAc,SAA2B;CACvD,kBAAkB;CAClB,MAAM,OAAO,gBAAgB;CAC7B,MAAM,UAAU,GAAG,KAAK;CACxB,cAAc,SAAS,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,GAAG;CAC9D,WAAW,SAAS,IAAI;AAC1B;AAEA,SAAgB,mBAAmB,OAAyC;CAC1E,MAAM,OAAO,UAAU,aAAa,CAAC;CACrC,MAAM,OAAiB;EAAE,GAAG;EAAO,MAAM;CAAa;CACtD,MAAM,cAAc,KAAK,QAAQ,aAAa,SAAS,QAAQ,MAAM,GAAG;CACxE,YAAY,KAAK,IAAI;CACrB,cAAc,WAAW;CACzB,OAAO;AACT;AASA,SAAgB,cAAc,KAAmB;CAC/C,MAAM,OAAO,UAAU,aAAa,CAAC;CACrC,MAAM,OAAO,KAAK,QAAQ,UAAU,MAAM,QAAQ,GAAG;CACrD,IAAI,KAAK,WAAW,KAAK,QAAQ;CACjC,cAAc,IAAI;AACpB;AASA,SAAgB,kBAA0B;CACxC,OAAO,QAAQ,IAAI,wBAAwB,KAAK,eAAe,GAAG,WAAW;AAC/E;AAEA,SAAgB,eAA4B;CAC1C,MAAM,OAAO,UAAU,aAAa,CAAC;CACrC,MAAM,sBAAM,IAAI,IAAY;CAC5B,KAAK,MAAM,SAAS,MAClB,KAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,KAAK,GAC1C,IAAI,OAAO,SAAS,UAAU,IAAI,IAAI,IAAI;CAG9C,OAAO;AACT"}