everything-dev 1.43.0 → 1.44.1

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.
Files changed (79) hide show
  1. package/dist/api-contract.cjs +35 -39
  2. package/dist/api-contract.cjs.map +1 -1
  3. package/dist/api-contract.mjs +35 -39
  4. package/dist/api-contract.mjs.map +1 -1
  5. package/dist/cli/catalog.cjs +2 -2
  6. package/dist/cli/catalog.cjs.map +1 -1
  7. package/dist/cli/catalog.mjs +2 -2
  8. package/dist/cli/catalog.mjs.map +1 -1
  9. package/dist/cli/infra.cjs +14 -4
  10. package/dist/cli/infra.cjs.map +1 -1
  11. package/dist/cli/infra.mjs +14 -4
  12. package/dist/cli/infra.mjs.map +1 -1
  13. package/dist/cli/init.cjs +9 -4
  14. package/dist/cli/init.cjs.map +1 -1
  15. package/dist/cli/init.d.cts +1 -1
  16. package/dist/cli/init.d.cts.map +1 -1
  17. package/dist/cli/init.d.mts +1 -1
  18. package/dist/cli/init.d.mts.map +1 -1
  19. package/dist/cli/init.mjs +9 -4
  20. package/dist/cli/init.mjs.map +1 -1
  21. package/dist/cli/status.cjs +13 -29
  22. package/dist/cli/status.cjs.map +1 -1
  23. package/dist/cli/status.mjs +13 -29
  24. package/dist/cli/status.mjs.map +1 -1
  25. package/dist/cli.cjs +9 -4
  26. package/dist/cli.cjs.map +1 -1
  27. package/dist/cli.mjs +9 -4
  28. package/dist/cli.mjs.map +1 -1
  29. package/dist/config.cjs +27 -23
  30. package/dist/config.cjs.map +1 -1
  31. package/dist/config.d.cts.map +1 -1
  32. package/dist/config.d.mts.map +1 -1
  33. package/dist/config.mjs +27 -23
  34. package/dist/config.mjs.map +1 -1
  35. package/dist/contract.cjs +6 -5
  36. package/dist/contract.cjs.map +1 -1
  37. package/dist/contract.d.cts +12 -4
  38. package/dist/contract.d.cts.map +1 -1
  39. package/dist/contract.d.mts +12 -4
  40. package/dist/contract.d.mts.map +1 -1
  41. package/dist/contract.mjs +6 -5
  42. package/dist/contract.mjs.map +1 -1
  43. package/dist/dev-session.cjs +3 -1
  44. package/dist/dev-session.cjs.map +1 -1
  45. package/dist/dev-session.mjs +3 -1
  46. package/dist/dev-session.mjs.map +1 -1
  47. package/dist/fastkv.cjs +13 -29
  48. package/dist/fastkv.cjs.map +1 -1
  49. package/dist/fastkv.mjs +14 -29
  50. package/dist/fastkv.mjs.map +1 -1
  51. package/dist/http-client.cjs +102 -0
  52. package/dist/http-client.cjs.map +1 -0
  53. package/dist/http-client.mjs +100 -0
  54. package/dist/http-client.mjs.map +1 -0
  55. package/dist/integrity.cjs +3 -2
  56. package/dist/integrity.cjs.map +1 -1
  57. package/dist/integrity.d.cts.map +1 -1
  58. package/dist/integrity.d.mts.map +1 -1
  59. package/dist/integrity.mjs +3 -2
  60. package/dist/integrity.mjs.map +1 -1
  61. package/dist/mf.cjs +10 -2
  62. package/dist/mf.cjs.map +1 -1
  63. package/dist/mf.d.cts.map +1 -1
  64. package/dist/mf.d.mts.map +1 -1
  65. package/dist/mf.mjs +10 -2
  66. package/dist/mf.mjs.map +1 -1
  67. package/dist/near-cli.cjs +1 -1
  68. package/dist/near-cli.mjs +1 -1
  69. package/dist/orchestrator.cjs +39 -23
  70. package/dist/orchestrator.cjs.map +1 -1
  71. package/dist/orchestrator.mjs +39 -23
  72. package/dist/orchestrator.mjs.map +1 -1
  73. package/dist/plugin.cjs +57 -65
  74. package/dist/plugin.cjs.map +1 -1
  75. package/dist/plugin.d.cts +4 -0
  76. package/dist/plugin.d.mts +4 -0
  77. package/dist/plugin.mjs +57 -65
  78. package/dist/plugin.mjs.map +1 -1
  79. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.cjs","names":["Effect","DevRuntimeConfig","Deferred","Ref","Command","Stream","Option","ServiceDescriptorMap"],"sources":["../src/orchestrator.ts"],"sourcesContent":["import { Command } from \"@effect/platform\";\nimport type { ExitCode } from \"@effect/platform/CommandExecutor\";\nimport { Deferred, Effect, Option, Ref, Stream } from \"effect\";\nimport { patchManifestFetchForSsrPublicPath } from \"./mf\";\nimport {\n DevRuntimeConfig,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\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, unknown>;\n waitForReady: Effect.Effect<void, Error>;\n waitForExit: Effect.Effect<ExitCode, unknown>;\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}\n\nconst stripAnsi = (input: string): string => {\n const ESC = String.fromCharCode(27);\n const BEL = String.fromCharCode(7);\n return input\n .replace(new RegExp(`${ESC}\\\\][^${BEL}]*${BEL}`, \"g\"), \"\")\n .replace(new RegExp(`${ESC}\\\\[[0-?]*[ -/]*[@-~]`, \"g\"), \"\");\n};\n\nconst probeHttpOk = (url: string, timeoutMs = 400) =>\n Effect.tryPromise({\n try: async () => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n },\n catch: () => false,\n });\n\nconst 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 errorPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"error\", isError: true };\n }\n }\n for (const pattern of readyPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"ready\", isError: false };\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}\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) parts.push(`(cause: ${String(arg.cause)})`);\n if (isError && arg.stack) parts.push(arg.stack);\n return parts.join(\"\\n\");\n }\n return typeof arg === \"object\" ? JSON.stringify(arg, null, 2) : String(arg);\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\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* Effect.fail(new Error(\"remoteUrl not provided on host descriptor\"));\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: (e) => new Error(`Failed to load MF runtime: ${e}`),\n });\n\n const mfCore = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/runtime-core\"),\n catch: (e) => new Error(`Failed to load MF core: ${e}`),\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 const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 10_000);\n let res: Response;\n try {\n res = await fetch(manifestUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timer);\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 {}\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: (e) => new Error(`Failed to load host module: ${e}`),\n });\n\n if (!hostModule?.runServer) {\n return yield* Effect.fail(new Error(\"Host module does not export runServer function\"));\n }\n\n callbacks.onLog(descriptor.key, \"Starting server...\");\n const serverHandle = hostModule.runServer({ config: runtimeConfig });\n yield* Effect.tryPromise({\n try: () => serverHandle.ready,\n catch: (e) => new Error(`Server failed to start: ${e}`),\n });\n\n callbacks.onStatus(descriptor.key, \"ready\");\n\n return {\n name: descriptor.key,\n pid: process.pid,\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.succeed(undefined),\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\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* Effect.fail(new Error(`No localPath for local service: ${descriptor.key}`));\n }\n\n const fullCwd = descriptor.localPath;\n const command = descriptor.command ?? \"bun\";\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, Error>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n callbacks.onStatus(name, \"starting\");\n\n const envVars: Record<string, string> = {\n ...(process.env as Record<string, string>),\n FORCE_COLOR: \"1\",\n ...(port > 0 ? { PORT: String(port) } : {}),\n };\n\n if (name === \"host\") {\n envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);\n }\n\n const cmd = Command.make(command, ...args).pipe(\n Command.workingDirectory(fullCwd),\n Command.env(envVars),\n );\n\n const proc = yield* Command.start(cmd);\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 if (port > 0) {\n const readinessPath = descriptor.readinessPath;\n const url = `http://127.0.0.1:${port}${readinessPath}`;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const deadline = Date.now() + 90_000;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n const ok = yield* probeHttpOk(url);\n if (ok) {\n yield* markReady;\n return;\n }\n yield* Effect.sleep(\"200 millis\");\n }\n }),\n );\n }\n\n const pid = Number(proc.pid);\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const exitCode = yield* proc.exitCode;\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);\n yield* Ref.set(statusRef, \"error\");\n callbacks.onStatus(name, \"error\");\n yield* Deferred.fail(readyDeferred, new Error(`Process exited before ready: ${name}`)).pipe(\n Effect.ignore,\n );\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 !/^\\$/.test(cleanLine);\n callbacks.onLog(name, line, looksLikeError);\n\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n\n const detected = detectStatus(line, descriptor);\n if (detected) {\n yield* Ref.set(statusRef, detected.status);\n callbacks.onStatus(name, detected.status);\n if (detected.status === \"ready\" || detected.status === \"error\") {\n if (detected.status === \"ready\") {\n yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);\n } else {\n yield* Deferred.fail(readyDeferred, new Error(`Process failed: ${name}`)).pipe(\n Effect.ignore,\n );\n }\n }\n }\n });\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, false))(\n Stream.splitLines(Stream.decodeText(proc.stdout, \"utf-8\")),\n ),\n );\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, true))(\n Stream.splitLines(Stream.decodeText(proc.stderr, \"utf-8\")),\n ),\n );\n\n return {\n name,\n pid,\n kill: Effect.gen(function* () {\n const result = yield* proc.kill(\"SIGTERM\").pipe(Effect.timeout(\"3 seconds\"), Effect.option);\n if (Option.isNone(result)) {\n const pid = Number(proc.pid);\n yield* Effect.try(() => process.kill(-pid, \"SIGKILL\")).pipe(Effect.ignore);\n yield* Effect.sleep(\"250 millis\");\n }\n }).pipe(Effect.ignore),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: proc.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, Error>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n const markReady = Effect.gen(function* () {\n yield* Ref.set(statusRef, \"ready\");\n yield* Deferred.succeed(readyDeferred, undefined);\n callbacks.onStatus(pkg, \"ready\", \"loaded\");\n });\n\n const markError = Effect.gen(function* () {\n yield* Ref.set(statusRef, \"error\");\n yield* Deferred.fail(readyDeferred, new Error(`Remote ${pkg} unreachable`));\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 const REMOTE_PROBE_TIMEOUT_MS = 5000;\n const REMOTE_PROBE_BACKOFF_INITIAL_MS = 1000;\n const REMOTE_PROBE_BACKOFF_MAX_MS = 15_000;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n yield* Effect.sleep(`${Math.floor(Math.random() * 2000)} millis`);\n\n const deadline = Date.now() + 60_000;\n let delay = REMOTE_PROBE_BACKOFF_INITIAL_MS;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n\n const ok = yield* probeHttpOk(probeUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (ok) {\n yield* markReady;\n return;\n }\n\n const fallbackOk = yield* probeHttpOk(entryUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (fallbackOk) {\n yield* markReady;\n return;\n }\n\n yield* Effect.sleep(`${delay} millis`);\n delay = Math.min(Math.round(delay * 1.5), REMOTE_PROBE_BACKOFF_MAX_MS);\n }\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(readyDeferred, new Error(\"Killed\")).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 };\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"orchestrator.cjs","names":["Effect","DevRuntimeConfig","Deferred","Ref","Command","Stream","Option","ServiceDescriptorMap"],"sources":["../src/orchestrator.ts"],"sourcesContent":["import { Command } from \"@effect/platform\";\nimport type { ExitCode } from \"@effect/platform/CommandExecutor\";\nimport { Deferred, Effect, Option, Ref, Stream } from \"effect\";\nimport { patchManifestFetchForSsrPublicPath } from \"./mf\";\nimport {\n DevRuntimeConfig,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\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, unknown>;\n waitForReady: Effect.Effect<void, Error>;\n waitForExit: Effect.Effect<ExitCode, unknown>;\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}\n\nconst stripAnsi = (input: string): string => {\n const ESC = String.fromCharCode(27);\n const BEL = String.fromCharCode(7);\n return input\n .replace(new RegExp(`${ESC}\\\\][^${BEL}]*${BEL}`, \"g\"), \"\")\n .replace(new RegExp(`${ESC}\\\\[[0-?]*[ -/]*[@-~]`, \"g\"), \"\");\n};\n\nconst probeHttpOk = (url: string, timeoutMs = 400) =>\n Effect.tryPromise({\n try: async () => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n },\n catch: () => false,\n });\n\nconst LOCAL_PROBE_DEADLINE_MS = 90_000;\nconst LOCAL_PROBE_INTERVAL_MS = 200;\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\nconst 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 errorPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"error\", isError: true };\n }\n }\n for (const pattern of readyPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"ready\", isError: false };\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}\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) parts.push(`(cause: ${String(arg.cause)})`);\n if (isError && arg.stack) parts.push(arg.stack);\n return parts.join(\"\\n\");\n }\n return typeof arg === \"object\" ? JSON.stringify(arg, null, 2) : String(arg);\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\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* Effect.fail(new Error(\"remoteUrl not provided on host descriptor\"));\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: (e) => new Error(`Failed to load MF runtime: ${e}`),\n });\n\n const mfCore = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/runtime-core\"),\n catch: (e) => new Error(`Failed to load MF core: ${e}`),\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 const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 10_000);\n let res: Response;\n try {\n res = await fetch(manifestUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timer);\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 {}\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: (e) => new Error(`Failed to load host module: ${e}`),\n });\n\n if (!hostModule?.runServer) {\n return yield* Effect.fail(new Error(\"Host module does not export runServer function\"));\n }\n\n callbacks.onLog(descriptor.key, \"Starting server...\");\n const serverHandle = hostModule.runServer({ config: runtimeConfig });\n yield* Effect.tryPromise({\n try: () => serverHandle.ready,\n catch: (e) => new Error(`Server failed to start: ${e}`),\n });\n\n callbacks.onStatus(descriptor.key, \"ready\");\n\n return {\n name: descriptor.key,\n pid: process.pid,\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.succeed(undefined),\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\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* Effect.fail(new Error(`No localPath for local service: ${descriptor.key}`));\n }\n\n const fullCwd = descriptor.localPath;\n const command = descriptor.command ?? \"bun\";\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, Error>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n callbacks.onStatus(name, \"starting\");\n\n const envVars: Record<string, string> = {\n ...(process.env as Record<string, string>),\n FORCE_COLOR: \"1\",\n ...(port > 0 ? { PORT: String(port) } : {}),\n };\n\n if (name === \"host\") {\n envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);\n }\n\n const cmd = Command.make(command, ...args).pipe(\n Command.workingDirectory(fullCwd),\n Command.env(envVars),\n );\n\n const proc = yield* Command.start(cmd);\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(readyDeferred, new Error(message)).pipe(Effect.ignore);\n });\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const deadline = Date.now() + LOCAL_PROBE_DEADLINE_MS;\n\n if (port > 0) {\n const readinessPath = descriptor.readinessPath;\n const url = `http://127.0.0.1:${port}${readinessPath}`;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n const ok = yield* probeHttpOk(url);\n if (ok) {\n yield* markReady;\n return;\n }\n yield* Effect.sleep(`${LOCAL_PROBE_INTERVAL_MS} millis`);\n }\n } else {\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n yield* Effect.sleep(\"500 millis\");\n }\n }\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 = Number(proc.pid);\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const exitCode = yield* proc.exitCode;\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);\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 !/^\\$/.test(cleanLine);\n callbacks.onLog(name, line, looksLikeError);\n\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n\n const detected = detectStatus(line, descriptor);\n if (detected) {\n if (detected.status === \"ready\") {\n yield* markReady;\n } else {\n yield* markError(`Process failed: ${name}`);\n }\n }\n });\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, false))(\n Stream.splitLines(Stream.decodeText(proc.stdout, \"utf-8\")),\n ),\n );\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, true))(\n Stream.splitLines(Stream.decodeText(proc.stderr, \"utf-8\")),\n ),\n );\n\n return {\n name,\n pid,\n kill: Effect.gen(function* () {\n const result = yield* proc.kill(\"SIGTERM\").pipe(Effect.timeout(\"3 seconds\"), Effect.option);\n if (Option.isNone(result)) {\n const pid = Number(proc.pid);\n yield* Effect.try(() => process.kill(-pid, \"SIGKILL\")).pipe(Effect.ignore);\n yield* Effect.sleep(\"250 millis\");\n }\n }).pipe(Effect.ignore),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: proc.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, Error>();\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(readyDeferred, new Error(`Remote ${pkg} unreachable`)).pipe(\n Effect.ignore,\n );\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 deadline = Date.now() + REMOTE_PROBE_DEADLINE_MS;\n let delay = REMOTE_PROBE_BACKOFF_INITIAL_MS;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n\n const ok = yield* probeHttpOk(probeUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (ok) {\n yield* markReady;\n return;\n }\n\n const fallbackOk = yield* probeHttpOk(entryUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (fallbackOk) {\n yield* markReady;\n return;\n }\n\n yield* Effect.sleep(`${delay} millis`);\n delay = Math.min(Math.round(delay * 1.5), REMOTE_PROBE_BACKOFF_MAX_MS);\n }\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(readyDeferred, new Error(\"Killed\")).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 };\n });\n}\n"],"mappings":";;;;;;;AAWA,QAAQ,GAAG,uBAAuB,WAAW;AAC3C,SAAQ,MAAM,uCAAuC,OAAO;EAC5D;AAEF,QAAQ,GAAG,sBAAsB,QAAQ;AACvC,SAAQ,MAAM,sCAAsC,IAAI;EACxD;AAyBF,MAAM,aAAa,UAA0B;CAC3C,MAAM,MAAM,OAAO,aAAa,GAAG;CACnC,MAAM,MAAM,OAAO,aAAa,EAAE;AAClC,QAAO,MACJ,QAAQ,IAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE,GAAG,CACzD,QAAQ,IAAI,OAAO,GAAG,IAAI,uBAAuB,IAAI,EAAE,GAAG;;AAG/D,MAAM,eAAe,KAAa,YAAY,QAC5CA,cAAO,WAAW;CAChB,KAAK,YAAY;EACf,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,UAAU;AAC7D,MAAI;AAEF,WAAO,MADW,MAAM,KAAK,EAAE,QAAQ,WAAW,QAAQ,CAAC,EAChD;UACL;AACN,UAAO;YACC;AACR,gBAAa,MAAM;;;CAGvB,aAAa;CACd,CAAC;AAEJ,MAAM,0BAA0B;AAChC,MAAM,0BAA0B;AAEhC,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AACjC,MAAM,kCAAkC;AACxC,MAAM,8BAA8B;AAEpC,MAAM,gBACJ,MACA,eACuD;CACvD,MAAM,YAAY,UAAU,KAAK;CACjC,MAAM,gBAAgB,WAAW,iBAAiB,EAAE;CACpD,MAAM,gBAAgB,WAAW,iBAAiB,EAAE;AACpD,MAAK,MAAM,WAAW,cACpB,KAAI,QAAQ,KAAK,UAAU,CACzB,QAAO;EAAE,QAAQ;EAAS,SAAS;EAAM;AAG7C,MAAK,MAAM,WAAW,cACpB,KAAI,QAAQ,KAAK,UAAU,CACzB,QAAO;EAAE,QAAQ;EAAS,SAAS;EAAO;AAG9C,QAAO;;AAYT,MAAM,gBAAgB,MAAc,cAA8C;CAChF,MAAM,cAAc,QAAQ;CAC5B,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,eAAe,QAAQ;CAC7B,MAAM,eAAe,QAAQ;CAE7B,MAAM,cAAc,MAAiB,UAAU,UAAkB;AAC/D,SAAO,KACJ,KAAK,QAAQ;AACZ,OAAI,eAAe,OAAO;IACxB,MAAM,QAAQ,CAAC,GAAG,IAAI,KAAK,IAAI,IAAI,UAAU;AAC7C,QAAI,IAAI,iBAAiB,MACvB,OAAM,KAAK,WAAW,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,QAAQ,GAAG;aACvD,IAAI,MAAO,OAAM,KAAK,WAAW,OAAO,IAAI,MAAM,CAAC,GAAG;AAC/D,QAAI,WAAW,IAAI,MAAO,OAAM,KAAK,IAAI,MAAM;AAC/C,WAAO,MAAM,KAAK,KAAK;;AAEzB,UAAO,OAAO,QAAQ,WAAW,KAAK,UAAU,KAAK,MAAM,EAAE,GAAG,OAAO,IAAI;IAC3E,CACD,KAAK,IAAI;;AAGd,SAAQ,OAAO,GAAG,SAAoB;AACpC,YAAU,MAAM,MAAM,WAAW,KAAK,EAAE,MAAM;;AAEhD,SAAQ,SAAS,GAAG,SAAoB;AACtC,YAAU,MAAM,MAAM,WAAW,MAAM,KAAK,EAAE,KAAK;;AAErD,SAAQ,QAAQ,GAAG,SAAoB;AACrC,YAAU,MAAM,MAAM,WAAW,KAAK,EAAE,MAAM;;AAEhD,SAAQ,QAAQ,GAAG,SAAoB;AACrC,YAAU,MAAM,MAAM,WAAW,KAAK,EAAE,MAAM;;AAGhD,cAAa;AACX,UAAQ,MAAM;AACd,UAAQ,QAAQ;AAChB,UAAQ,OAAO;AACf,UAAQ,OAAO;;;AAInB,MAAM,mBAAmB,YAA+B,cACtDA,cAAO,IAAI,aAAa;CACtB,MAAM,gBAAgB,OAAOC;CAC7B,MAAM,YAAY,WAAW;AAC7B,KAAI,CAAC,UACH,QAAO,OAAOD,cAAO,qBAAK,IAAI,MAAM,4CAA4C,CAAC;AAGnF,WAAU,SAAS,WAAW,KAAK,WAAW;AAC9C,WAAU,MAAM,WAAW,KAAK,WAAW,YAAY;CACvD,MAAM,iBAAiB,aAAa,WAAW,KAAK,UAAU;AAC9D,WAAU,MAAM,WAAW,KAAK,uCAAuC;CAEvE,MAAM,YAAY,OAAOA,cAAO,WAAW;EACzC,WAAW,OAAO;EAClB,QAAQ,sBAAM,IAAI,MAAM,8BAA8B,IAAI;EAC3D,CAAC;CAEF,MAAM,SAAS,OAAOA,cAAO,WAAW;EACtC,WAAW,OAAO;EAClB,QAAQ,sBAAM,IAAI,MAAM,2BAA2B,IAAI;EACxD,CAAC;CAEF,IAAI,KAAK,UAAU,aAAa;AAChC,KAAI,CAAC,IAAI;AACP,OAAK,UAAU,eAAe;GAAE,MAAM;GAAY,SAAS,EAAE;GAAE,CAAC;AAChE,SAAO,4BAA4B,GAAG;;AAExC,+CAAmC,GAAU;CAE7C,MAAM,UAAU,UACb,QAAQ,sBAAsB,GAAG,CACjC,QAAQ,wBAAwB,GAAG,CACnC,QAAQ,OAAO,GAAG;CACrB,MAAM,iBAAiB,GAAG,QAAQ;CAClC,MAAM,cAAc,GAAG,QAAQ;CAE/B,MAAM,WAAW,OAAOA,cAAO,WAAW;EACxC,KAAK,YAAY;AACf,OAAI;IACF,MAAM,aAAa,IAAI,iBAAiB;IACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,IAAO;IAC1D,IAAI;AACJ,QAAI;AACF,WAAM,MAAM,MAAM,aAAa,EAAE,QAAQ,WAAW,QAAQ,CAAC;cACrD;AACR,kBAAa,MAAM;;AAErB,QAAI,CAAC,IAAI,GAAI,QAAO;IACpB,MAAM,OAAQ,MAAM,IAAI,MAAM;AAC9B,QACE,QACA,OAAO,SAAS,YAChB,cAAc,QACd,aAAa,QACb,YAAY,KAEZ,QAAO;WAEH;AACR,UAAO;;EAET,aAAa;EACd,CAAC;AAEF,CAAC,GAAW,gBAAgB,CAAC;EAAE,MAAM;EAAQ,OAAO;EAAU,CAAC,CAAC;AAChE,WAAU,MAAM,WAAW,KAAK,qBAAqB,SAAS,KAAK;CAEnE,MAAM,aAAa,OAAOA,cAAO,WAAW;EAC1C,WACG,GAAW,WAAW,cAAc;EAGvC,QAAQ,sBAAM,IAAI,MAAM,+BAA+B,IAAI;EAC5D,CAAC;AAEF,KAAI,CAAC,YAAY,UACf,QAAO,OAAOA,cAAO,qBAAK,IAAI,MAAM,iDAAiD,CAAC;AAGxF,WAAU,MAAM,WAAW,KAAK,qBAAqB;CACrD,MAAM,eAAe,WAAW,UAAU,EAAE,QAAQ,eAAe,CAAC;AACpE,QAAOA,cAAO,WAAW;EACvB,WAAW,aAAa;EACxB,QAAQ,sBAAM,IAAI,MAAM,2BAA2B,IAAI;EACxD,CAAC;AAEF,WAAU,SAAS,WAAW,KAAK,QAAQ;AAE3C,QAAO;EACL,MAAM,WAAW;EACjB,KAAK,QAAQ;EACb,MAAMA,cAAO,IAAI,aAAa;AAC5B,aAAU,MAAM,WAAW,KAAK,+BAA+B;AAC/D,mBAAgB;AAChB,UAAOA,cAAO,WAAW;IACvB,WAAW,aAAa,UAAU;IAClC,aAAa;IACd,CAAC,CAAC,KAAKA,cAAO,OAAO;IACtB;EACF,cAAcA,cAAO,QAAQ,OAAU;EACvC,aAAaA,cAAO;EACrB;EACD;AAEJ,MAAM,mBAAmB,YAA+B,cACtDA,cAAO,IAAI,aAAa;CACtB,MAAM,gBAAgB,OAAOC;AAE7B,KAAI,CAAC,WAAW,UACd,QAAO,OAAOD,cAAO,qBAAK,IAAI,MAAM,mCAAmC,WAAW,MAAM,CAAC;CAG3F,MAAM,UAAU,WAAW;CAC3B,MAAM,UAAU,WAAW,WAAW;CACtC,MAAM,OAAO,WAAW,QAAQ,CAAC,OAAO,MAAM;CAC9C,MAAM,OAAO,WAAW,QAAQ,WAAW;CAC3C,MAAM,OAAO,WAAW;CAExB,MAAM,gBAAgB,OAAOE,gBAAS,MAAmB;CACzD,MAAM,YAAY,OAAOC,WAAI,KAAoB,WAAW;AAE5D,WAAU,SAAS,MAAM,WAAW;CAEpC,MAAM,UAAkC;EACtC,GAAI,QAAQ;EACZ,aAAa;EACb,GAAI,OAAO,IAAI,EAAE,MAAM,OAAO,KAAK,EAAE,GAAG,EAAE;EAC3C;AAED,KAAI,SAAS,OACX,SAAQ,qBAAqB,KAAK,UAAU,cAAc;CAG5D,MAAM,MAAMC,yBAAQ,KAAK,SAAS,GAAG,KAAK,CAAC,KACzCA,yBAAQ,iBAAiB,QAAQ,EACjCA,yBAAQ,IAAI,QAAQ,CACrB;CAED,MAAM,OAAO,OAAOA,yBAAQ,MAAM,IAAI;CAEtC,MAAM,YAAYJ,cAAO,IAAI,aAAa;EACxC,MAAM,gBAAgB,OAAOG,WAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,SAAOA,WAAI,IAAI,WAAW,QAAQ;AAClC,YAAU,SAAS,MAAM,QAAQ;AACjC,SAAOD,gBAAS,QAAQ,eAAe,OAAU,CAAC,KAAKF,cAAO,OAAO;GACrE;CAEF,MAAM,aAAa,YACjBA,cAAO,IAAI,aAAa;EACtB,MAAM,gBAAgB,OAAOG,WAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,SAAOA,WAAI,IAAI,WAAW,QAAQ;AAClC,YAAU,SAAS,MAAM,QAAQ;AACjC,SAAOD,gBAAS,KAAK,eAAe,IAAI,MAAM,QAAQ,CAAC,CAAC,KAAKF,cAAO,OAAO;GAC3E;AAEJ,QAAOA,cAAO,WACZA,cAAO,IAAI,aAAa;EACtB,MAAM,WAAW,KAAK,KAAK,GAAG;AAE9B,MAAI,OAAO,GAAG;GAEZ,MAAM,MAAM,oBAAoB,OADV,WAAW;AAEjC,UAAO,KAAK,KAAK,GAAG,UAAU;IAC5B,MAAM,SAAS,OAAOG,WAAI,IAAI,UAAU;AACxC,QAAI,WAAW,WAAW,WAAW,QAAS;AAE9C,QAAI,OADc,YAAY,IAAI,EAC1B;AACN,YAAO;AACP;;AAEF,WAAOH,cAAO,MAAM,GAAG,wBAAwB,SAAS;;QAG1D,QAAO,KAAK,KAAK,GAAG,UAAU;GAC5B,MAAM,SAAS,OAAOG,WAAI,IAAI,UAAU;AACxC,OAAI,WAAW,WAAW,WAAW,QAAS;AAC9C,UAAOH,cAAO,MAAM,aAAa;;EAIrC,MAAM,SAAS,OAAOG,WAAI,IAAI,UAAU;AACxC,MAAI,WAAW,WAAW,WAAW,SAAS;AAC5C,aAAU,MAAM,MAAM,qCAAqC,KAAK;AAChE,UAAO,UAAU,4BAA4B,OAAO;;GAEtD,CACH;CAED,MAAM,MAAM,OAAO,KAAK,IAAI;AAE5B,QAAOH,cAAO,WACZA,cAAO,IAAI,aAAa;EACtB,MAAM,WAAW,OAAO,KAAK;EAC7B,MAAM,gBAAgB,OAAOG,WAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,YAAU,MAAM,MAAM,2CAA2C,SAAS,IAAI,KAAK;AACnF,SAAO,UAAU,gCAAgC,OAAO;GACxD,CACH;CAED,MAAM,cAAc,MAAc,aAChCH,cAAO,IAAI,aAAa;AACtB,MAAI,CAAC,KAAK,MAAM,CAAE;EAElB,MAAM,YAAY,UAAU,KAAK;EACjC,MAAM,iBACJ,YACA,kDAAkD,KAAK,UAAU,IACjE,CAAC,MAAM,KAAK,UAAU;AACxB,YAAU,MAAM,MAAM,MAAM,eAAe;EAE3C,MAAM,gBAAgB,OAAOG,WAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;EAE5D,MAAM,WAAW,aAAa,MAAM,WAAW;AAC/C,MAAI,SACF,KAAI,SAAS,WAAW,QACtB,QAAO;MAEP,QAAO,UAAU,mBAAmB,OAAO;GAG/C;AAEJ,QAAOH,cAAO,WACZK,cAAO,YAAY,SAAiB,WAAW,MAAM,MAAM,CAAC,CAC1DA,cAAO,WAAWA,cAAO,WAAW,KAAK,QAAQ,QAAQ,CAAC,CAC3D,CACF;AAED,QAAOL,cAAO,WACZK,cAAO,YAAY,SAAiB,WAAW,MAAM,KAAK,CAAC,CACzDA,cAAO,WAAWA,cAAO,WAAW,KAAK,QAAQ,QAAQ,CAAC,CAC3D,CACF;AAED,QAAO;EACL;EACA;EACA,MAAML,cAAO,IAAI,aAAa;GAC5B,MAAM,SAAS,OAAO,KAAK,KAAK,UAAU,CAAC,KAAKA,cAAO,QAAQ,YAAY,EAAEA,cAAO,OAAO;AAC3F,OAAIM,cAAO,OAAO,OAAO,EAAE;IACzB,MAAM,MAAM,OAAO,KAAK,IAAI;AAC5B,WAAON,cAAO,UAAU,QAAQ,KAAK,CAAC,KAAK,UAAU,CAAC,CAAC,KAAKA,cAAO,OAAO;AAC1E,WAAOA,cAAO,MAAM,aAAa;;IAEnC,CAAC,KAAKA,cAAO,OAAO;EACtB,cAAcE,gBAAS,MAAM,cAAc;EAC3C,aAAa,KAAK;EACnB;EACD;AAEJ,MAAM,oBACJ,KACA,YACA,cAEAF,cAAO,IAAI,aAAa;AACtB,WAAU,SAAS,KAAK,WAAW;CACnC,MAAM,gBAAgB,OAAOE,gBAAS,MAAmB;CACzD,MAAM,YAAY,OAAOC,WAAI,KAAoB,WAAW;CAE5D,MAAM,YAAYH,cAAO,IAAI,aAAa;EACxC,MAAM,gBAAgB,OAAOG,WAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,SAAOA,WAAI,IAAI,WAAW,QAAQ;AAClC,SAAOD,gBAAS,QAAQ,eAAe,OAAU,CAAC,KAAKF,cAAO,OAAO;AACrE,YAAU,SAAS,KAAK,SAAS,SAAS;GAC1C;CAEF,MAAM,YAAYA,cAAO,IAAI,aAAa;EACxC,MAAM,gBAAgB,OAAOG,WAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,SAAOA,WAAI,IAAI,WAAW,QAAQ;AAClC,SAAOD,gBAAS,KAAK,+BAAe,IAAI,MAAM,UAAU,IAAI,cAAc,CAAC,CAAC,KAC1EF,cAAO,OACR;AACD,YAAU,SAAS,KAAK,SAAS,cAAc;GAC/C;CAEF,MAAM,UAAU,WAAW,IAAI,QAAQ,OAAO,GAAG;CACjD,MAAM,cAAc,GAAG,QAAQ;CAC/B,MAAM,WAAW,GAAG,UAAU,WAAW;CACzC,MAAM,WAAW,WAAW,kBAAkB,YAAY,GAAG,QAAQ,WAAW;AAEhF,QAAOA,cAAO,WACZA,cAAO,IAAI,aAAa;EACtB,MAAM,WAAW,KAAK,KAAK,GAAG;EAC9B,IAAI,QAAQ;AACZ,SAAO,KAAK,KAAK,GAAG,UAAU;GAC5B,MAAM,SAAS,OAAOG,WAAI,IAAI,UAAU;AACxC,OAAI,WAAW,WAAW,WAAW,QAAS;AAI9C,OAAI,OAFc,YAAY,UAAU,wBAAwB,EAExD;AACN,WAAO;AACP;;AAKF,OAAI,OAFsB,YAAY,UAAU,wBAAwB,EAExD;AACd,WAAO;AACP;;AAGF,UAAOH,cAAO,MAAM,GAAG,MAAM,SAAS;AACtC,WAAQ,KAAK,IAAI,KAAK,MAAM,QAAQ,IAAI,EAAE,4BAA4B;;AAIxE,OAAI,OADkBG,WAAI,IAAI,UAAU,MACzB,QACb,QAAO;GAET,CACH;AAED,QAAO;EACL,MAAM;EACN,KAAK;EACL,MAAMH,cAAO,IAAI,aAAa;AAC5B,UAAOG,WAAI,IAAI,WAAW,QAAQ;AAClC,UAAOD,gBAAS,KAAK,+BAAe,IAAI,MAAM,SAAS,CAAC,CAAC,KAAKF,cAAO,OAAO;IAC5E;EACF,cAAcE,gBAAS,MAAM,cAAc;EAC3C,aAAaF,cAAO;EACrB;EACD;AAEJ,MAAa,kBAAkB,KAAa,WAA6B,iBACvEA,cAAO,IAAI,aAAa;CAEtB,MAAM,cAAa,OADKO,iDACI,IAAI,IAAI;AAEpC,KAAI,CAAC,YAAY;AACf,YAAU,SAAS,KAAK,SAAS,SAAS;AAC1C,SAAO;GACL,MAAM;GACN,KAAK;GACL,MAAMP,cAAO;GACb,cAAcA,cAAO;GACrB,aAAaA,cAAO;GACrB;;AAGH,KAAI,QAAQ,UAAU,WAAW,WAAW,SAC1C,QAAO,OAAO,gBAAgB,YAAY,UAAU;AAGtD,KAAI,WAAW,WAAW,YAAY,CAAC,WAAW,UAChD,QAAO,OAAO,iBAAiB,KAAK,YAAY,UAAU;AAK5D,QAAO,OAAO,gBAFa,eAAe;EAAE,GAAG;EAAY,MAAM;EAAc,GAAG,YAEhC,UAAU;EAC5D;AAEJ,SAAgB,iBACd,UACA,UACA,cACgB;AAChB,QAAO,SAAS,KAAK,QAAQ;EAC3B,MAAM,aAAa,SAAS,IAAI,IAAI;AACpC,SAAO;GACL,MAAM;GACN,QAAQ;GACR,MACE,gBAAgB,QAAQ,SACpB,eACC,YAAY,QAAQ,YAAY,eAAe;GACtD,QAAQ,YAAY;GACrB;GACD"}
