UNPKG

everything-dev

Version:

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

1 lines 17.3 kB
{"version":3,"file":"dev-session.cjs","names":["Effect","getProjectRoot","ServiceDescriptorMap","DevRuntimeConfig","getProcessStates","createDevLogger","Deferred","renderDevView","renderStreamingView","makeDevProcess","ServiceDescriptorMapLive","DevRuntimeConfigLive","NodeContext","Exit"],"sources":["../src/dev-session.ts"],"sourcesContent":["import * as NodeContext from \"@effect/platform-node/NodeContext\";\nimport { Deferred, Effect, Exit } from \"effect\";\nimport {\n type DevViewHandle,\n type LogEntry,\n type ProcessState,\n renderDevView,\n} from \"./components/dev-view\";\nimport { renderStreamingView } from \"./components/streaming-view\";\nimport { getProjectRoot } from \"./config\";\nimport { createDevLogger } from \"./dev-logs\";\nimport {\n getProcessStates,\n makeDevProcess,\n type ProcessCallbacks,\n type ProcessHandle,\n} from \"./orchestrator\";\nimport { registerStandalone, unregisterPid } from \"./process-registry\";\nimport {\n type AppOrchestrator,\n DevRuntimeConfig,\n DevRuntimeConfigLive,\n type ServiceDescriptor,\n ServiceDescriptorMap,\n ServiceDescriptorMapLive,\n} from \"./service-descriptor\";\nimport type { RuntimeConfig } from \"./types\";\n\nconst LOG_NOISE_PATTERNS = [\n /\\[ Federation Runtime \\] Version .* from (host|ui) of shared singleton module/,\n /Executing an Effect versioned \\d+\\.\\d+\\.\\d+ with a Runtime of version/,\n /you may want to dedupe the effect dependencies/,\n];\n\nconst SSR_LOG_ALLOWLIST = [\n /\\bready\\s+built in\\b/i,\n /\\bcompiled\\b.*successfully/i,\n /\\berror\\b/i,\n /\\bfailed\\b/i,\n /\\bexception\\b/i,\n];\n\nconst shouldDisplayLog = (source: string, line: string, isError?: boolean): boolean => {\n if (process.env.DEBUG === \"true\" || process.env.DEBUG === \"1\") return true;\n if (source === \"ui-ssr\") {\n if (isError) return true;\n return SSR_LOG_ALLOWLIST.some((pattern) => pattern.test(line));\n }\n return !LOG_NOISE_PATTERNS.some((pattern) => pattern.test(line));\n};\n\nconst isInteractiveSupported = (): boolean => {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n};\n\nconst STARTUP_ORDER = [\"ui-ssr\", \"ui\", \"auth\", \"api\", \"plugin\", \"host-build\", \"host\"];\n\nconst sortByOrder = (packages: string[]): string[] => {\n return [...packages].sort((a, b) => {\n const aIdx = a.startsWith(\"plugin:\")\n ? STARTUP_ORDER.indexOf(\"plugin\")\n : STARTUP_ORDER.indexOf(a);\n const bIdx = b.startsWith(\"plugin:\")\n ? STARTUP_ORDER.indexOf(\"plugin\")\n : STARTUP_ORDER.indexOf(b);\n if (aIdx === -1 && bIdx === -1) return 0;\n if (aIdx === -1) return 1;\n if (bIdx === -1) return -1;\n return aIdx - bIdx;\n });\n};\n\nfunction formatLogLine(entry: LogEntry): string {\n const ts = new Date(entry.timestamp).toISOString();\n const prefix = entry.isError ? \"ERR\" : \"OUT\";\n return `[${ts}] [${entry.source}] [${prefix}] ${entry.line}`;\n}\n\nexport const runDevSession = (\n orchestrator: AppOrchestrator,\n onShutdownReady?: (requestShutdown: () => void) => void,\n) =>\n Effect.gen(function* () {\n const configDir = getProjectRoot();\n const services = yield* ServiceDescriptorMap;\n const runtimeConfig = yield* DevRuntimeConfig;\n const orderedPackages = sortByOrder(orchestrator.packages);\n const initialProcesses: ProcessState[] = getProcessStates(\n orderedPackages,\n services,\n orchestrator.port,\n );\n\n if (process.env.DEBUG === \"true\" || process.env.DEBUG === \"1\") {\n console.error(\"[DEBUG session] orchestrator.packages:\", orchestrator.packages.join(\", \"));\n console.error(\"[DEBUG session] orderedPackages:\", orderedPackages.join(\", \"));\n console.error(\"[DEBUG session] services keys:\", [...services.keys()].join(\", \"));\n console.error(\n \"[DEBUG session] initialProcesses:\",\n initialProcesses.map((p) => `${p.name}=${p.source}`).join(\", \"),\n );\n }\n\n const logger = yield* Effect.promise(() =>\n createDevLogger(configDir, orchestrator.description),\n );\n\n const shutdown = yield* Deferred.make<void>();\n\n onShutdownReady?.(() => {\n void Effect.runPromise(Deferred.succeed(shutdown, undefined));\n });\n\n const isWorkspaceChild = process.env.BOS_WORKSPACE_CHILD === \"1\";\n if (!isWorkspaceChild) {\n const regPorts: Record<string, number> = {};\n const addPort = (key: string, value: number | undefined | null) => {\n if (value != null) regPorts[key] = value;\n };\n addPort(\"host\", runtimeConfig.host.port);\n addPort(\"api\", runtimeConfig.api.port);\n addPort(\"ui\", runtimeConfig.ui.port);\n addPort(\"auth\", runtimeConfig.auth?.port);\n addPort(\n \"uiSsr\",\n runtimeConfig.ui.ssrUrl\n ? Number.parseInt(new URL(runtimeConfig.ui.ssrUrl).port, 10)\n : undefined,\n );\n if (runtimeConfig.plugins) {\n for (const [id, plugin] of Object.entries(runtimeConfig.plugins)) {\n addPort(`plugin:${id}`, plugin.port);\n addPort(`plugin-ui:${id}`, plugin.ui?.port);\n }\n }\n registerStandalone({\n pid: process.pid,\n configDir,\n ports: regPorts,\n startedAt: Date.now(),\n description: orchestrator.description,\n });\n }\n\n const allLogs: LogEntry[] = [];\n let view: DevViewHandle | null = null;\n let shouldExportLogs = false;\n\n const requestShutdownAndExport = () => {\n shouldExportLogs = true;\n void Effect.runPromise(Deferred.succeed(shutdown, undefined));\n };\n\n const useInteractive = orchestrator.interactive ?? isInteractiveSupported();\n view = useInteractive\n ? renderDevView(\n initialProcesses,\n orchestrator.description,\n orchestrator.env,\n () => void Effect.runPromise(Deferred.succeed(shutdown, undefined)),\n requestShutdownAndExport,\n )\n : renderStreamingView(\n initialProcesses,\n orchestrator.description,\n orchestrator.env,\n () => void Effect.runPromise(Deferred.succeed(shutdown, undefined)),\n );\n\n const callbacks: ProcessCallbacks = {\n onStatus: (name, status, message) => {\n view?.updateProcess(name, status, message);\n },\n onLog: (name, line, isError) => {\n const entry: LogEntry = {\n id: `${Date.now()}-${allLogs.length + 1}`,\n source: name,\n line,\n timestamp: Date.now(),\n isError,\n };\n allLogs.push(entry);\n if (shouldDisplayLog(name, line, isError)) {\n view?.addLog(name, line, isError);\n }\n if (!orchestrator.noLogs) {\n void logger.write(entry);\n }\n },\n };\n\n const startProcess = (pkg: string) => {\n const portOverride = pkg === \"host\" ? orchestrator.port : undefined;\n return makeDevProcess(pkg, callbacks, portOverride).pipe(\n Effect.tapError((err) =>\n Effect.sync(() => {\n callbacks.onLog(pkg, `Failed to start: ${err}`, true);\n callbacks.onStatus(pkg, \"error\");\n }),\n ),\n Effect.catchAll(() =>\n Effect.succeed({\n name: pkg,\n pid: undefined,\n kill: Effect.void,\n waitForReady: Effect.void,\n waitForExit: Effect.never,\n } satisfies ProcessHandle),\n ),\n );\n };\n\n const startGroup = (packages: string[]) =>\n Effect.forEach(packages, startProcess, { concurrency: \"unbounded\" });\n\n const awaitReady = (pkg: string, handle: ProcessHandle) =>\n handle.waitForReady.pipe(\n Effect.timeout(\"120 seconds\"),\n Effect.catchAll((err) =>\n Effect.sync(() => {\n callbacks.onLog(\n pkg,\n `Timed out or failed: ${err instanceof Error ? err.message : String(err)}`,\n true,\n );\n }),\n ),\n );\n\n const nonHostPackages = orderedPackages.filter((pkg) => pkg !== \"host\");\n const hostPackages = orderedPackages.filter((pkg) => pkg === \"host\");\n\n const nonHostHandles = yield* startGroup(nonHostPackages);\n\n yield* Effect.forEach(\n nonHostHandles.map((handle, index) => ({\n handle,\n pkg: nonHostPackages[index] ?? handle.name,\n })),\n ({ handle, pkg }) => awaitReady(pkg, handle),\n { concurrency: \"unbounded\" },\n );\n\n const hostHandles = yield* startGroup(hostPackages);\n\n yield* Effect.forEach(\n hostHandles.map((handle, index) => ({ handle, pkg: hostPackages[index] ?? handle.name })),\n ({ handle, pkg }) => awaitReady(pkg, handle),\n { concurrency: \"unbounded\" },\n );\n\n const allHandles = [...nonHostHandles, ...hostHandles];\n\n yield* Effect.addFinalizer(() =>\n Effect.gen(function* () {\n yield* Effect.forEach(allHandles, (h) => h.kill.pipe(Effect.ignore), {\n concurrency: \"unbounded\",\n });\n\n yield* Effect.sleep(\"200 millis\");\n\n view?.unmount();\n\n if (!isWorkspaceChild) {\n try {\n unregisterPid(process.pid);\n } catch {\n // best-effort; pruneDead cleans stale entries on next ps/kill\n }\n }\n\n if (shouldExportLogs) {\n console.log(\"\\n\");\n console.log(\"\".repeat(70));\n console.log(` SESSION LOGS: ${orchestrator.description}`);\n console.log(` Started: ${new Date(allLogs[0]?.timestamp || Date.now()).toISOString()}`);\n console.log(` Total entries: ${allLogs.length}`);\n console.log(\"\".repeat(70));\n console.log(\"\");\n for (const entry of allLogs) {\n console.log(formatLogLine(entry));\n }\n console.log(\"\");\n console.log(\"\".repeat(70));\n console.log(` Full logs saved to: ${logger.logFile}`);\n console.log(\"\".repeat(70));\n console.log(\"\");\n }\n }),\n );\n\n yield* Deferred.await(shutdown);\n });\n\nconst runApp = (\n orchestrator: AppOrchestrator,\n services: Map<string, ServiceDescriptor>,\n runtimeConfig: RuntimeConfig,\n) => {\n let requestShutdown: (() => void) | null = null;\n let signalCount = 0;\n let forceExitTimer: ReturnType<typeof setTimeout> | null = null;\n\n const forceExit = () => {\n console.log(\"\\n[Dev] Force exit\");\n process.exit(0);\n };\n\n const program = Effect.scoped(\n runDevSession(orchestrator, (shutdown) => {\n requestShutdown = shutdown;\n }),\n ).pipe(\n Effect.provide(ServiceDescriptorMapLive(services)),\n Effect.provide(DevRuntimeConfigLive(runtimeConfig)),\n Effect.provide(NodeContext.layer),\n Effect.catchAllDefect((defect) =>\n Effect.sync(() => {\n console.error(\"[Dev] Unhandled defect in orchestrator:\", defect);\n }),\n ),\n );\n\n const handleSignal = () => {\n signalCount++;\n if (signalCount > 1) {\n forceExit();\n return;\n }\n console.log(\"\\n[Dev] Shutting down...\");\n forceExitTimer = setTimeout(forceExit, 5000);\n requestShutdown?.();\n };\n\n process.on(\"SIGINT\", handleSignal);\n process.on(\"SIGTERM\", handleSignal);\n\n Effect.runPromiseExit(program).then((exit) => {\n if (forceExitTimer) clearTimeout(forceExitTimer);\n process.exit(Exit.isSuccess(exit) ? 0 : 0);\n });\n};\n\nexport const devApp = runApp;\n\nexport const startApp = runApp;\n"],"mappings":"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"}