everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 3.99 kB
Source Map (JSON)
{"version":3,"file":"port-ownership.cjs","names":["Effect"],"sources":["../../src/infra/port-ownership.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { Effect } from \"effect\";\n\nexport interface PortOwner {\n pid: number;\n command: string;\n}\n\nexport interface PortOwnershipOptions {\n lsofPath?: string;\n psPath?: string;\n}\n\nlet lsofCached: boolean | null = null;\n\nconst execFileText = (path: string, args: string[]): Promise<string> =>\n new Promise((resolve, reject) => {\n execFile(path, args, { timeout: 5000 }, (error, stdout) => {\n if (error && !stdout) reject(error);\n else resolve(stdout);\n });\n });\n\nconst isValidPort = (port: number): boolean => Number.isInteger(port) && port > 0 && port <= 65535;\n\n// ownerOfPort identifies the process listening on a port via lsof + ps.\n// Returns null when the port is free or when lsof is unavailable (degraded:\n// callers report \"unknown owner\" rather than failing).\nexport const ownerOfPort = (\n port: number,\n options?: PortOwnershipOptions,\n): Effect.Effect<PortOwner | null> =>\n Effect.callback((resume) => {\n void (async () => {\n if (!isValidPort(port)) {\n resume(Effect.succeed(null));\n return;\n }\n const lsof = options?.lsofPath ?? \"lsof\";\n try {\n const stdout = await execFileText(lsof, [\"-ti\", `:${port}`, \"-sTCP:LISTEN\"]);\n const pid = Number.parseInt(stdout.trim().split(\"\\n\")[0] ?? \"\", 10);\n if (!Number.isInteger(pid) || pid <= 0) {\n resume(Effect.succeed(null));\n return;\n }\n try {\n const psOut = await execFileText(options?.psPath ?? \"ps\", [\n \"-o\",\n \"command=\",\n \"-p\",\n String(pid),\n ]);\n resume(Effect.succeed({ pid, command: psOut.trim() || \"unknown\" }));\n } catch {\n resume(Effect.succeed({ pid, command: \"unknown\" }));\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") lsofCached = false;\n resume(Effect.succeed(null));\n }\n })();\n });\n\n// isLsofAvailable probes (once per process) whether the lsof binary exists —\n// ownership reporting degrades gracefully when it does not.\nexport const isLsofAvailable = (options?: PortOwnershipOptions): Effect.Effect<boolean> =>\n Effect.callback((resume) => {\n if (lsofCached !== null) {\n resume(Effect.succeed(lsofCached));\n return;\n }\n void (async () => {\n try {\n await execFileText(options?.lsofPath ?? \"lsof\", [\"-v\"]);\n lsofCached = true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") lsofCached = false;\n else lsofCached = true;\n }\n resume(Effect.succeed(lsofCached));\n })();\n });\n\nexport const resetLsofCacheForTests = (): void => {\n lsofCached = null;\n};\n"],"mappings":";;;;AAaA,IAAI,aAA6B;AAEjC,MAAM,gBAAgB,MAAc,SAClC,IAAI,SAAS,SAAS,WAAW;CAC/B,iCAAS,MAAM,MAAM,EAAE,SAAS,IAAK,IAAI,OAAO,WAAW;EACzD,IAAI,SAAS,CAAC,QAAQ,OAAO,KAAK;OAC7B,QAAQ,MAAM;CACrB,CAAC;AACH,CAAC;AAEH,MAAM,eAAe,SAA0B,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,QAAQ;AAK7F,MAAa,eACX,MACA,YAEAA,cAAO,UAAU,WAAW;CAC1B,CAAM,YAAY;EAChB,IAAI,CAAC,YAAY,IAAI,GAAG;GACtB,OAAOA,cAAO,QAAQ,IAAI,CAAC;GAC3B;EACF;EACA,MAAM,OAAO,SAAS,YAAY;EAClC,IAAI;GACF,MAAM,SAAS,MAAM,aAAa,MAAM;IAAC;IAAO,IAAI;IAAQ;GAAc,CAAC;GAC3E,MAAM,MAAM,OAAO,SAAS,OAAO,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE;GAClE,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,GAAG;IACtC,OAAOA,cAAO,QAAQ,IAAI,CAAC;IAC3B;GACF;GACA,IAAI;IACF,MAAM,QAAQ,MAAM,aAAa,SAAS,UAAU,MAAM;KACxD;KACA;KACA;KACA,OAAO,GAAG;IACZ,CAAC;IACD,OAAOA,cAAO,QAAQ;KAAE;KAAK,SAAS,MAAM,KAAK,KAAK;IAAU,CAAC,CAAC;GACpE,QAAQ;IACN,OAAOA,cAAO,QAAQ;KAAE;KAAK,SAAS;IAAU,CAAC,CAAC;GACpD;EACF,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,aAAa;GACrE,OAAOA,cAAO,QAAQ,IAAI,CAAC;EAC7B;CACF,EAAC,CAAE;AACL,CAAC"}