@@ -29,6 +29,12 @@ const probeHttpOk = (url, timeoutMs = 400) => Effect.tryPromise({
29
29
  },
30
30
  catch: () => false
31
31
  });
32
+ const LOCAL_PROBE_DEADLINE_MS = 9e4;
33
+ const LOCAL_PROBE_INTERVAL_MS = 200;
34
+ const REMOTE_PROBE_TIMEOUT_MS = 5e3;
35
+ const REMOTE_PROBE_DEADLINE_MS = 6e4;
36
+ const REMOTE_PROBE_BACKOFF_INITIAL_MS = 1e3;
37
+ const REMOTE_PROBE_BACKOFF_MAX_MS = 15e3;
32
38
  const detectStatus = (line, descriptor) => {
33
39
  const cleanLine = stripAnsi(line);
34
40
  const errorPatterns = descriptor.errorPatterns ?? [];
@@ -184,10 +190,17 @@ const spawnDevProcess = (descriptor, callbacks) => Effect.gen(function* () {
184
190
  callbacks.onStatus(name, "ready");
185
191
  yield* Deferred.succeed(readyDeferred, void 0).pipe(Effect.ignore);
186
192
  });
187
- if (port > 0) {
188
- const url = `http://127.0.0.1:${port}${descriptor.readinessPath}`;
189
- yield* Effect.forkScoped(Effect.gen(function* () {
190
- const deadline = Date.now() + 9e4;
193
+ const markError = (message) => Effect.gen(function* () {
194
+ const currentStatus = yield* Ref.get(statusRef);
195
+ if (currentStatus === "ready" || currentStatus === "error") return;
196
+ yield* Ref.set(statusRef, "error");
197
+ callbacks.onStatus(name, "error");
198
+ yield* Deferred.fail(readyDeferred, new Error(message)).pipe(Effect.ignore);
199
+ });
200
+ yield* Effect.forkScoped(Effect.gen(function* () {
201
+ const deadline = Date.now() + LOCAL_PROBE_DEADLINE_MS;
202
+ if (port > 0) {
203
+ const url = `http://127.0.0.1:${port}${descriptor.readinessPath}`;
191
204
  while (Date.now() < deadline) {
192
205
  const status = yield* Ref.get(statusRef);
193
206
  if (status === "ready" || status === "error") return;
@@ -195,19 +208,26 @@ const spawnDevProcess = (descriptor, callbacks) => Effect.gen(function* () {
195
208
  yield* markReady;
196
209
  return;
197
210
  }
198
- yield* Effect.sleep("200 millis");
211
+ yield* Effect.sleep(`${LOCAL_PROBE_INTERVAL_MS} millis`);
199
212
  }
200
- }));
201
- }
213
+ } else while (Date.now() < deadline) {
214
+ const status = yield* Ref.get(statusRef);
215
+ if (status === "ready" || status === "error") return;
216
+ yield* Effect.sleep("500 millis");
217
+ }
218
+ const status = yield* Ref.get(statusRef);
219
+ if (status !== "ready" && status !== "error") {
220
+ callbacks.onLog(name, "Probe deadline exceeded after 90s", true);
221
+ yield* markError(`Probe deadline exceeded: ${name}`);
222
+ }
223
+ }));
202
224
  const pid = Number(proc.pid);
203
225
  yield* Effect.forkScoped(Effect.gen(function* () {
204
226
  const exitCode = yield* proc.exitCode;
205
227
  const currentStatus = yield* Ref.get(statusRef);
206
228
  if (currentStatus === "ready" || currentStatus === "error") return;
207
229
  callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);
208
- yield* Ref.set(statusRef, "error");
209
- callbacks.onStatus(name, "error");
210
- yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process exited before ready: ${name}`)).pipe(Effect.ignore);
230
+ yield* markError(`Process exited before ready: ${name}`);
211
231
  }));
212
232
  const handleLine = (line, isStderr) => Effect.gen(function* () {
213
233
  if (!line.trim()) return;
@@ -217,12 +237,8 @@ const spawnDevProcess = (descriptor, callbacks) => Effect.gen(function* () {
217
237
  const currentStatus = yield* Ref.get(statusRef);
218
238
  if (currentStatus === "ready" || currentStatus === "error") return;
219
239
  const detected = detectStatus(line, descriptor);
220
- if (detected) {
221
- yield* Ref.set(statusRef, detected.status);
222
- callbacks.onStatus(name, detected.status);
223
- if (detected.status === "ready" || detected.status === "error") if (detected.status === "ready") yield* Deferred.succeed(readyDeferred, void 0).pipe(Effect.ignore);
224
- else yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process failed: ${name}`)).pipe(Effect.ignore);
225
- }
240
+ if (detected) if (detected.status === "ready") yield* markReady;
241
+ else yield* markError(`Process failed: ${name}`);
226
242
  });
