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