everything-dev 2.0.0-rc.5 → 2.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle-fs-resolve.cjs +37 -3
- package/dist/bundle-fs-resolve.cjs.map +1 -1
- package/dist/bundle-fs-resolve.mjs +37 -3
- package/dist/bundle-fs-resolve.mjs.map +1 -1
- package/dist/cli/infra.cjs +4 -2
- package/dist/cli/infra.cjs.map +1 -1
- package/dist/cli/infra.mjs +4 -2
- package/dist/cli/infra.mjs.map +1 -1
- package/dist/dev-program.cjs +1 -2
- package/dist/dev-program.cjs.map +1 -1
- package/dist/dev-program.mjs +1 -2
- package/dist/dev-program.mjs.map +1 -1
- package/dist/orchestrator.cjs +22 -18
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.mjs +22 -18
- package/dist/orchestrator.mjs.map +1 -1
- package/package.json +1 -1
package/dist/orchestrator.mjs
CHANGED
|
@@ -7,9 +7,6 @@ import { spawn } from "node:child_process";
|
|
|
7
7
|
import { Readable } from "node:stream";
|
|
8
8
|
|
|
9
9
|
//#region src/orchestrator.ts
|
|
10
|
-
const warnOutsideEffect = (...args) => {
|
|
11
|
-
console.warn(...args);
|
|
12
|
-
};
|
|
13
10
|
const runFetch = (url, init) => fetch(url, init);
|
|
14
11
|
process.on("unhandledRejection", (reason) => {
|
|
15
12
|
console.error("[Orchestrator] Unhandled rejection:", reason);
|
|
@@ -158,27 +155,34 @@ const spawnRemoteHost = (descriptor, callbacks) => Effect.gen(function* () {
|
|
|
158
155
|
mfCore.setGlobalFederationInstance(mf);
|
|
159
156
|
}
|
|
160
157
|
patchManifestFetchForSsrPublicPath(mf);
|
|
161
|
-
const
|
|
162
|
-
const remoteEntryUrl = `${baseUrl}/remoteEntry.js`;
|
|
163
|
-
const manifestUrl = `${baseUrl}/mf-manifest.json`;
|
|
158
|
+
const manifestUrl = `${remoteUrl.replace(/\/remoteEntry\.js$/, "").replace(/\/mf-manifest\.json$/, "").replace(/\/$/, "")}/mf-manifest.json`;
|
|
164
159
|
const entryUrl = yield* Effect.tryPromise({
|
|
165
160
|
try: async () => {
|
|
161
|
+
let res;
|
|
166
162
|
try {
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
res = await runFetch(manifestUrl, { signal: AbortSignal.timeout(1e4) });
|
|
170
|
-
} catch {
|
|
171
|
-
throw new Error("manifest fetch failed");
|
|
172
|
-
}
|
|
173
|
-
if (!res.ok) return remoteEntryUrl;
|
|
174
|
-
const json = await res.json();
|
|
175
|
-
if (json && typeof json === "object" && "metaData" in json && "exposes" in json && "shared" in json) return manifestUrl;
|
|
163
|
+
res = await runFetch(manifestUrl, { signal: AbortSignal.timeout(1e4) });
|
|
176
164
|
} catch (e) {
|
|
177
|
-
|
|
165
|
+
throw new OrchestratorError({
|
|
166
|
+
operation: "fetch host manifest",
|
|
167
|
+
detail: `${manifestUrl} — ${e instanceof Error ? e.message : String(e)}`,
|
|
168
|
+
cause: e
|
|
169
|
+
});
|
|
178
170
|
}
|
|
179
|
-
|
|
171
|
+
if (!res.ok) throw new OrchestratorError({
|
|
172
|
+
operation: "fetch host manifest",
|
|
173
|
+
detail: `${manifestUrl} — HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : ""}`
|
|
174
|
+
});
|
|
175
|
+
const json = await res.json();
|
|
176
|
+
if (!json || typeof json !== "object" || !("metaData" in json) || !("exposes" in json) || !("shared" in json)) throw new OrchestratorError({
|
|
177
|
+
operation: "fetch host manifest",
|
|
178
|
+
detail: `${manifestUrl} — not a Module Federation manifest (missing metaData/exposes/shared)`
|
|
179
|
+
});
|
|
180
|
+
return manifestUrl;
|
|
180
181
|
},
|
|
181
|
-
catch: () =>
|
|
182
|
+
catch: (cause) => cause instanceof OrchestratorError ? cause : new OrchestratorError({
|
|
183
|
+
operation: "fetch host manifest",
|
|
184
|
+
detail: `${manifestUrl} — ${String(cause)}`
|
|
185
|
+
})
|
|
182
186
|
});
|
|
183
187
|
mf.registerRemotes([{
|
|
184
188
|
name: "host",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.mjs","names":[],"sources":["../src/orchestrator.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Readable } from \"node:stream\";\nimport { Data, Deferred, Effect, Option, Ref, Schedule, Schema, Stream } from \"effect\";\nimport { stripAnsi } from \"./dev-log-pipeline\";\nimport { ShellEnv } from \"./env/project-env\";\nimport { patchManifestFetchForSsrPublicPath } from \"./mf\";\nimport {\n DevGeneratedEnv,\n DevRuntimeConfig,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\n\nconst warnOutsideEffect = (...args: unknown[]): void => {\n console.warn(...args);\n};\n\nconst runFetch = (url: string, init?: RequestInit): Promise<Response> => fetch(url, init);\n\nprocess.on(\"unhandledRejection\", (reason) => {\n console.error(\"[Orchestrator] Unhandled rejection:\", reason);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n console.error(\"[Orchestrator] Uncaught exception:\", err);\n});\n\nexport interface ProcessCallbacks {\n onStatus: (name: string, status: ProcessStatus, message?: string) => void;\n onLog: (name: string, line: string, isError?: boolean) => void;\n}\n\nexport interface ProcessHandle {\n name: string;\n pid: number | undefined;\n kill: Effect.Effect<void>;\n waitForReady: Effect.Effect<void, OrchestratorError>;\n waitForExit: Effect.Effect<number, OrchestratorError>;\n}\n\nexport class OrchestratorError extends Data.TaggedError(\"OrchestratorError\")<{\n readonly operation: string;\n readonly detail: string;\n readonly cause?: unknown;\n}> {\n override get message() {\n return `${this.operation}: ${this.detail}`;\n }\n}\n\nconst omitUndefined = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n return value.map((item) => (item === undefined ? null : omitUndefined(item)));\n }\n if (value !== null && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, item]) => item !== undefined)\n .map(([key, item]) => [key, omitUndefined(item)]),\n );\n }\n return value;\n};\n\nexport const encodeRuntimeConfig = (runtimeConfig: unknown) =>\n Schema.encodeUnknownEffect(Schema.fromJsonString(Schema.Json))(omitUndefined(runtimeConfig)).pipe(\n Effect.mapError(\n (cause) =>\n new OrchestratorError({\n operation: \"serialize runtime config\",\n detail: cause.message,\n cause,\n }),\n ),\n );\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: \"local\" | \"remote\";\n uiPort?: number;\n}\n\nconst probeHttpOk = (url: string, timeoutMs = 400) =>\n Effect.tryPromise({\n try: async () => {\n try {\n const res = await runFetch(url, { signal: AbortSignal.timeout(timeoutMs) });\n return res.ok;\n } catch {\n return false;\n }\n },\n catch: () => false,\n });\n\nconst LOCAL_PROBE_DEADLINE_MS = 90_000;\nconst LOCAL_PROBE_INTERVAL_MS = 200;\n\nconst cleanExitSignals: Record<string, true> = { SIGTERM: true, SIGINT: true };\n\nconst REMOTE_PROBE_TIMEOUT_MS = 5000;\nconst REMOTE_PROBE_DEADLINE_MS = 60_000;\nconst REMOTE_PROBE_BACKOFF_INITIAL_MS = 1000;\nconst REMOTE_PROBE_BACKOFF_MAX_MS = 15_000;\n\nexport const detectStatus = (\n line: string,\n descriptor: ServiceDescriptor,\n): { status: ProcessStatus; isError: boolean } | null => {\n const cleanLine = stripAnsi(line);\n const errorPatterns = descriptor.errorPatterns ?? [];\n const readyPatterns = descriptor.readyPatterns ?? [];\n for (const pattern of readyPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"ready\", isError: false };\n }\n }\n for (const pattern of errorPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"error\", isError: true };\n }\n }\n return null;\n};\n\ninterface ServerHandle {\n ready: Promise<void>;\n shutdown: () => Promise<void>;\n}\n\ninterface ServerInput {\n config: RuntimeConfig;\n port?: number;\n env?: Record<string, string>;\n}\n\nconst patchConsole = (name: string, callbacks: ProcessCallbacks): (() => void) => {\n const originalLog = console.log;\n const originalError = console.error;\n const originalWarn = console.warn;\n const originalInfo = console.info;\n\n const formatArgs = (args: unknown[], isError = false): string => {\n return args\n .map((arg) => {\n if (arg instanceof Error) {\n const parts = [`${arg.name}: ${arg.message}`];\n if (arg.cause instanceof Error)\n parts.push(`(cause: ${arg.cause.name}: ${arg.cause.message})`);\n else if (arg.cause) {\n const cause = arg.cause;\n parts.push(\n `(cause: ${cause instanceof Error ? cause.message : typeof cause === \"string\" ? cause : (JSON.stringify(cause) ?? \"[Unserializable cause]\")})`,\n );\n }\n if (isError && arg.stack) parts.push(arg.stack);\n return parts.join(\"\\n\");\n }\n if (arg === null) return \"null\";\n if (typeof arg === \"string\") return arg;\n if (typeof arg === \"number\" || typeof arg === \"bigint\" || typeof arg === \"boolean\")\n return `${arg}`;\n if (typeof arg === \"symbol\") return arg.description ?? \"\";\n if (typeof arg === \"undefined\") return \"undefined\";\n return JSON.stringify(arg, null, 2) ?? \"[Unserializable value]\";\n })\n .join(\" \");\n };\n\n console.log = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args), false);\n };\n console.error = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args, true), true);\n };\n console.warn = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args), false);\n };\n console.info = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args), false);\n };\n\n return () => {\n console.log = originalLog;\n console.error = originalError;\n console.warn = originalWarn;\n console.info = originalInfo;\n };\n};\n\nexport class HostRemoteUrlMissing extends Data.TaggedError(\"HostRemoteUrlMissing\")<\n Record<never, never>\n> {\n override get message() {\n return \"remoteUrl not provided on host descriptor\";\n }\n}\n\nexport class HostModuleInvalid extends Data.TaggedError(\"HostModuleInvalid\")<Record<never, never>> {\n override get message() {\n return \"Host module does not export runServer function\";\n }\n}\n\nexport class LocalPathMissing extends Data.TaggedError(\"LocalPathMissing\")<{ key: string }> {\n get message() {\n return `No localPath for local service: ${this.key}`;\n }\n}\n\nconst spawnRemoteHost = (descriptor: ServiceDescriptor, callbacks: ProcessCallbacks) =>\n Effect.gen(function* () {\n const runtimeConfig = yield* DevRuntimeConfig;\n const remoteUrl = descriptor.remoteUrl;\n if (!remoteUrl) {\n return yield* new HostRemoteUrlMissing();\n }\n\n callbacks.onStatus(descriptor.key, \"starting\");\n callbacks.onLog(descriptor.key, `Remote: ${remoteUrl}`);\n const restoreConsole = patchConsole(descriptor.key, callbacks);\n callbacks.onLog(descriptor.key, \"Loading Module Federation runtime...\");\n\n const mfRuntime = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/enhanced/runtime\"),\n catch: (cause) =>\n new OrchestratorError({\n operation: \"load module federation runtime\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n const mfCore = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/runtime-core\"),\n catch: (cause) =>\n new OrchestratorError({\n operation: \"load module federation core\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n let mf = mfRuntime.getInstance();\n if (!mf) {\n mf = mfRuntime.createInstance({ name: \"cli-host\", remotes: [] });\n mfCore.setGlobalFederationInstance(mf);\n }\n patchManifestFetchForSsrPublicPath(mf as any);\n\n const baseUrl = remoteUrl\n .replace(/\\/remoteEntry\\.js$/, \"\")\n .replace(/\\/mf-manifest\\.json$/, \"\")\n .replace(/\\/$/, \"\");\n const remoteEntryUrl = `${baseUrl}/remoteEntry.js`;\n const manifestUrl = `${baseUrl}/mf-manifest.json`;\n\n const entryUrl = yield* Effect.tryPromise({\n try: async () => {\n try {\n let res: Response;\n try {\n res = await runFetch(manifestUrl, { signal: AbortSignal.timeout(10_000) });\n } catch {\n throw new Error(\"manifest fetch failed\");\n }\n if (!res.ok) return remoteEntryUrl;\n const json = (await res.json()) as Record<string, unknown>;\n if (\n json &&\n typeof json === \"object\" &&\n \"metaData\" in json &&\n \"exposes\" in json &&\n \"shared\" in json\n ) {\n return manifestUrl;\n }\n } catch (e) {\n warnOutsideEffect(\n `[Orchestrator] Failed to fetch or parse manifest from ${manifestUrl}, falling back to remoteEntryUrl: ${String(e)}`,\n );\n }\n return remoteEntryUrl;\n },\n catch: () => remoteEntryUrl,\n });\n\n (mf as any).registerRemotes([{ name: \"host\", entry: entryUrl }]);\n callbacks.onLog(descriptor.key, `Loading host from ${entryUrl}...`);\n\n const hostModule = yield* Effect.tryPromise({\n try: () =>\n (mf as any).loadRemote(\"host/Server\") as Promise<{\n runServer: (input: ServerInput) => ServerHandle;\n }>,\n catch: (cause) =>\n new OrchestratorError({\n operation: \"load host module\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n if (!hostModule?.runServer) {\n return yield* new HostModuleInvalid();\n }\n\n callbacks.onLog(descriptor.key, \"Starting server...\");\n const hostPort = runtimeConfig.host?.port;\n const hostEnv: Record<string, string> | undefined = hostPort\n ? { PORT: String(hostPort) }\n : undefined;\n const serverHandle = hostModule.runServer({\n config: runtimeConfig,\n port: hostPort,\n env: hostEnv,\n });\n yield* Effect.tryPromise({\n try: () => serverHandle.ready,\n catch: (cause) =>\n new OrchestratorError({\n operation: \"start server\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n callbacks.onStatus(descriptor.key, \"ready\");\n\n return {\n name: descriptor.key,\n pid: undefined,\n kill: Effect.gen(function* () {\n callbacks.onLog(descriptor.key, \"Shutting down remote host...\");\n restoreConsole();\n yield* Effect.tryPromise({\n try: () => serverHandle.shutdown(),\n catch: () => {},\n }).pipe(Effect.ignore);\n }),\n waitForReady: Effect.void,\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\n });\n\n/**\n * Spawn env precedence, three tiers: values explicitly exported by the\n * caller (shell / CI / regression harness — captured by the bootstrap\n * program into the `ShellEnv` service before any `.env` loading) outrank the\n * generated infra env, which outranks `.env`-file values inherited through\n * `processEnv`. The service's resolved port is always authoritative.\n */\nexport function composeSpawnEnv(\n processEnv: Record<string, string>,\n generatedEnv: Record<string, string>,\n port: number,\n shellEnv: Record<string, string> = {},\n): Record<string, string> {\n return {\n ...processEnv,\n ...mergeGeneratedOverFileEnv(generatedEnv, processEnv, shellEnv),\n FORCE_COLOR: \"1\",\n ...(port > 0 ? { PORT: String(port) } : {}),\n };\n}\n\n/**\n * Overlay the generated infra env (ports drift, so stale `.env` values must\n * lose) while keeping every explicitly exported key from `shellEnv` intact.\n * Keys absent from both are untouched.\n *\n * Exception (ADR 0012 / plan 038): the origin keys BASE_URL and CORS_ORIGIN\n * are always generated-owned — a wrapper that sourced a stale `.env` must not\n * win over the resolved host port, in the child env or on disk.\n */\nconst GENERATED_OWNED_KEYS = new Set([\"BASE_URL\", \"CORS_ORIGIN\"]);\n\nexport function mergeGeneratedOverFileEnv(\n generatedEnv: Record<string, string>,\n processEnv: Record<string, string>,\n shellEnv: Record<string, string> = {},\n): Record<string, string> {\n const result: Record<string, string> = { ...processEnv };\n for (const [key, value] of Object.entries(generatedEnv)) {\n if (!GENERATED_OWNED_KEYS.has(key) && key in shellEnv) {\n result[key] = shellEnv[key]!;\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nconst spawnDevProcess = (descriptor: ServiceDescriptor, callbacks: ProcessCallbacks) =>\n Effect.gen(function* () {\n const runtimeConfig = yield* DevRuntimeConfig;\n\n if (!descriptor.localPath) {\n return yield* new LocalPathMissing({ key: descriptor.key });\n }\n\n const fullCwd = descriptor.localPath;\n const command = descriptor.command ?? \"pnpm\";\n const args = descriptor.args ?? [\"run\", \"dev\"];\n const port = descriptor.port ?? descriptor.defaultPort;\n const name = descriptor.key;\n\n const readyDeferred = yield* Deferred.make<void, OrchestratorError>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n callbacks.onStatus(name, \"starting\");\n\n const generatedEnv = yield* DevGeneratedEnv;\n const shellTier = yield* ShellEnv;\n // The descriptor's env joins the generated tier — folding it in before\n // composeSpawnEnv keeps the documented precedence (shell > generated >\n // .env-file); assigning after would let it outrank the shell tier (and\n // the generated-owned keys) silently.\n const envVars = composeSpawnEnv(\n process.env as Record<string, string>,\n { ...generatedEnv, ...descriptor.env },\n port,\n shellTier,\n );\n\n envVars.BOS_RUNTIME_CONFIG = yield* encodeRuntimeConfig(runtimeConfig);\n\n const cmd = spawn(command, args, {\n cwd: fullCwd,\n env: envVars,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: true,\n });\n\n let lastExit: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n const exitCode = Effect.callback<number, OrchestratorError>((resume) => {\n const onExit = (code: number | null, signal: NodeJS.Signals | null) => {\n lastExit = { code, signal };\n resume(Effect.succeed(code ?? (signal ? 1 : 0)));\n };\n const onError = (err: Error) => {\n callbacks.onLog(name, `Spawn failed: ${err.message}`, true);\n callbacks.onStatus(name, \"error\", `spawn failed: ${err.message}`);\n resume(\n Effect.fail(\n new OrchestratorError({\n operation: \"spawn process\",\n detail: err.message,\n cause: err,\n }),\n ),\n );\n };\n cmd.once(\"exit\", onExit);\n cmd.once(\"error\", onError);\n return Effect.sync(() => {\n cmd.off(\"exit\", onExit);\n cmd.off(\"error\", onError);\n });\n });\n\n const describeExit = (exitCodeValue: number): string => {\n if (lastExit?.code === null && lastExit.signal) return `signal: ${lastExit.signal}`;\n return `exit code: ${exitCodeValue}`;\n };\n const isCleanSignalExit = (): boolean =>\n lastExit?.code === null && lastExit.signal != null && lastExit.signal in cleanExitSignals;\n\n const markReady = Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"ready\");\n callbacks.onStatus(name, \"ready\");\n yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);\n });\n\n const markError = (message: string) =>\n Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"error\");\n callbacks.onStatus(name, \"error\");\n yield* Deferred.fail(\n readyDeferred,\n new OrchestratorError({ operation: \"wait for process readiness\", detail: message }),\n ).pipe(Effect.ignore);\n });\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const readinessPath = descriptor.readinessPath;\n const url = `http://127.0.0.1:${port}${readinessPath}`;\n const readinessCheck = Effect.gen(function* () {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return true;\n if (port <= 0) return false;\n const ok = yield* probeHttpOk(url);\n if (ok) yield* markReady;\n return ok;\n });\n const ready = yield* Effect.repeat(readinessCheck, {\n schedule: Schedule.spaced(`${port > 0 ? LOCAL_PROBE_INTERVAL_MS : 500} millis`),\n until: (done) => done,\n }).pipe(\n Effect.timeout(`${LOCAL_PROBE_DEADLINE_MS} millis`),\n Effect.catchTag(\"TimeoutError\", () => Effect.succeed(false)),\n );\n if (ready) return;\n\n const status = yield* Ref.get(statusRef);\n if (status !== \"ready\" && status !== \"error\") {\n callbacks.onLog(name, \"Probe deadline exceeded after 90s\", true);\n yield* markError(`Probe deadline exceeded: ${name}`);\n }\n }),\n );\n\n const pid = cmd.pid ?? undefined;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const exitCodeValue = yield* exitCode;\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") {\n // Post-ready exits must stay visible — a silently dead child (OOM\n // kill included) used to leave the stack answering with nothing.\n // A SIGTERM/SIGINT-signalled exit is the polite-quit path, not a\n // crash, so it logs as a plain shutdown line.\n if (currentStatus === \"ready\") {\n const detail = describeExit(exitCodeValue);\n callbacks.onLog(name, `Process exited after ready (${detail})`, !isCleanSignalExit());\n if (!isCleanSignalExit()) {\n callbacks.onStatus(name, \"error\", `exited after ready (${detail})`);\n }\n yield* markError(`Process exited after ready: ${name}`);\n }\n return;\n }\n callbacks.onLog(\n name,\n `Process exited before ready (${describeExit(exitCodeValue)})`,\n !isCleanSignalExit(),\n );\n yield* markError(`Process exited before ready: ${name}`);\n }),\n );\n\n const handleLine = (line: string, isStderr: boolean) =>\n Effect.gen(function* () {\n if (!line.trim()) return;\n\n const cleanLine = stripAnsi(line);\n const looksLikeError =\n isStderr &&\n /^(error|fail|fatal|exception|unhandled|reject)/i.test(cleanLine) &&\n !cleanLine.startsWith(\"$\");\n callbacks.onLog(name, line, looksLikeError);\n\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\") return;\n\n const detected = detectStatus(line, descriptor);\n if (detected) {\n if (detected.status === \"ready\") {\n yield* markReady;\n } else if (currentStatus !== \"error\") {\n yield* markError(`Process failed: ${name}`);\n }\n }\n });\n\n const stdoutStream = cmd.stdout\n ? (Readable.toWeb(cmd.stdout) as unknown as ReadableStream<Uint8Array>)\n : null;\n const stderrStream = cmd.stderr\n ? (Readable.toWeb(cmd.stderr) as unknown as ReadableStream<Uint8Array>)\n : null;\n\n if (stdoutStream) {\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, false))(\n Stream.splitLines(\n Stream.decodeText(\n Stream.fromReadableStream({\n evaluate: () => stdoutStream,\n onError: (cause) =>\n new OrchestratorError({\n operation: \"read process stdout\",\n detail: String(cause),\n cause,\n }),\n }),\n ),\n ),\n ),\n );\n }\n\n if (stderrStream) {\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, true))(\n Stream.splitLines(\n Stream.decodeText(\n Stream.fromReadableStream({\n evaluate: () => stderrStream,\n onError: (cause) =>\n new OrchestratorError({\n operation: \"read process stderr\",\n detail: String(cause),\n cause,\n }),\n }),\n ),\n ),\n ),\n );\n }\n\n return {\n name,\n pid,\n kill: Effect.gen(function* () {\n const groupPid = pid;\n const groupSignal = (signal: NodeJS.Signals) =>\n Effect.try(() => {\n if (groupPid !== undefined) process.kill(-groupPid, signal);\n }).pipe(Effect.ignore);\n yield* groupSignal(\"SIGTERM\");\n const exited = yield* exitCode.pipe(Effect.timeout(\"3 seconds\"), Effect.option);\n if (Option.isNone(exited)) {\n yield* groupSignal(\"SIGKILL\");\n yield* Effect.sleep(\"250 millis\");\n }\n }).pipe(Effect.ignore),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: exitCode,\n } satisfies ProcessHandle;\n });\n\nconst spawnRemoteProbe = (\n pkg: string,\n descriptor: ServiceDescriptor,\n callbacks: ProcessCallbacks,\n) =>\n Effect.gen(function* () {\n callbacks.onStatus(pkg, \"starting\");\n const readyDeferred = yield* Deferred.make<void, OrchestratorError>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n const markReady = Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"ready\");\n yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);\n callbacks.onStatus(pkg, \"ready\", \"loaded\");\n });\n\n const markError = Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"error\");\n yield* Deferred.fail(\n readyDeferred,\n new OrchestratorError({\n operation: \"probe remote process\",\n detail: `Remote ${pkg} unreachable`,\n }),\n ).pipe(Effect.ignore);\n callbacks.onStatus(pkg, \"error\", \"unreachable\");\n });\n\n const baseUrl = descriptor.url.replace(/\\/$/, \"\");\n const manifestUrl = `${baseUrl}/mf-manifest.json`;\n const entryUrl = `${baseUrl}${descriptor.readinessPath}`;\n const probeUrl = descriptor.readinessPath === \"/health\" ? `${baseUrl}/health` : manifestUrl;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const readinessCheck = Effect.gen(function* () {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return true;\n\n const ok = yield* probeHttpOk(probeUrl, REMOTE_PROBE_TIMEOUT_MS);\n if (ok) {\n yield* markReady;\n return true;\n }\n\n const fallbackOk = yield* probeHttpOk(entryUrl, REMOTE_PROBE_TIMEOUT_MS);\n if (fallbackOk) {\n yield* markReady;\n return true;\n }\n return false;\n });\n const ready = yield* Effect.repeat(readinessCheck, {\n schedule: Schedule.min([\n Schedule.exponential(`${REMOTE_PROBE_BACKOFF_INITIAL_MS} millis`, 1.5),\n Schedule.spaced(`${REMOTE_PROBE_BACKOFF_MAX_MS} millis`),\n ]),\n until: (done) => done,\n }).pipe(\n Effect.timeout(`${REMOTE_PROBE_DEADLINE_MS} millis`),\n Effect.catchTag(\"TimeoutError\", () => Effect.succeed(false)),\n );\n if (ready) return;\n\n const status = yield* Ref.get(statusRef);\n if (status !== \"ready\") {\n yield* markError;\n }\n }),\n );\n\n return {\n name: pkg,\n pid: undefined,\n kill: Effect.gen(function* () {\n yield* Ref.set(statusRef, \"error\");\n yield* Deferred.fail(\n readyDeferred,\n new OrchestratorError({ operation: \"stop remote process\", detail: \"Killed\" }),\n ).pipe(Effect.ignore);\n }),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\n });\n\nexport const makeDevProcess = (pkg: string, callbacks: ProcessCallbacks, portOverride?: number) =>\n Effect.gen(function* () {\n const services = yield* ServiceDescriptorMap;\n const descriptor = services.get(pkg);\n\n if (!descriptor) {\n callbacks.onStatus(pkg, \"ready\", \"Remote\");\n return {\n name: pkg,\n pid: undefined,\n kill: Effect.void,\n waitForReady: Effect.void,\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\n }\n\n if (pkg === \"host\" && descriptor.source === \"remote\") {\n return yield* spawnRemoteHost(descriptor, callbacks);\n }\n\n if (descriptor.source === \"remote\" || !descriptor.localPath) {\n return yield* spawnRemoteProbe(pkg, descriptor, callbacks);\n }\n\n const resolvedDescriptor = portOverride ? { ...descriptor, port: portOverride } : descriptor;\n\n return yield* spawnDevProcess(resolvedDescriptor, callbacks);\n });\n\nexport function getProcessStates(\n packages: string[],\n services: Map<string, ServiceDescriptor>,\n portOverride?: number,\n): ProcessState[] {\n return packages.map((pkg) => {\n const descriptor = services.get(pkg);\n return {\n name: pkg,\n status: \"pending\" as const,\n port:\n portOverride && pkg === \"host\"\n ? portOverride\n : (descriptor?.port ?? descriptor?.defaultPort ?? 0),\n source: descriptor?.source,\n uiPort: descriptor?.uiPort,\n };\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"orchestrator.mjs","names":[],"sources":["../src/orchestrator.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Readable } from \"node:stream\";\nimport { Data, Deferred, Effect, Option, Ref, Schedule, Schema, Stream } from \"effect\";\nimport { stripAnsi } from \"./dev-log-pipeline\";\nimport { ShellEnv } from \"./env/project-env\";\nimport { patchManifestFetchForSsrPublicPath } from \"./mf\";\nimport {\n DevGeneratedEnv,\n DevRuntimeConfig,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\n\nconst runFetch = (url: string, init?: RequestInit): Promise<Response> => fetch(url, init);\n\nprocess.on(\"unhandledRejection\", (reason) => {\n console.error(\"[Orchestrator] Unhandled rejection:\", reason);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n console.error(\"[Orchestrator] Uncaught exception:\", err);\n});\n\nexport interface ProcessCallbacks {\n onStatus: (name: string, status: ProcessStatus, message?: string) => void;\n onLog: (name: string, line: string, isError?: boolean) => void;\n}\n\nexport interface ProcessHandle {\n name: string;\n pid: number | undefined;\n kill: Effect.Effect<void>;\n waitForReady: Effect.Effect<void, OrchestratorError>;\n waitForExit: Effect.Effect<number, OrchestratorError>;\n}\n\nexport class OrchestratorError extends Data.TaggedError(\"OrchestratorError\")<{\n readonly operation: string;\n readonly detail: string;\n readonly cause?: unknown;\n}> {\n override get message() {\n return `${this.operation}: ${this.detail}`;\n }\n}\n\nconst omitUndefined = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n return value.map((item) => (item === undefined ? null : omitUndefined(item)));\n }\n if (value !== null && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, item]) => item !== undefined)\n .map(([key, item]) => [key, omitUndefined(item)]),\n );\n }\n return value;\n};\n\nexport const encodeRuntimeConfig = (runtimeConfig: unknown) =>\n Schema.encodeUnknownEffect(Schema.fromJsonString(Schema.Json))(omitUndefined(runtimeConfig)).pipe(\n Effect.mapError(\n (cause) =>\n new OrchestratorError({\n operation: \"serialize runtime config\",\n detail: cause.message,\n cause,\n }),\n ),\n );\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: \"local\" | \"remote\";\n uiPort?: number;\n}\n\nconst probeHttpOk = (url: string, timeoutMs = 400) =>\n Effect.tryPromise({\n try: async () => {\n try {\n const res = await runFetch(url, { signal: AbortSignal.timeout(timeoutMs) });\n return res.ok;\n } catch {\n return false;\n }\n },\n catch: () => false,\n });\n\nconst LOCAL_PROBE_DEADLINE_MS = 90_000;\nconst LOCAL_PROBE_INTERVAL_MS = 200;\n\nconst cleanExitSignals: Record<string, true> = { SIGTERM: true, SIGINT: true };\n\nconst REMOTE_PROBE_TIMEOUT_MS = 5000;\nconst REMOTE_PROBE_DEADLINE_MS = 60_000;\nconst REMOTE_PROBE_BACKOFF_INITIAL_MS = 1000;\nconst REMOTE_PROBE_BACKOFF_MAX_MS = 15_000;\n\nexport const detectStatus = (\n line: string,\n descriptor: ServiceDescriptor,\n): { status: ProcessStatus; isError: boolean } | null => {\n const cleanLine = stripAnsi(line);\n const errorPatterns = descriptor.errorPatterns ?? [];\n const readyPatterns = descriptor.readyPatterns ?? [];\n for (const pattern of readyPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"ready\", isError: false };\n }\n }\n for (const pattern of errorPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"error\", isError: true };\n }\n }\n return null;\n};\n\ninterface ServerHandle {\n ready: Promise<void>;\n shutdown: () => Promise<void>;\n}\n\ninterface ServerInput {\n config: RuntimeConfig;\n port?: number;\n env?: Record<string, string>;\n}\n\nconst patchConsole = (name: string, callbacks: ProcessCallbacks): (() => void) => {\n const originalLog = console.log;\n const originalError = console.error;\n const originalWarn = console.warn;\n const originalInfo = console.info;\n\n const formatArgs = (args: unknown[], isError = false): string => {\n return args\n .map((arg) => {\n if (arg instanceof Error) {\n const parts = [`${arg.name}: ${arg.message}`];\n if (arg.cause instanceof Error)\n parts.push(`(cause: ${arg.cause.name}: ${arg.cause.message})`);\n else if (arg.cause) {\n const cause = arg.cause;\n parts.push(\n `(cause: ${cause instanceof Error ? cause.message : typeof cause === \"string\" ? cause : (JSON.stringify(cause) ?? \"[Unserializable cause]\")})`,\n );\n }\n if (isError && arg.stack) parts.push(arg.stack);\n return parts.join(\"\\n\");\n }\n if (arg === null) return \"null\";\n if (typeof arg === \"string\") return arg;\n if (typeof arg === \"number\" || typeof arg === \"bigint\" || typeof arg === \"boolean\")\n return `${arg}`;\n if (typeof arg === \"symbol\") return arg.description ?? \"\";\n if (typeof arg === \"undefined\") return \"undefined\";\n return JSON.stringify(arg, null, 2) ?? \"[Unserializable value]\";\n })\n .join(\" \");\n };\n\n console.log = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args), false);\n };\n console.error = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args, true), true);\n };\n console.warn = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args), false);\n };\n console.info = (...args: unknown[]) => {\n callbacks.onLog(name, formatArgs(args), false);\n };\n\n return () => {\n console.log = originalLog;\n console.error = originalError;\n console.warn = originalWarn;\n console.info = originalInfo;\n };\n};\n\nexport class HostRemoteUrlMissing extends Data.TaggedError(\"HostRemoteUrlMissing\")<\n Record<never, never>\n> {\n override get message() {\n return \"remoteUrl not provided on host descriptor\";\n }\n}\n\nexport class HostModuleInvalid extends Data.TaggedError(\"HostModuleInvalid\")<Record<never, never>> {\n override get message() {\n return \"Host module does not export runServer function\";\n }\n}\n\nexport class LocalPathMissing extends Data.TaggedError(\"LocalPathMissing\")<{ key: string }> {\n get message() {\n return `No localPath for local service: ${this.key}`;\n }\n}\n\nconst spawnRemoteHost = (descriptor: ServiceDescriptor, callbacks: ProcessCallbacks) =>\n Effect.gen(function* () {\n const runtimeConfig = yield* DevRuntimeConfig;\n const remoteUrl = descriptor.remoteUrl;\n if (!remoteUrl) {\n return yield* new HostRemoteUrlMissing();\n }\n\n callbacks.onStatus(descriptor.key, \"starting\");\n callbacks.onLog(descriptor.key, `Remote: ${remoteUrl}`);\n const restoreConsole = patchConsole(descriptor.key, callbacks);\n callbacks.onLog(descriptor.key, \"Loading Module Federation runtime...\");\n\n const mfRuntime = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/enhanced/runtime\"),\n catch: (cause) =>\n new OrchestratorError({\n operation: \"load module federation runtime\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n const mfCore = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/runtime-core\"),\n catch: (cause) =>\n new OrchestratorError({\n operation: \"load module federation core\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n let mf = mfRuntime.getInstance();\n if (!mf) {\n mf = mfRuntime.createInstance({ name: \"cli-host\", remotes: [] });\n mfCore.setGlobalFederationInstance(mf);\n }\n patchManifestFetchForSsrPublicPath(mf as any);\n\n const baseUrl = remoteUrl\n .replace(/\\/remoteEntry\\.js$/, \"\")\n .replace(/\\/mf-manifest\\.json$/, \"\")\n .replace(/\\/$/, \"\");\n const manifestUrl = `${baseUrl}/mf-manifest.json`;\n\n // The manifest IS the entry contract (ADR 0020 amendment: hashed\n // entrypoints, fixed-name `remoteEntry.js` retired — it 404s on the CDN).\n // A failed manifest fetch must fail loud with the URL and status: the old\n // silent fallback to remoteEntry.js turned any transient hiccup (a CDN\n // 403 challenge, a timeout) into a guaranteed loadRemote failure with no\n // named cause.\n const entryUrl = yield* Effect.tryPromise({\n try: async () => {\n let res: Response;\n try {\n res = await runFetch(manifestUrl, { signal: AbortSignal.timeout(10_000) });\n } catch (e) {\n throw new OrchestratorError({\n operation: \"fetch host manifest\",\n detail: `${manifestUrl} — ${e instanceof Error ? e.message : String(e)}`,\n cause: e,\n });\n }\n if (!res.ok) {\n throw new OrchestratorError({\n operation: \"fetch host manifest\",\n detail: `${manifestUrl} — HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : \"\"}`,\n });\n }\n const json = (await res.json()) as Record<string, unknown>;\n if (\n !json ||\n typeof json !== \"object\" ||\n !(\"metaData\" in json) ||\n !(\"exposes\" in json) ||\n !(\"shared\" in json)\n ) {\n throw new OrchestratorError({\n operation: \"fetch host manifest\",\n detail: `${manifestUrl} — not a Module Federation manifest (missing metaData/exposes/shared)`,\n });\n }\n return manifestUrl;\n },\n catch: (cause) =>\n cause instanceof OrchestratorError\n ? cause\n : new OrchestratorError({\n operation: \"fetch host manifest\",\n detail: `${manifestUrl} — ${String(cause)}`,\n }),\n });\n\n (mf as any).registerRemotes([{ name: \"host\", entry: entryUrl }]);\n callbacks.onLog(descriptor.key, `Loading host from ${entryUrl}...`);\n\n const hostModule = yield* Effect.tryPromise({\n try: () =>\n (mf as any).loadRemote(\"host/Server\") as Promise<{\n runServer: (input: ServerInput) => ServerHandle;\n }>,\n catch: (cause) =>\n new OrchestratorError({\n operation: \"load host module\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n if (!hostModule?.runServer) {\n return yield* new HostModuleInvalid();\n }\n\n callbacks.onLog(descriptor.key, \"Starting server...\");\n const hostPort = runtimeConfig.host?.port;\n const hostEnv: Record<string, string> | undefined = hostPort\n ? { PORT: String(hostPort) }\n : undefined;\n const serverHandle = hostModule.runServer({\n config: runtimeConfig,\n port: hostPort,\n env: hostEnv,\n });\n yield* Effect.tryPromise({\n try: () => serverHandle.ready,\n catch: (cause) =>\n new OrchestratorError({\n operation: \"start server\",\n detail: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n\n callbacks.onStatus(descriptor.key, \"ready\");\n\n return {\n name: descriptor.key,\n pid: undefined,\n kill: Effect.gen(function* () {\n callbacks.onLog(descriptor.key, \"Shutting down remote host...\");\n restoreConsole();\n yield* Effect.tryPromise({\n try: () => serverHandle.shutdown(),\n catch: () => {},\n }).pipe(Effect.ignore);\n }),\n waitForReady: Effect.void,\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\n });\n\n/**\n * Spawn env precedence, three tiers: values explicitly exported by the\n * caller (shell / CI / regression harness — captured by the bootstrap\n * program into the `ShellEnv` service before any `.env` loading) outrank the\n * generated infra env, which outranks `.env`-file values inherited through\n * `processEnv`. The service's resolved port is always authoritative.\n */\nexport function composeSpawnEnv(\n processEnv: Record<string, string>,\n generatedEnv: Record<string, string>,\n port: number,\n shellEnv: Record<string, string> = {},\n): Record<string, string> {\n return {\n ...processEnv,\n ...mergeGeneratedOverFileEnv(generatedEnv, processEnv, shellEnv),\n FORCE_COLOR: \"1\",\n ...(port > 0 ? { PORT: String(port) } : {}),\n };\n}\n\n/**\n * Overlay the generated infra env (ports drift, so stale `.env` values must\n * lose) while keeping every explicitly exported key from `shellEnv` intact.\n * Keys absent from both are untouched.\n *\n * Exception (ADR 0012 / plan 038): the origin keys BASE_URL and CORS_ORIGIN\n * are always generated-owned — a wrapper that sourced a stale `.env` must not\n * win over the resolved host port, in the child env or on disk.\n */\nconst GENERATED_OWNED_KEYS = new Set([\"BASE_URL\", \"CORS_ORIGIN\"]);\n\nexport function mergeGeneratedOverFileEnv(\n generatedEnv: Record<string, string>,\n processEnv: Record<string, string>,\n shellEnv: Record<string, string> = {},\n): Record<string, string> {\n const result: Record<string, string> = { ...processEnv };\n for (const [key, value] of Object.entries(generatedEnv)) {\n if (!GENERATED_OWNED_KEYS.has(key) && key in shellEnv) {\n result[key] = shellEnv[key]!;\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nconst spawnDevProcess = (descriptor: ServiceDescriptor, callbacks: ProcessCallbacks) =>\n Effect.gen(function* () {\n const runtimeConfig = yield* DevRuntimeConfig;\n\n if (!descriptor.localPath) {\n return yield* new LocalPathMissing({ key: descriptor.key });\n }\n\n const fullCwd = descriptor.localPath;\n const command = descriptor.command ?? \"pnpm\";\n const args = descriptor.args ?? [\"run\", \"dev\"];\n const port = descriptor.port ?? descriptor.defaultPort;\n const name = descriptor.key;\n\n const readyDeferred = yield* Deferred.make<void, OrchestratorError>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n callbacks.onStatus(name, \"starting\");\n\n const generatedEnv = yield* DevGeneratedEnv;\n const shellTier = yield* ShellEnv;\n // The descriptor's env joins the generated tier — folding it in before\n // composeSpawnEnv keeps the documented precedence (shell > generated >\n // .env-file); assigning after would let it outrank the shell tier (and\n // the generated-owned keys) silently.\n const envVars = composeSpawnEnv(\n process.env as Record<string, string>,\n { ...generatedEnv, ...descriptor.env },\n port,\n shellTier,\n );\n\n envVars.BOS_RUNTIME_CONFIG = yield* encodeRuntimeConfig(runtimeConfig);\n\n const cmd = spawn(command, args, {\n cwd: fullCwd,\n env: envVars,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: true,\n });\n\n let lastExit: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n const exitCode = Effect.callback<number, OrchestratorError>((resume) => {\n const onExit = (code: number | null, signal: NodeJS.Signals | null) => {\n lastExit = { code, signal };\n resume(Effect.succeed(code ?? (signal ? 1 : 0)));\n };\n const onError = (err: Error) => {\n callbacks.onLog(name, `Spawn failed: ${err.message}`, true);\n callbacks.onStatus(name, \"error\", `spawn failed: ${err.message}`);\n resume(\n Effect.fail(\n new OrchestratorError({\n operation: \"spawn process\",\n detail: err.message,\n cause: err,\n }),\n ),\n );\n };\n cmd.once(\"exit\", onExit);\n cmd.once(\"error\", onError);\n return Effect.sync(() => {\n cmd.off(\"exit\", onExit);\n cmd.off(\"error\", onError);\n });\n });\n\n const describeExit = (exitCodeValue: number): string => {\n if (lastExit?.code === null && lastExit.signal) return `signal: ${lastExit.signal}`;\n return `exit code: ${exitCodeValue}`;\n };\n const isCleanSignalExit = (): boolean =>\n lastExit?.code === null && lastExit.signal != null && lastExit.signal in cleanExitSignals;\n\n const markReady = Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"ready\");\n callbacks.onStatus(name, \"ready\");\n yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);\n });\n\n const markError = (message: string) =>\n Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"error\");\n callbacks.onStatus(name, \"error\");\n yield* Deferred.fail(\n readyDeferred,\n new OrchestratorError({ operation: \"wait for process readiness\", detail: message }),\n ).pipe(Effect.ignore);\n });\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const readinessPath = descriptor.readinessPath;\n const url = `http://127.0.0.1:${port}${readinessPath}`;\n const readinessCheck = Effect.gen(function* () {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return true;\n if (port <= 0) return false;\n const ok = yield* probeHttpOk(url);\n if (ok) yield* markReady;\n return ok;\n });\n const ready = yield* Effect.repeat(readinessCheck, {\n schedule: Schedule.spaced(`${port > 0 ? LOCAL_PROBE_INTERVAL_MS : 500} millis`),\n until: (done) => done,\n }).pipe(\n Effect.timeout(`${LOCAL_PROBE_DEADLINE_MS} millis`),\n Effect.catchTag(\"TimeoutError\", () => Effect.succeed(false)),\n );\n if (ready) return;\n\n const status = yield* Ref.get(statusRef);\n if (status !== \"ready\" && status !== \"error\") {\n callbacks.onLog(name, \"Probe deadline exceeded after 90s\", true);\n yield* markError(`Probe deadline exceeded: ${name}`);\n }\n }),\n );\n\n const pid = cmd.pid ?? undefined;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const exitCodeValue = yield* exitCode;\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") {\n // Post-ready exits must stay visible — a silently dead child (OOM\n // kill included) used to leave the stack answering with nothing.\n // A SIGTERM/SIGINT-signalled exit is the polite-quit path, not a\n // crash, so it logs as a plain shutdown line.\n if (currentStatus === \"ready\") {\n const detail = describeExit(exitCodeValue);\n callbacks.onLog(name, `Process exited after ready (${detail})`, !isCleanSignalExit());\n if (!isCleanSignalExit()) {\n callbacks.onStatus(name, \"error\", `exited after ready (${detail})`);\n }\n yield* markError(`Process exited after ready: ${name}`);\n }\n return;\n }\n callbacks.onLog(\n name,\n `Process exited before ready (${describeExit(exitCodeValue)})`,\n !isCleanSignalExit(),\n );\n yield* markError(`Process exited before ready: ${name}`);\n }),\n );\n\n const handleLine = (line: string, isStderr: boolean) =>\n Effect.gen(function* () {\n if (!line.trim()) return;\n\n const cleanLine = stripAnsi(line);\n const looksLikeError =\n isStderr &&\n /^(error|fail|fatal|exception|unhandled|reject)/i.test(cleanLine) &&\n !cleanLine.startsWith(\"$\");\n callbacks.onLog(name, line, looksLikeError);\n\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\") return;\n\n const detected = detectStatus(line, descriptor);\n if (detected) {\n if (detected.status === \"ready\") {\n yield* markReady;\n } else if (currentStatus !== \"error\") {\n yield* markError(`Process failed: ${name}`);\n }\n }\n });\n\n const stdoutStream = cmd.stdout\n ? (Readable.toWeb(cmd.stdout) as unknown as ReadableStream<Uint8Array>)\n : null;\n const stderrStream = cmd.stderr\n ? (Readable.toWeb(cmd.stderr) as unknown as ReadableStream<Uint8Array>)\n : null;\n\n if (stdoutStream) {\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, false))(\n Stream.splitLines(\n Stream.decodeText(\n Stream.fromReadableStream({\n evaluate: () => stdoutStream,\n onError: (cause) =>\n new OrchestratorError({\n operation: \"read process stdout\",\n detail: String(cause),\n cause,\n }),\n }),\n ),\n ),\n ),\n );\n }\n\n if (stderrStream) {\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, true))(\n Stream.splitLines(\n Stream.decodeText(\n Stream.fromReadableStream({\n evaluate: () => stderrStream,\n onError: (cause) =>\n new OrchestratorError({\n operation: \"read process stderr\",\n detail: String(cause),\n cause,\n }),\n }),\n ),\n ),\n ),\n );\n }\n\n return {\n name,\n pid,\n kill: Effect.gen(function* () {\n const groupPid = pid;\n const groupSignal = (signal: NodeJS.Signals) =>\n Effect.try(() => {\n if (groupPid !== undefined) process.kill(-groupPid, signal);\n }).pipe(Effect.ignore);\n yield* groupSignal(\"SIGTERM\");\n const exited = yield* exitCode.pipe(Effect.timeout(\"3 seconds\"), Effect.option);\n if (Option.isNone(exited)) {\n yield* groupSignal(\"SIGKILL\");\n yield* Effect.sleep(\"250 millis\");\n }\n }).pipe(Effect.ignore),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: exitCode,\n } satisfies ProcessHandle;\n });\n\nconst spawnRemoteProbe = (\n pkg: string,\n descriptor: ServiceDescriptor,\n callbacks: ProcessCallbacks,\n) =>\n Effect.gen(function* () {\n callbacks.onStatus(pkg, \"starting\");\n const readyDeferred = yield* Deferred.make<void, OrchestratorError>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n const markReady = Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"ready\");\n yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);\n callbacks.onStatus(pkg, \"ready\", \"loaded\");\n });\n\n const markError = Effect.gen(function* () {\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n yield* Ref.set(statusRef, \"error\");\n yield* Deferred.fail(\n readyDeferred,\n new OrchestratorError({\n operation: \"probe remote process\",\n detail: `Remote ${pkg} unreachable`,\n }),\n ).pipe(Effect.ignore);\n callbacks.onStatus(pkg, \"error\", \"unreachable\");\n });\n\n const baseUrl = descriptor.url.replace(/\\/$/, \"\");\n const manifestUrl = `${baseUrl}/mf-manifest.json`;\n const entryUrl = `${baseUrl}${descriptor.readinessPath}`;\n const probeUrl = descriptor.readinessPath === \"/health\" ? `${baseUrl}/health` : manifestUrl;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const readinessCheck = Effect.gen(function* () {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return true;\n\n const ok = yield* probeHttpOk(probeUrl, REMOTE_PROBE_TIMEOUT_MS);\n if (ok) {\n yield* markReady;\n return true;\n }\n\n const fallbackOk = yield* probeHttpOk(entryUrl, REMOTE_PROBE_TIMEOUT_MS);\n if (fallbackOk) {\n yield* markReady;\n return true;\n }\n return false;\n });\n const ready = yield* Effect.repeat(readinessCheck, {\n schedule: Schedule.min([\n Schedule.exponential(`${REMOTE_PROBE_BACKOFF_INITIAL_MS} millis`, 1.5),\n Schedule.spaced(`${REMOTE_PROBE_BACKOFF_MAX_MS} millis`),\n ]),\n until: (done) => done,\n }).pipe(\n Effect.timeout(`${REMOTE_PROBE_DEADLINE_MS} millis`),\n Effect.catchTag(\"TimeoutError\", () => Effect.succeed(false)),\n );\n if (ready) return;\n\n const status = yield* Ref.get(statusRef);\n if (status !== \"ready\") {\n yield* markError;\n }\n }),\n );\n\n return {\n name: pkg,\n pid: undefined,\n kill: Effect.gen(function* () {\n yield* Ref.set(statusRef, \"error\");\n yield* Deferred.fail(\n readyDeferred,\n new OrchestratorError({ operation: \"stop remote process\", detail: \"Killed\" }),\n ).pipe(Effect.ignore);\n }),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\n });\n\nexport const makeDevProcess = (pkg: string, callbacks: ProcessCallbacks, portOverride?: number) =>\n Effect.gen(function* () {\n const services = yield* ServiceDescriptorMap;\n const descriptor = services.get(pkg);\n\n if (!descriptor) {\n callbacks.onStatus(pkg, \"ready\", \"Remote\");\n return {\n name: pkg,\n pid: undefined,\n kill: Effect.void,\n waitForReady: Effect.void,\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\n }\n\n if (pkg === \"host\" && descriptor.source === \"remote\") {\n return yield* spawnRemoteHost(descriptor, callbacks);\n }\n\n if (descriptor.source === \"remote\" || !descriptor.localPath) {\n return yield* spawnRemoteProbe(pkg, descriptor, callbacks);\n }\n\n const resolvedDescriptor = portOverride ? { ...descriptor, port: portOverride } : descriptor;\n\n return yield* spawnDevProcess(resolvedDescriptor, callbacks);\n });\n\nexport function getProcessStates(\n packages: string[],\n services: Map<string, ServiceDescriptor>,\n portOverride?: number,\n): ProcessState[] {\n return packages.map((pkg) => {\n const descriptor = services.get(pkg);\n return {\n name: pkg,\n status: \"pending\" as const,\n port:\n portOverride && pkg === \"host\"\n ? portOverride\n : (descriptor?.port ?? descriptor?.defaultPort ?? 0),\n source: descriptor?.source,\n uiPort: descriptor?.uiPort,\n };\n });\n}\n"],"mappings":"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"}
|