227
243
  yield* Effect.forkScoped(Stream.runForEach((line) => handleLine(line, false))(Stream.splitLines(Stream.decodeText(proc.stdout, "utf-8"))));
228
244
  yield* Effect.forkScoped(Stream.runForEach((line) => handleLine(line, true))(Stream.splitLines(Stream.decodeText(proc.stderr, "utf-8"))));
@@ -246,25 +262,25 @@ const spawnRemoteProbe = (pkg, descriptor, callbacks) => Effect.gen(function* ()
246
262
  const readyDeferred = yield* Deferred.make();
247
263
  const statusRef = yield* Ref.make("starting");
248
264
  const markReady = Effect.gen(function* () {
265
+ const currentStatus = yield* Ref.get(statusRef);
266
+ if (currentStatus === "ready" || currentStatus === "error") return;
249
267
  yield* Ref.set(statusRef, "ready");
250
- yield* Deferred.succeed(readyDeferred, void 0);
268
+ yield* Deferred.succeed(readyDeferred, void 0).pipe(Effect.ignore);
251
269
  callbacks.onStatus(pkg, "ready", "loaded");
252
270
  });
253
271
  const markError = Effect.gen(function* () {
272
+ const currentStatus = yield* Ref.get(statusRef);
273
+ if (currentStatus === "ready" || currentStatus === "error") return;
254
274
  yield* Ref.set(statusRef, "error");
255
- yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Remote ${pkg} unreachable`));
275
+ yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Remote ${pkg} unreachable`)).pipe(Effect.ignore);
256
276
  callbacks.onStatus(pkg, "error", "unreachable");
257
277
  });
