UNPKG

everything-dev

Version:

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

159 lines (157 loc) • 5.64 kB
import { fetchBosConfigFromFastKv } from "./registry/fastkv.mjs"; import { MISSING_CONFIG_MESSAGE, findConfigPath } from "./resolution/load.mjs"; import { pointerFingerprint } from "./registry/fingerprint.mjs"; import { computeDeployedVersionStatus } from "./version/status.mjs"; import { isPidAlive, readRegistry, unregisterPid } from "./processes/registry.mjs"; import { probePortBindable } from "../infra/ports.mjs"; import { ownerOfPort } from "../infra/port-ownership.mjs"; import { readDevLatestLog, resolveDevLatestFile } from "./dev/logs.mjs"; import { killProcessGroupEscalating, reapGroup } from "./processes/kill.mjs"; import { getStatus } from "./status.mjs"; import { dirname, resolve } from "node:path"; import { Context, Effect, Layer } from "effect"; //#region src/services/ops.ts var OpsService = class OpsService extends Context.Service()("bos/OpsService") { static Live = Layer.succeed(OpsService, OpsService.of({ status: (session) => Effect.tryPromise({ try: async () => { const configPath = findConfigPath(); if (!configPath) return { status: "error", packages: [], envFile: "missing", error: `${MISSING_CONFIG_MESSAGE} in current directory` }; const projectDir = resolve(dirname(configPath)); return { ...await getStatus(projectDir), deployedVersion: await deployedVersionStatus(session) }; }, catch: (error) => ({ status: "error", packages: [], envFile: "missing", error: error instanceof Error ? error.message : "Unknown error" }) }), logs: (input, configDir) => Effect.tryPromise({ try: async () => { const text = await readDevLatestLog(configDir, { tail: input.tail }); const service = input.service; const lines = text.split("\n").filter((line) => line.length > 0).filter((line) => { if (!service) return true; const match = /\] \[([^\]]+)\] \[(?:OUT|ERR)\] /.exec(line); return match?.[1] === service || match?.[1] === `plugin:${service}`; }); return { logFile: resolveDevLatestFile(configDir), lines }; }, catch: (error) => ({ logFile: "unknown", lines: [error instanceof Error ? `Failed to read logs: ${error.message}` : "Failed to read logs"] }) }), kill: (input) => Effect.tryPromise({ try: async () => { const configPath = findConfigPath(); const targetConfigDir = input.all ? void 0 : input.configDir ?? (configPath ? resolve(dirname(configPath)) : void 0); const targets = targetConfigDir ? readRegistry().filter((entry) => entry.configDir === targetConfigDir) : readRegistry(); const killed = []; const skipped = []; for (const entry of targets) { const wasAlive = isPidAlive(entry.pid); if (wasAlive) await Effect.runPromise(killProcessGroupEscalating(entry.pid, { terminateMs: 5e3, signal: input.signal === "SIGKILL" ? "SIGKILL" : "SIGTERM" })); const reapedChildren = []; for (const childPid of entry.childPids ?? []) if (isPidAlive(childPid)) { reapGroup(childPid); reapedChildren.push(childPid); } const ports = Object.values(entry.ports ?? {}).filter((port) => Number.isFinite(port) && port > 0); const stillBound = []; for (const port of ports) { let bindable = false; for (let attempt = 0; attempt < 4; attempt++) { bindable = await Effect.runPromise(probePortBindable(port)); if (bindable) break; await new Promise((resolve) => setTimeout(resolve, 500)); } if (!bindable) stillBound.push(port); } if (stillBound.length > 0) { const owner = await Effect.runPromise(ownerOfPort(stillBound[0])); skipped.push({ pid: entry.pid, reason: `ports still bound after kill: ${stillBound.join(", ")}${owner ? ` — held by pid ${owner.pid} (${owner.command})` : " — owner unknown"}` }); continue; } unregisterPid(entry.pid); killed.push({ pid: entry.pid, configDir: entry.configDir }); if (!wasAlive && reapedChildren.length > 0) skipped.push({ pid: entry.pid, reason: `process already exited; reaped orphaned children ${reapedChildren.join(", ")}` }); } return { status: "killed", killed, skipped }; }, catch: (error) => ({ status: "error", killed: [], skipped: [], error: error instanceof Error ? error.message : "Unknown error" }) }) })); }; async function deployedVersionStatus(session) { const config = session?.config; if (!config?.account || !config.domain) return void 0; const { account, domain } = config; let publishedFingerprint; try { const published = await fetchBosConfigFromFastKv(`bos://${account}/${domain}`); publishedFingerprint = pointerFingerprint(published); } catch { return; } const hostUrl = session?.runtime?.host?.url; if (!hostUrl) return computeDeployedVersionStatus({ publishedFingerprint, servedFingerprint: null }); try { const response = await fetch(new URL("/.well-known/version", hostUrl)); if (!response.ok) return computeDeployedVersionStatus({ publishedFingerprint, servedFingerprint: null, servedError: `version endpoint returned ${response.status}` }); const body = await response.json(); return computeDeployedVersionStatus({ publishedFingerprint, servedFingerprint: body.fingerprint ?? null }); } catch (error) { return computeDeployedVersionStatus({ publishedFingerprint, servedFingerprint: null, servedError: error instanceof Error ? error.message : String(error) }); } } //#endregion export { OpsService }; //# sourceMappingURL=ops.mjs.map