258
278
  const baseUrl = descriptor.url.replace(/\/$/, "");
259
279
  const manifestUrl = `${baseUrl}/mf-manifest.json`;
260
280
  const entryUrl = `${baseUrl}${descriptor.readinessPath}`;
261
281
  const probeUrl = descriptor.readinessPath === "/health" ? `${baseUrl}/health` : manifestUrl;
262
- const REMOTE_PROBE_TIMEOUT_MS = 5e3;
263
- const REMOTE_PROBE_BACKOFF_INITIAL_MS = 1e3;
264
- const REMOTE_PROBE_BACKOFF_MAX_MS = 15e3;
265
282
  yield* Effect.forkScoped(Effect.gen(function* () {
266
- yield* Effect.sleep(`${Math.floor(Math.random() * 2e3)} millis`);
267
- const deadline = Date.now() + 6e4;
283
+ const deadline = Date.now() + REMOTE_PROBE_DEADLINE_MS;
268
284
  let delay = REMOTE_PROBE_BACKOFF_INITIAL_MS;
269
285
  while (Date.now() < deadline) {
270
286
  const status = yield* Ref.get(statusRef);
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.mjs","names":[],"sources":["../src/orchestrator.ts"],"sourcesContent":["import { Command } from \"@effect/platform\";\nimport type { ExitCode } from \"@effect/platform/CommandExecutor\";\nimport { Deferred, Effect, Option, Ref, Stream } from \"effect\";\nimport { patchManifestFetchForSsrPublicPath } from \"./mf\";\nimport {\n DevRuntimeConfig,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\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, unknown>;\n waitForReady: Effect.Effect<void, Error>;\n waitForExit: Effect.Effect<ExitCode, unknown>;\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}\n\nconst stripAnsi = (input: string): string => {\n const ESC = String.fromCharCode(27);\n const BEL = String.fromCharCode(7);\n return input\n .replace(new RegExp(`${ESC}\\\\][^${BEL}]*${BEL}`, \"g\"), \"\")\n .replace(new RegExp(`${ESC}\\\\[[0-?]*[ -/]*[@-~]`, \"g\"), \"\");\n};\n\nconst probeHttpOk = (url: string, timeoutMs = 400) =>\n Effect.tryPromise({\n try: async () => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n },\n catch: () => false,\n });\n\nconst 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 errorPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"error\", isError: true };\n }\n }\n for (const pattern of readyPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"ready\", isError: false };\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}\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) parts.push(`(cause: ${String(arg.cause)})`);\n if (isError && arg.stack) parts.push(arg.stack);\n return parts.join(\"\\n\");\n }\n return typeof arg === \"object\" ? JSON.stringify(arg, null, 2) : String(arg);\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\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* Effect.fail(new Error(\"remoteUrl not provided on host descriptor\"));\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: (e) => new Error(`Failed to load MF runtime: ${e}`),\n });\n\n const mfCore = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/runtime-core\"),\n catch: (e) => new Error(`Failed to load MF core: ${e}`),\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 const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 10_000);\n let res: Response;\n try {\n res = await fetch(manifestUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timer);\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 {}\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: (e) => new Error(`Failed to load host module: ${e}`),\n });\n\n if (!hostModule?.runServer) {\n return yield* Effect.fail(new Error(\"Host module does not export runServer function\"));\n }\n\n callbacks.onLog(descriptor.key, \"Starting server...\");\n const serverHandle = hostModule.runServer({ config: runtimeConfig });\n yield* Effect.tryPromise({\n try: () => serverHandle.ready,\n catch: (e) => new Error(`Server failed to start: ${e}`),\n });\n\n callbacks.onStatus(descriptor.key, \"ready\");\n\n return {\n name: descriptor.key,\n pid: process.pid,\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.succeed(undefined),\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\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* Effect.fail(new Error(`No localPath for local service: ${descriptor.key}`));\n }\n\n const fullCwd = descriptor.localPath;\n const command = descriptor.command ?? \"bun\";\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, Error>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n callbacks.onStatus(name, \"starting\");\n\n const envVars: Record<string, string> = {\n ...(process.env as Record<string, string>),\n FORCE_COLOR: \"1\",\n ...(port > 0 ? { PORT: String(port) } : {}),\n };\n\n if (name === \"host\") {\n envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);\n }\n\n const cmd = Command.make(command, ...args).pipe(\n Command.workingDirectory(fullCwd),\n Command.env(envVars),\n );\n\n const proc = yield* Command.start(cmd);\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 if (port > 0) {\n const readinessPath = descriptor.readinessPath;\n const url = `http://127.0.0.1:${port}${readinessPath}`;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const deadline = Date.now() + 90_000;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n const ok = yield* probeHttpOk(url);\n if (ok) {\n yield* markReady;\n return;\n }\n yield* Effect.sleep(\"200 millis\");\n }\n }),\n );\n }\n\n const pid = Number(proc.pid);\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const exitCode = yield* proc.exitCode;\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);\n yield* Ref.set(statusRef, \"error\");\n callbacks.onStatus(name, \"error\");\n yield* Deferred.fail(readyDeferred, new Error(`Process exited before ready: ${name}`)).pipe(\n Effect.ignore,\n );\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 !/^\\$/.test(cleanLine);\n callbacks.onLog(name, line, looksLikeError);\n\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n\n const detected = detectStatus(line, descriptor);\n if (detected) {\n yield* Ref.set(statusRef, detected.status);\n callbacks.onStatus(name, detected.status);\n if (detected.status === \"ready\" || detected.status === \"error\") {\n if (detected.status === \"ready\") {\n yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);\n } else {\n yield* Deferred.fail(readyDeferred, new Error(`Process failed: ${name}`)).pipe(\n Effect.ignore,\n );\n }\n }\n }\n });\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, false))(\n Stream.splitLines(Stream.decodeText(proc.stdout, \"utf-8\")),\n ),\n );\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, true))(\n Stream.splitLines(Stream.decodeText(proc.stderr, \"utf-8\")),\n ),\n );\n\n return {\n name,\n pid,\n kill: Effect.gen(function* () {\n const result = yield* proc.kill(\"SIGTERM\").pipe(Effect.timeout(\"3 seconds\"), Effect.option);\n if (Option.isNone(result)) {\n const pid = Number(proc.pid);\n yield* Effect.try(() => process.kill(-pid, \"SIGKILL\")).pipe(Effect.ignore);\n yield* Effect.sleep(\"250 millis\");\n }\n }).pipe(Effect.ignore),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: proc.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, Error>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n const markReady = Effect.gen(function* () {\n yield* Ref.set(statusRef, \"ready\");\n yield* Deferred.succeed(readyDeferred, undefined);\n callbacks.onStatus(pkg, \"ready\", \"loaded\");\n });\n\n const markError = Effect.gen(function* () {\n yield* Ref.set(statusRef, \"error\");\n yield* Deferred.fail(readyDeferred, new Error(`Remote ${pkg} unreachable`));\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 const REMOTE_PROBE_TIMEOUT_MS = 5000;\n const REMOTE_PROBE_BACKOFF_INITIAL_MS = 1000;\n const REMOTE_PROBE_BACKOFF_MAX_MS = 15_000;\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n yield* Effect.sleep(`${Math.floor(Math.random() * 2000)} millis`);\n\n const deadline = Date.now() + 60_000;\n let delay = REMOTE_PROBE_BACKOFF_INITIAL_MS;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n\n const ok = yield* probeHttpOk(probeUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (ok) {\n yield* markReady;\n return;\n }\n\n const fallbackOk = yield* probeHttpOk(entryUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (fallbackOk) {\n yield* markReady;\n return;\n }\n\n yield* Effect.sleep(`${delay} millis`);\n delay = Math.min(Math.round(delay * 1.5), REMOTE_PROBE_BACKOFF_MAX_MS);\n }\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(readyDeferred, new Error(\"Killed\")).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 };\n });\n}\n"],"mappings":";;;;;;AAWA,QAAQ,GAAG,uBAAuB,WAAW;AAC3C,SAAQ,MAAM,uCAAuC,OAAO;EAC5D;AAEF,QAAQ,GAAG,sBAAsB,QAAQ;AACvC,SAAQ,MAAM,sCAAsC,IAAI;EACxD;AAyBF,MAAM,aAAa,UAA0B;CAC3C,MAAM,MAAM,OAAO,aAAa,GAAG;CACnC,MAAM,MAAM,OAAO,aAAa,EAAE;AAClC,QAAO,MACJ,QAAQ,IAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE,GAAG,CACzD,QAAQ,IAAI,OAAO,GAAG,IAAI,uBAAuB,IAAI,EAAE,GAAG;;AAG/D,MAAM,eAAe,KAAa,YAAY,QAC5C,OAAO,WAAW;CAChB,KAAK,YAAY;EACf,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,UAAU;AAC7D,MAAI;AAEF,WAAO,MADW,MAAM,KAAK,EAAE,QAAQ,WAAW,QAAQ,CAAC,EAChD;UACL;AACN,UAAO;YACC;AACR,gBAAa,MAAM;;;CAGvB,aAAa;CACd,CAAC;AAEJ,MAAM,gBACJ,MACA,eACuD;CACvD,MAAM,YAAY,UAAU,KAAK;CACjC,MAAM,gBAAgB,WAAW,iBAAiB,EAAE;CACpD,MAAM,gBAAgB,WAAW,iBAAiB,EAAE;AACpD,MAAK,MAAM,WAAW,cACpB,KAAI,QAAQ,KAAK,UAAU,CACzB,QAAO;EAAE,QAAQ;EAAS,SAAS;EAAM;AAG7C,MAAK,MAAM,WAAW,cACpB,KAAI,QAAQ,KAAK,UAAU,CACzB,QAAO;EAAE,QAAQ;EAAS,SAAS;EAAO;AAG9C,QAAO;;AAYT,MAAM,gBAAgB,MAAc,cAA8C;CAChF,MAAM,cAAc,QAAQ;CAC5B,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,eAAe,QAAQ;CAC7B,MAAM,eAAe,QAAQ;CAE7B,MAAM,cAAc,MAAiB,UAAU,UAAkB;AAC/D,SAAO,KACJ,KAAK,QAAQ;AACZ,OAAI,eAAe,OAAO;IACxB,MAAM,QAAQ,CAAC,GAAG,IAAI,KAAK,IAAI,IAAI,UAAU;AAC7C,QAAI,IAAI,iBAAiB,MACvB,OAAM,KAAK,WAAW,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,QAAQ,GAAG;aACvD,IAAI,MAAO,OAAM,KAAK,WAAW,OAAO,IAAI,MAAM,CAAC,GAAG;AAC/D,QAAI,WAAW,IAAI,MAAO,OAAM,KAAK,IAAI,MAAM;AAC/C,WAAO,MAAM,KAAK,KAAK;;AAEzB,UAAO,OAAO,QAAQ,WAAW,KAAK,UAAU,KAAK,MAAM,EAAE,GAAG,OAAO,IAAI;IAC3E,CACD,KAAK,IAAI;;AAGd,SAAQ,OAAO,GAAG,SAAoB;AACpC,YAAU,MAAM,MAAM,WAAW,KAAK,EAAE,MAAM;;AAEhD,SAAQ,SAAS,GAAG,SAAoB;AACtC,YAAU,MAAM,MAAM,WAAW,MAAM,KAAK,EAAE,KAAK;;AAErD,SAAQ,QAAQ,GAAG,SAAoB;AACrC,YAAU,MAAM,MAAM,WAAW,KAAK,EAAE,MAAM;;AAEhD,SAAQ,QAAQ,GAAG,SAAoB;AACrC,YAAU,MAAM,MAAM,WAAW,KAAK,EAAE,MAAM;;AAGhD,cAAa;AACX,UAAQ,MAAM;AACd,UAAQ,QAAQ;AAChB,UAAQ,OAAO;AACf,UAAQ,OAAO;;;AAInB,MAAM,mBAAmB,YAA+B,cACtD,OAAO,IAAI,aAAa;CACtB,MAAM,gBAAgB,OAAO;CAC7B,MAAM,YAAY,WAAW;AAC7B,KAAI,CAAC,UACH,QAAO,OAAO,OAAO,qBAAK,IAAI,MAAM,4CAA4C,CAAC;AAGnF,WAAU,SAAS,WAAW,KAAK,WAAW;AAC9C,WAAU,MAAM,WAAW,KAAK,WAAW,YAAY;CACvD,MAAM,iBAAiB,aAAa,WAAW,KAAK,UAAU;AAC9D,WAAU,MAAM,WAAW,KAAK,uCAAuC;CAEvE,MAAM,YAAY,OAAO,OAAO,WAAW;EACzC,WAAW,OAAO;EAClB,QAAQ,sBAAM,IAAI,MAAM,8BAA8B,IAAI;EAC3D,CAAC;CAEF,MAAM,SAAS,OAAO,OAAO,WAAW;EACtC,WAAW,OAAO;EAClB,QAAQ,sBAAM,IAAI,MAAM,2BAA2B,IAAI;EACxD,CAAC;CAEF,IAAI,KAAK,UAAU,aAAa;AAChC,KAAI,CAAC,IAAI;AACP,OAAK,UAAU,eAAe;GAAE,MAAM;GAAY,SAAS,EAAE;GAAE,CAAC;AAChE,SAAO,4BAA4B,GAAG;;AAExC,oCAAmC,GAAU;CAE7C,MAAM,UAAU,UACb,QAAQ,sBAAsB,GAAG,CACjC,QAAQ,wBAAwB,GAAG,CACnC,QAAQ,OAAO,GAAG;CACrB,MAAM,iBAAiB,GAAG,QAAQ;CAClC,MAAM,cAAc,GAAG,QAAQ;CAE/B,MAAM,WAAW,OAAO,OAAO,WAAW;EACxC,KAAK,YAAY;AACf,OAAI;IACF,MAAM,aAAa,IAAI,iBAAiB;IACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,IAAO;IAC1D,IAAI;AACJ,QAAI;AACF,WAAM,MAAM,MAAM,aAAa,EAAE,QAAQ,WAAW,QAAQ,CAAC;cACrD;AACR,kBAAa,MAAM;;AAErB,QAAI,CAAC,IAAI,GAAI,QAAO;IACpB,MAAM,OAAQ,MAAM,IAAI,MAAM;AAC9B,QACE,QACA,OAAO,SAAS,YAChB,cAAc,QACd,aAAa,QACb,YAAY,KAEZ,QAAO;WAEH;AACR,UAAO;;EAET,aAAa;EACd,CAAC;AAEF,CAAC,GAAW,gBAAgB,CAAC;EAAE,MAAM;EAAQ,OAAO;EAAU,CAAC,CAAC;AAChE,WAAU,MAAM,WAAW,KAAK,qBAAqB,SAAS,KAAK;CAEnE,MAAM,aAAa,OAAO,OAAO,WAAW;EAC1C,WACG,GAAW,WAAW,cAAc;EAGvC,QAAQ,sBAAM,IAAI,MAAM,+BAA+B,IAAI;EAC5D,CAAC;AAEF,KAAI,CAAC,YAAY,UACf,QAAO,OAAO,OAAO,qBAAK,IAAI,MAAM,iDAAiD,CAAC;AAGxF,WAAU,MAAM,WAAW,KAAK,qBAAqB;CACrD,MAAM,eAAe,WAAW,UAAU,EAAE,QAAQ,eAAe,CAAC;AACpE,QAAO,OAAO,WAAW;EACvB,WAAW,aAAa;EACxB,QAAQ,sBAAM,IAAI,MAAM,2BAA2B,IAAI;EACxD,CAAC;AAEF,WAAU,SAAS,WAAW,KAAK,QAAQ;AAE3C,QAAO;EACL,MAAM,WAAW;EACjB,KAAK,QAAQ;EACb,MAAM,OAAO,IAAI,aAAa;AAC5B,aAAU,MAAM,WAAW,KAAK,+BAA+B;AAC/D,mBAAgB;AAChB,UAAO,OAAO,WAAW;IACvB,WAAW,aAAa,UAAU;IAClC,aAAa;IACd,CAAC,CAAC,KAAK,OAAO,OAAO;IACtB;EACF,cAAc,OAAO,QAAQ,OAAU;EACvC,aAAa,OAAO;EACrB;EACD;AAEJ,MAAM,mBAAmB,YAA+B,cACtD,OAAO,IAAI,aAAa;CACtB,MAAM,gBAAgB,OAAO;AAE7B,KAAI,CAAC,WAAW,UACd,QAAO,OAAO,OAAO,qBAAK,IAAI,MAAM,mCAAmC,WAAW,MAAM,CAAC;CAG3F,MAAM,UAAU,WAAW;CAC3B,MAAM,UAAU,WAAW,WAAW;CACtC,MAAM,OAAO,WAAW,QAAQ,CAAC,OAAO,MAAM;CAC9C,MAAM,OAAO,WAAW,QAAQ,WAAW;CAC3C,MAAM,OAAO,WAAW;CAExB,MAAM,gBAAgB,OAAO,SAAS,MAAmB;CACzD,MAAM,YAAY,OAAO,IAAI,KAAoB,WAAW;AAE5D,WAAU,SAAS,MAAM,WAAW;CAEpC,MAAM,UAAkC;EACtC,GAAI,QAAQ;EACZ,aAAa;EACb,GAAI,OAAO,IAAI,EAAE,MAAM,OAAO,KAAK,EAAE,GAAG,EAAE;EAC3C;AAED,KAAI,SAAS,OACX,SAAQ,qBAAqB,KAAK,UAAU,cAAc;CAG5D,MAAM,MAAM,QAAQ,KAAK,SAAS,GAAG,KAAK,CAAC,KACzC,QAAQ,iBAAiB,QAAQ,EACjC,QAAQ,IAAI,QAAQ,CACrB;CAED,MAAM,OAAO,OAAO,QAAQ,MAAM,IAAI;CAEtC,MAAM,YAAY,OAAO,IAAI,aAAa;EACxC,MAAM,gBAAgB,OAAO,IAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,SAAO,IAAI,IAAI,WAAW,QAAQ;AAClC,YAAU,SAAS,MAAM,QAAQ;AACjC,SAAO,SAAS,QAAQ,eAAe,OAAU,CAAC,KAAK,OAAO,OAAO;GACrE;AAEF,KAAI,OAAO,GAAG;EAEZ,MAAM,MAAM,oBAAoB,OADV,WAAW;AAGjC,SAAO,OAAO,WACZ,OAAO,IAAI,aAAa;GACtB,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,UAAO,KAAK,KAAK,GAAG,UAAU;IAC5B,MAAM,SAAS,OAAO,IAAI,IAAI,UAAU;AACxC,QAAI,WAAW,WAAW,WAAW,QAAS;AAE9C,QAAI,OADc,YAAY,IAAI,EAC1B;AACN,YAAO;AACP;;AAEF,WAAO,OAAO,MAAM,aAAa;;IAEnC,CACH;;CAGH,MAAM,MAAM,OAAO,KAAK,IAAI;AAE5B,QAAO,OAAO,WACZ,OAAO,IAAI,aAAa;EACtB,MAAM,WAAW,OAAO,KAAK;EAC7B,MAAM,gBAAgB,OAAO,IAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;AAC5D,YAAU,MAAM,MAAM,2CAA2C,SAAS,IAAI,KAAK;AACnF,SAAO,IAAI,IAAI,WAAW,QAAQ;AAClC,YAAU,SAAS,MAAM,QAAQ;AACjC,SAAO,SAAS,KAAK,+BAAe,IAAI,MAAM,gCAAgC,OAAO,CAAC,CAAC,KACrF,OAAO,OACR;GACD,CACH;CAED,MAAM,cAAc,MAAc,aAChC,OAAO,IAAI,aAAa;AACtB,MAAI,CAAC,KAAK,MAAM,CAAE;EAElB,MAAM,YAAY,UAAU,KAAK;EACjC,MAAM,iBACJ,YACA,kDAAkD,KAAK,UAAU,IACjE,CAAC,MAAM,KAAK,UAAU;AACxB,YAAU,MAAM,MAAM,MAAM,eAAe;EAE3C,MAAM,gBAAgB,OAAO,IAAI,IAAI,UAAU;AAC/C,MAAI,kBAAkB,WAAW,kBAAkB,QAAS;EAE5D,MAAM,WAAW,aAAa,MAAM,WAAW;AAC/C,MAAI,UAAU;AACZ,UAAO,IAAI,IAAI,WAAW,SAAS,OAAO;AAC1C,aAAU,SAAS,MAAM,SAAS,OAAO;AACzC,OAAI,SAAS,WAAW,WAAW,SAAS,WAAW,QACrD,KAAI,SAAS,WAAW,QACtB,QAAO,SAAS,QAAQ,eAAe,OAAU,CAAC,KAAK,OAAO,OAAO;OAErE,QAAO,SAAS,KAAK,+BAAe,IAAI,MAAM,mBAAmB,OAAO,CAAC,CAAC,KACxE,OAAO,OACR;;GAIP;AAEJ,QAAO,OAAO,WACZ,OAAO,YAAY,SAAiB,WAAW,MAAM,MAAM,CAAC,CAC1D,OAAO,WAAW,OAAO,WAAW,KAAK,QAAQ,QAAQ,CAAC,CAC3D,CACF;AAED,QAAO,OAAO,WACZ,OAAO,YAAY,SAAiB,WAAW,MAAM,KAAK,CAAC,CACzD,OAAO,WAAW,OAAO,WAAW,KAAK,QAAQ,QAAQ,CAAC,CAC3D,CACF;AAED,QAAO;EACL;EACA;EACA,MAAM,OAAO,IAAI,aAAa;GAC5B,MAAM,SAAS,OAAO,KAAK,KAAK,UAAU,CAAC,KAAK,OAAO,QAAQ,YAAY,EAAE,OAAO,OAAO;AAC3F,OAAI,OAAO,OAAO,OAAO,EAAE;IACzB,MAAM,MAAM,OAAO,KAAK,IAAI;AAC5B,WAAO,OAAO,UAAU,QAAQ,KAAK,CAAC,KAAK,UAAU,CAAC,CAAC,KAAK,OAAO,OAAO;AAC1E,WAAO,OAAO,MAAM,aAAa;;IAEnC,CAAC,KAAK,OAAO,OAAO;EACtB,cAAc,SAAS,MAAM,cAAc;EAC3C,aAAa,KAAK;EACnB;EACD;AAEJ,MAAM,oBACJ,KACA,YACA,cAEA,OAAO,IAAI,aAAa;AACtB,WAAU,SAAS,KAAK,WAAW;CACnC,MAAM,gBAAgB,OAAO,SAAS,MAAmB;CACzD,MAAM,YAAY,OAAO,IAAI,KAAoB,WAAW;CAE5D,MAAM,YAAY,OAAO,IAAI,aAAa;AACxC,SAAO,IAAI,IAAI,WAAW,QAAQ;AAClC,SAAO,SAAS,QAAQ,eAAe,OAAU;AACjD,YAAU,SAAS,KAAK,SAAS,SAAS;GAC1C;CAEF,MAAM,YAAY,OAAO,IAAI,aAAa;AACxC,SAAO,IAAI,IAAI,WAAW,QAAQ;AAClC,SAAO,SAAS,KAAK,+BAAe,IAAI,MAAM,UAAU,IAAI,cAAc,CAAC;AAC3E,YAAU,SAAS,KAAK,SAAS,cAAc;GAC/C;CAEF,MAAM,UAAU,WAAW,IAAI,QAAQ,OAAO,GAAG;CACjD,MAAM,cAAc,GAAG,QAAQ;CAC/B,MAAM,WAAW,GAAG,UAAU,WAAW;CACzC,MAAM,WAAW,WAAW,kBAAkB,YAAY,GAAG,QAAQ,WAAW;CAEhF,MAAM,0BAA0B;CAChC,MAAM,kCAAkC;CACxC,MAAM,8BAA8B;AAEpC,QAAO,OAAO,WACZ,OAAO,IAAI,aAAa;AACtB,SAAO,OAAO,MAAM,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAK,CAAC,SAAS;EAEjE,MAAM,WAAW,KAAK,KAAK,GAAG;EAC9B,IAAI,QAAQ;AACZ,SAAO,KAAK,KAAK,GAAG,UAAU;GAC5B,MAAM,SAAS,OAAO,IAAI,IAAI,UAAU;AACxC,OAAI,WAAW,WAAW,WAAW,QAAS;AAI9C,OAAI,OAFc,YAAY,UAAU,wBAAwB,EAExD;AACN,WAAO;AACP;;AAKF,OAAI,OAFsB,YAAY,UAAU,wBAAwB,EAExD;AACd,WAAO;AACP;;AAGF,UAAO,OAAO,MAAM,GAAG,MAAM,SAAS;AACtC,WAAQ,KAAK,IAAI,KAAK,MAAM,QAAQ,IAAI,EAAE,4BAA4B;;AAIxE,OAAI,OADkB,IAAI,IAAI,UAAU,MACzB,QACb,QAAO;GAET,CACH;AAED,QAAO;EACL,MAAM;EACN,KAAK;EACL,MAAM,OAAO,IAAI,aAAa;AAC5B,UAAO,IAAI,IAAI,WAAW,QAAQ;AAClC,UAAO,SAAS,KAAK,+BAAe,IAAI,MAAM,SAAS,CAAC,CAAC,KAAK,OAAO,OAAO;IAC5E;EACF,cAAc,SAAS,MAAM,cAAc;EAC3C,aAAa,OAAO;EACrB;EACD;AAEJ,MAAa,kBAAkB,KAAa,WAA6B,iBACvE,OAAO,IAAI,aAAa;CAEtB,MAAM,cAAa,OADK,sBACI,IAAI,IAAI;AAEpC,KAAI,CAAC,YAAY;AACf,YAAU,SAAS,KAAK,SAAS,SAAS;AAC1C,SAAO;GACL,MAAM;GACN,KAAK;GACL,MAAM,OAAO;GACb,cAAc,OAAO;GACrB,aAAa,OAAO;GACrB;;AAGH,KAAI,QAAQ,UAAU,WAAW,WAAW,SAC1C,QAAO,OAAO,gBAAgB,YAAY,UAAU;AAGtD,KAAI,WAAW,WAAW,YAAY,CAAC,WAAW,UAChD,QAAO,OAAO,iBAAiB,KAAK,YAAY,UAAU;AAK5D,QAAO,OAAO,gBAFa,eAAe;EAAE,GAAG;EAAY,MAAM;EAAc,GAAG,YAEhC,UAAU;EAC5D;AAEJ,SAAgB,iBACd,UACA,UACA,cACgB;AAChB,QAAO,SAAS,KAAK,QAAQ;EAC3B,MAAM,aAAa,SAAS,IAAI,IAAI;AACpC,SAAO;GACL,MAAM;GACN,QAAQ;GACR,MACE,gBAAgB,QAAQ,SACpB,eACC,YAAY,QAAQ,YAAY,eAAe;GACtD,QAAQ,YAAY;GACrB;GACD"}
1
+ {"version":3,"file":"orchestrator.mjs","names":[],"sources":["../src/orchestrator.ts"],"sourcesContent":["import { Command } from \"@effect/platform\";\nimport type { ExitCode } from \"@effect/platform/CommandExecutor\";\nimport { Deferred, Effect, Option, Ref, Stream } from \"effect\";\nimport { patchManifestFetchForSsrPublicPath } from \"./mf\";\nimport {\n DevRuntimeConfig,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\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, unknown>;\n waitForReady: Effect.Effect<void, Error>;\n waitForExit: Effect.Effect<ExitCode, unknown>;\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}\n\nconst stripAnsi = (input: string): string => {\n const ESC = String.fromCharCode(27);\n const BEL = String.fromCharCode(7);\n return input\n .replace(new RegExp(`${ESC}\\\\][^${BEL}]*${BEL}`, \"g\"), \"\")\n .replace(new RegExp(`${ESC}\\\\[[0-?]*[ -/]*[@-~]`, \"g\"), \"\");\n};\n\nconst probeHttpOk = (url: string, timeoutMs = 400) =>\n Effect.tryPromise({\n try: async () => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n },\n catch: () => false,\n });\n\nconst LOCAL_PROBE_DEADLINE_MS = 90_000;\nconst LOCAL_PROBE_INTERVAL_MS = 200;\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\nconst 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 errorPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"error\", isError: true };\n }\n }\n for (const pattern of readyPatterns) {\n if (pattern.test(cleanLine)) {\n return { status: \"ready\", isError: false };\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}\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) parts.push(`(cause: ${String(arg.cause)})`);\n if (isError && arg.stack) parts.push(arg.stack);\n return parts.join(\"\\n\");\n }\n return typeof arg === \"object\" ? JSON.stringify(arg, null, 2) : String(arg);\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\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* Effect.fail(new Error(\"remoteUrl not provided on host descriptor\"));\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: (e) => new Error(`Failed to load MF runtime: ${e}`),\n });\n\n const mfCore = yield* Effect.tryPromise({\n try: () => import(\"@module-federation/runtime-core\"),\n catch: (e) => new Error(`Failed to load MF core: ${e}`),\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 const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 10_000);\n let res: Response;\n try {\n res = await fetch(manifestUrl, { signal: controller.signal });\n } finally {\n clearTimeout(timer);\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 {}\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: (e) => new Error(`Failed to load host module: ${e}`),\n });\n\n if (!hostModule?.runServer) {\n return yield* Effect.fail(new Error(\"Host module does not export runServer function\"));\n }\n\n callbacks.onLog(descriptor.key, \"Starting server...\");\n const serverHandle = hostModule.runServer({ config: runtimeConfig });\n yield* Effect.tryPromise({\n try: () => serverHandle.ready,\n catch: (e) => new Error(`Server failed to start: ${e}`),\n });\n\n callbacks.onStatus(descriptor.key, \"ready\");\n\n return {\n name: descriptor.key,\n pid: process.pid,\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.succeed(undefined),\n waitForExit: Effect.never,\n } satisfies ProcessHandle;\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* Effect.fail(new Error(`No localPath for local service: ${descriptor.key}`));\n }\n\n const fullCwd = descriptor.localPath;\n const command = descriptor.command ?? \"bun\";\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, Error>();\n const statusRef = yield* Ref.make<ProcessStatus>(\"starting\");\n\n callbacks.onStatus(name, \"starting\");\n\n const envVars: Record<string, string> = {\n ...(process.env as Record<string, string>),\n FORCE_COLOR: \"1\",\n ...(port > 0 ? { PORT: String(port) } : {}),\n };\n\n if (name === \"host\") {\n envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);\n }\n\n const cmd = Command.make(command, ...args).pipe(\n Command.workingDirectory(fullCwd),\n Command.env(envVars),\n );\n\n const proc = yield* Command.start(cmd);\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(readyDeferred, new Error(message)).pipe(Effect.ignore);\n });\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const deadline = Date.now() + LOCAL_PROBE_DEADLINE_MS;\n\n if (port > 0) {\n const readinessPath = descriptor.readinessPath;\n const url = `http://127.0.0.1:${port}${readinessPath}`;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n const ok = yield* probeHttpOk(url);\n if (ok) {\n yield* markReady;\n return;\n }\n yield* Effect.sleep(`${LOCAL_PROBE_INTERVAL_MS} millis`);\n }\n } else {\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n yield* Effect.sleep(\"500 millis\");\n }\n }\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 = Number(proc.pid);\n\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n const exitCode = yield* proc.exitCode;\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);\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 !/^\\$/.test(cleanLine);\n callbacks.onLog(name, line, looksLikeError);\n\n const currentStatus = yield* Ref.get(statusRef);\n if (currentStatus === \"ready\" || currentStatus === \"error\") return;\n\n const detected = detectStatus(line, descriptor);\n if (detected) {\n if (detected.status === \"ready\") {\n yield* markReady;\n } else {\n yield* markError(`Process failed: ${name}`);\n }\n }\n });\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, false))(\n Stream.splitLines(Stream.decodeText(proc.stdout, \"utf-8\")),\n ),\n );\n\n yield* Effect.forkScoped(\n Stream.runForEach((line: string) => handleLine(line, true))(\n Stream.splitLines(Stream.decodeText(proc.stderr, \"utf-8\")),\n ),\n );\n\n return {\n name,\n pid,\n kill: Effect.gen(function* () {\n const result = yield* proc.kill(\"SIGTERM\").pipe(Effect.timeout(\"3 seconds\"), Effect.option);\n if (Option.isNone(result)) {\n const pid = Number(proc.pid);\n yield* Effect.try(() => process.kill(-pid, \"SIGKILL\")).pipe(Effect.ignore);\n yield* Effect.sleep(\"250 millis\");\n }\n }).pipe(Effect.ignore),\n waitForReady: Deferred.await(readyDeferred),\n waitForExit: proc.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, Error>();\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(readyDeferred, new Error(`Remote ${pkg} unreachable`)).pipe(\n Effect.ignore,\n );\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 deadline = Date.now() + REMOTE_PROBE_DEADLINE_MS;\n let delay = REMOTE_PROBE_BACKOFF_INITIAL_MS;\n while (Date.now() < deadline) {\n const status = yield* Ref.get(statusRef);\n if (status === \"ready\" || status === \"error\") return;\n\n const ok = yield* probeHttpOk(probeUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (ok) {\n yield* markReady;\n return;\n }\n\n const fallbackOk = yield* probeHttpOk(entryUrl, REMOTE_PROBE_TIMEOUT_MS);\n\n if (fallbackOk) {\n yield* markReady;\n return;\n }\n\n yield* Effect.sleep(`${delay} millis`);\n delay = Math.min(Math.round(delay * 1.5), REMOTE_PROBE_BACKOFF_MAX_MS);\n }\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(readyDeferred, new Error(\"Killed\")).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 };\n });\n}\n"],"mappings":"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"}
package/dist/plugin.cjs CHANGED
@@ -22,11 +22,11 @@ const require_service_descriptor = require('./service-descriptor.cjs');
22
22
  const require_run = require('./utils/run.cjs');
23
23
  let node_fs = require("node:fs");
24
24
  let node_path = require("node:path");
25
+ let effect = require("effect");
25
26
  let node_events = require("node:events");
26
27
  let node_fs_promises = require("node:fs/promises");
27
28
  let node_process = require("node:process");
28
29
  node_process = require_runtime.__toESM(node_process, 1);
29
- let effect = require("effect");
30
30
  let every_plugin = require("every-plugin");
31
31
  let zod = require("zod");
32
32
 
@@ -321,8 +321,10 @@ async function buildWorkspaceTargets(opts) {
321
321
  bosConfig: opts.bosConfig ?? void 0,
322
322
  extendsChain: []
323
323
  })).catalogChanged) await require_run.run("bun", ["install"], { cwd: opts.configDir });
324
- if (existing.some((entry) => entry.key === "api")) await buildEveryPluginQuietly(opts.configDir);
325
- await buildEverythingDevQuietly(opts.configDir);
324
+ const shouldBuildPlugin = existing.some((entry) => entry.key === "api");
325
+ const buildTasks = [buildEverythingDevQuietly(opts.configDir)];
326
+ if (shouldBuildPlugin) buildTasks.push(buildEveryPluginQuietly(opts.configDir));
327
+ await Promise.all(buildTasks);
326
328
  const env = {
327
329
  ...node_process.default.env,
328
330
  NODE_ENV: opts.deploy ? "production" : "development"
@@ -525,12 +527,9 @@ var plugin_default = (0, every_plugin.createPlugin)({
525
527
  };
526
528
  }),
527
529
  dev: builder.dev.handler(async ({ input }) => {
530
+ const devTimings = [];
528
531
  require_infra.ensureEnvFile(deps.configDir);
529
532
  require_infra.loadProjectEnv(deps.configDir);
530
- pluginEvents.emit("progress", {
531
- phase: "config",
532
- status: "running"
533
- });
534
533
  const localPackages = require_app.detectLocalPackages(deps.bosConfig ?? void 0, deps.runtimeConfig ?? void 0);
535
534
  const hostSource = localPackages.includes("host") ? parseSourceMode(input.host, "local") : "remote";
536
535
  const uiSource = localPackages.includes("ui") ? parseSourceMode(input.ui, "local") : "remote";
@@ -538,61 +537,44 @@ var plugin_default = (0, every_plugin.createPlugin)({
538
537
  const authSource = localPackages.includes("auth") ? parseSourceMode(input.auth, "local") : "remote";
539
538
  const ssr = input.ssr ?? false;
540
539
  const proxy = input.proxy ?? false;
541
- if ((await require_shared_deps.syncResolvedSharedDeps({
540
+ const sharedSync = await timePhase(devTimings, "shared deps", () => require_shared_deps.syncResolvedSharedDeps({
542
541
  configDir: deps.configDir,
543
542
  hostMode: hostSource,
544
543
  bosConfig: deps.bosConfig ?? void 0,
545
544
  extendsChain: []
546
- })).catalogChanged) {
547
- pluginEvents.emit("progress", {
548
- phase: "install",
549
- status: "running"
550
- });
551
- await require_run.run("bun", ["install"], { cwd: deps.configDir });
552
- pluginEvents.emit("progress", {
553
- phase: "install",
554
- status: "done"
555
- });
556
- }
557
- if (apiSource === "local" && !proxy || localPackages.some((pkg) => pkg.startsWith("plugin:"))) {
558
- pluginEvents.emit("progress", {
559
- phase: "build plugin",
560
- status: "running"
561
- });
562
- await buildEveryPluginQuietly(deps.configDir);
563
- pluginEvents.emit("progress", {
564
- phase: "build plugin",
565
- status: "done"
566
- });
545
+ }));
546
+ let configMayHaveChanged = false;
547
+ if (sharedSync.catalogChanged) {
548
+ await timePhase(devTimings, "install", () => require_run.run("bun", ["install"], { cwd: deps.configDir }));
549
+ configMayHaveChanged = true;
567
550
  }
568
- pluginEvents.emit("progress", {
569
- phase: "build",
570
- status: "running"
551
+ const shouldBuildPlugin = apiSource === "local" && !proxy || localPackages.some((pkg) => pkg.startsWith("plugin:"));
552
+ await timePhase(devTimings, "build", async () => {
553
+ const buildTasks = [buildEverythingDevQuietly(deps.configDir)];
554
+ if (shouldBuildPlugin) buildTasks.push(buildEveryPluginQuietly(deps.configDir));
555
+ await Promise.all(buildTasks);
571
556
  });
572
- await buildEverythingDevQuietly(deps.configDir);
573
- pluginEvents.emit("progress", {
574
- phase: "build",
575
- status: "done"
576
- });
577
- pluginEvents.emit("progress", {
578
- phase: "config",
579
- status: "done"
580
- });
581
- const refreshed = await require_config.loadResolvedConfig({
582
- cwd: deps.configDir,
583
- remotePlugins: input.remotePlugins
584
- });
585
- deps.bosConfig = refreshed?.config ?? deps.bosConfig;
586
- deps.runtimeConfig = refreshed?.runtime ?? deps.runtimeConfig;
557
+ let devExtendsChain;
558
+ if (configMayHaveChanged || input.remotePlugins?.length) {
559
+ const refreshed = await timePhase(devTimings, "resolve config", () => require_config.loadResolvedConfig({
560
+ cwd: deps.configDir,
561
+ remotePlugins: input.remotePlugins
562
+ }));
563
+ deps.bosConfig = refreshed?.config ?? deps.bosConfig;
564
+ deps.runtimeConfig = refreshed?.runtime ?? deps.runtimeConfig;
565
+ devExtendsChain = refreshed?.source.extended;
566
+ }
587
567
  if (!deps.bosConfig) return {
588
568
  status: "error",
589
569
  description: "No bos.config.json found",
590
- processes: []
570
+ processes: [],
571
+ timings: devTimings
591
572
  };
592
573
  if (proxy && !resolveProxyUrl(deps.bosConfig)) return {
593
574
  status: "error",
594
575
  description: "No valid proxy URL configured in bos.config.json",
595
- processes: []
576
+ processes: [],
577
+ timings: devTimings
596
578
  };
597
579
  const hostPort = input.port ?? require_config.getHostDevelopmentPort(deps.bosConfig.app.host.development);
598
580
  require_config.suppressWarnings();
@@ -606,20 +588,18 @@ var plugin_default = (0, every_plugin.createPlugin)({
606
588
  });
607
589
  require_config.drainConfigWarnings();
608
590
  require_config.resumeWarnings();
609
- const runtimeConfig = await require_app.prepareDevelopmentRuntimeConfig(developmentRuntime, {
591
+ const runtimeConfig = await timePhase(devTimings, "ports", () => require_app.prepareDevelopmentRuntimeConfig(developmentRuntime, {
610
592
  hostPort,
611
593
  ssr
612
- });
594
+ }));
613
595
  require_infra.syncGeneratedInfra(deps.configDir, runtimeConfig);
614
- if (!(0, node_fs.existsSync)((0, node_path.join)(deps.configDir, ".env"))) {
615
- require_infra.ensureEnvFile(deps.configDir);
616
- require_infra.loadProjectEnv(deps.configDir);
617
- }
618
- await generateCodeArtifacts(deps.configDir, deps.bosConfig, {
596
+ require_infra.ensureEnvFile(deps.configDir);
597
+ require_infra.loadProjectEnv(deps.configDir);
598
+ await timePhase(devTimings, "generate artifacts", () => generateCodeArtifacts(deps.configDir, deps.bosConfig, {
619
599
  env: "development",
620
- extendsChain: refreshed?.source.extended,
600
+ extendsChain: devExtendsChain,
621
601
  runtimeConfig
622
- });
602
+ }));
623
603
  const services = require_service_descriptor.buildServiceDescriptorMap(runtimeConfig, {
624
604
  ssr,
625
605
  proxy
@@ -651,7 +631,8 @@ var plugin_default = (0, every_plugin.createPlugin)({
651
631
  return {
652
632
  status: "started",
653
633
  description: orchestrator.description,
654
- processes: packages
634
+ processes: packages,
635
+ timings: devTimings
655
636
  };
656
637
  }),
657
638
  start: builder.start.handler(async ({ input }) => {
@@ -712,10 +693,8 @@ var plugin_default = (0, every_plugin.createPlugin)({
712
693
  if (isStaging && config.staging?.domain) runtimeConfig.domain = config.staging.domain;
713
694
  if (isStaging) runtimeConfig.env = "staging";
714
695
  require_infra.syncGeneratedInfra(deps.configDir, runtimeConfig);
715
- if (!(0, node_fs.existsSync)((0, node_path.join)(deps.configDir, ".env"))) {
716
- require_infra.ensureEnvFile(deps.configDir);
717
- require_infra.loadProjectEnv(deps.configDir);
718
- }
696
+ require_infra.ensureEnvFile(deps.configDir);
697
+ require_infra.loadProjectEnv(deps.configDir);
719
698
  pluginEvents.emit("progress", {
720
699
  phase: "generate artifacts",
721
700
  status: "running"
@@ -1037,10 +1016,23 @@ var plugin_default = (0, every_plugin.createPlugin)({
1037
1016
  try {
1038
1017
  parentConfig = await timePhase(timings, "parent config", () => require_cli_init.fetchParentConfig(extendsAccount, extendsGateway));
1039
1018
  if (parentConfig?.plugins && typeof parentConfig.plugins === "object") parentPluginKeys = Object.keys(parentConfig.plugins);
1040
- } catch {}
1019
+ } catch (e) {
1020
+ console.warn(`[init] Failed to fetch parent config from ${extendsAccount}/${extendsGateway}: ${e instanceof Error ? e.message : e}`);
1021
+ }
1041
1022
  overrides = overrides?.length ? overrides : ["ui", "api"];
1042
1023
  if (overrides.includes("plugins") && plugins === void 0) plugins = parentPluginKeys;
1043
1024
  plugins = plugins ?? [];
1025
+ const pluginDirMap = {};
1026
+ if (parentConfig?.plugins) for (const plugin of plugins) {
1027
+ const entry = parentConfig.plugins?.[plugin];
1028
+ if (entry && typeof entry === "object") {
1029
+ const dev = entry.development;
1030
+ if (typeof dev === "string") {
1031
+ const match = dev.match(/^local:plugins\/(.+)$/);
1032
+ if (match?.[1] && match[1] !== plugin) pluginDirMap[plugin] = match[1];
1033
+ }
1034
+ }
1035
+ }
1044
1036
  directory = directory || domain || extendsGateway;
1045
1037
  const targetDir = (0, node_path.resolve)(directory);
1046
1038
  const extendsRef = `bos://${extendsAccount}/${extendsGateway}`;
@@ -1098,7 +1090,7 @@ var plugin_default = (0, every_plugin.createPlugin)({
1098
1090
  staging: parentConfig?.staging
1099
1091
  }));
1100
1092
  } else {
1101
- const patterns = require_cli_init.buildInitPatterns(overrides, plugins);
1093
+ const patterns = require_cli_init.buildInitPatterns(overrides, plugins, pluginDirMap);
1102
1094
  filesCopied = await timePhase(timings, "copy files", () => require_cli_init.copyFilteredFiles(sourceDir, targetDir, patterns, {
1103
1095
  overrides,
1104
1096
  plugins