UNPKG

everything-dev

Version:

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

180 lines (178 loc) 7.67 kB
const require_runtime = require('./_virtual/_rolldown/runtime.cjs'); const require_config = require('./config.cjs'); const require_service_descriptor = require('./service-descriptor.cjs'); const require_dev_view = require('./components/dev-view.cjs'); const require_streaming_view = require('./components/streaming-view.cjs'); const require_dev_logs = require('./dev-logs.cjs'); const require_orchestrator = require('./orchestrator.cjs'); let effect = require("effect"); let _effect_platform_node_NodeContext = require("@effect/platform-node/NodeContext"); _effect_platform_node_NodeContext = require_runtime.__toESM(_effect_platform_node_NodeContext, 1); //#region src/dev-session.ts const LOG_NOISE_PATTERNS = [ /\[ Federation Runtime \] Version .* from (host|ui) of shared singleton module/, /Executing an Effect versioned \d+\.\d+\.\d+ with a Runtime of version/, /you may want to dedupe the effect dependencies/ ]; const SSR_LOG_ALLOWLIST = [ /\bready\s+built in\b/i, /\bcompiled\b.*successfully/i, /\berror\b/i, /\bfailed\b/i, /\bexception\b/i ]; const shouldDisplayLog = (source, line, isError) => { if (process.env.DEBUG === "true" || process.env.DEBUG === "1") return true; if (source === "ui-ssr") { if (isError) return true; return SSR_LOG_ALLOWLIST.some((pattern) => pattern.test(line)); } return !LOG_NOISE_PATTERNS.some((pattern) => pattern.test(line)); }; const isInteractiveSupported = () => { return process.stdin.isTTY === true && process.stdout.isTTY === true; }; const STARTUP_ORDER = [ "ui-ssr", "ui", "auth", "api", "plugin", "host-build", "host" ]; const sortByOrder = (packages) => { return [...packages].sort((a, b) => { const aIdx = a.startsWith("plugin:") ? STARTUP_ORDER.indexOf("plugin") : STARTUP_ORDER.indexOf(a); const bIdx = b.startsWith("plugin:") ? STARTUP_ORDER.indexOf("plugin") : STARTUP_ORDER.indexOf(b); if (aIdx === -1 && bIdx === -1) return 0; if (aIdx === -1) return 1; if (bIdx === -1) return -1; return aIdx - bIdx; }); }; function formatLogLine(entry) { const ts = new Date(entry.timestamp).toISOString(); const prefix = entry.isError ? "ERR" : "OUT"; return `[${ts}] [${entry.source}] [${prefix}] ${entry.line}`; } const runDevSession = (orchestrator, onShutdownReady) => effect.Effect.gen(function* () { const configDir = require_config.getProjectRoot(); const services = yield* require_service_descriptor.ServiceDescriptorMap; const orderedPackages = sortByOrder(orchestrator.packages); const initialProcesses = require_orchestrator.getProcessStates(orderedPackages, services, orchestrator.port); const logger = yield* effect.Effect.promise(() => require_dev_logs.createDevLogger(configDir, orchestrator.description)); const shutdown = yield* effect.Deferred.make(); onShutdownReady?.(() => { effect.Effect.runPromise(effect.Deferred.succeed(shutdown, void 0)); }); const allLogs = []; let view = null; let shouldExportLogs = false; const requestShutdownAndExport = () => { shouldExportLogs = true; effect.Effect.runPromise(effect.Deferred.succeed(shutdown, void 0)); }; view = orchestrator.interactive ?? isInteractiveSupported() ? require_dev_view.renderDevView(initialProcesses, orchestrator.description, orchestrator.env, () => void effect.Effect.runPromise(effect.Deferred.succeed(shutdown, void 0)), requestShutdownAndExport) : require_streaming_view.renderStreamingView(initialProcesses, orchestrator.description, orchestrator.env, () => void effect.Effect.runPromise(effect.Deferred.succeed(shutdown, void 0))); const callbacks = { onStatus: (name, status, message) => { view?.updateProcess(name, status, message); }, onLog: (name, line, isError) => { const entry = { id: `${Date.now()}-${allLogs.length + 1}`, source: name, line, timestamp: Date.now(), isError }; allLogs.push(entry); if (shouldDisplayLog(name, line, isError)) view?.addLog(name, line, isError); if (!orchestrator.noLogs) logger.write(entry); } }; const startProcess = (pkg) => { return require_orchestrator.makeDevProcess(pkg, callbacks, pkg === "host" ? orchestrator.port : void 0).pipe(effect.Effect.tapError((err) => effect.Effect.sync(() => { callbacks.onLog(pkg, `Failed to start: ${err}`, true); callbacks.onStatus(pkg, "error"); })), effect.Effect.catchAll(() => effect.Effect.succeed({ name: pkg, pid: void 0, kill: effect.Effect.void, waitForReady: effect.Effect.void, waitForExit: effect.Effect.never }))); }; const startGroup = (packages) => effect.Effect.forEach(packages, startProcess, { concurrency: "unbounded" }); const awaitReady = (_pkg, handle) => handle.waitForReady.pipe(effect.Effect.catchAll(() => effect.Effect.void)); const nonHostPackages = orderedPackages.filter((pkg) => pkg !== "host"); const hostPackages = orderedPackages.filter((pkg) => pkg === "host"); const nonHostHandles = yield* startGroup(nonHostPackages); yield* effect.Effect.forEach(nonHostHandles.map((handle, index) => ({ handle, pkg: nonHostPackages[index] ?? handle.name })), ({ handle, pkg }) => awaitReady(pkg, handle), { concurrency: "unbounded" }); const hostHandles = yield* startGroup(hostPackages); yield* effect.Effect.forEach(hostHandles.map((handle, index) => ({ handle, pkg: hostPackages[index] ?? handle.name })), ({ handle, pkg }) => awaitReady(pkg, handle), { concurrency: "unbounded" }); const allHandles = [...nonHostHandles, ...hostHandles]; yield* effect.Effect.addFinalizer(() => effect.Effect.gen(function* () { yield* effect.Effect.forEach(allHandles, (h) => h.kill.pipe(effect.Effect.ignore), { concurrency: "unbounded" }); yield* effect.Effect.sleep("200 millis"); view?.unmount(); if (shouldExportLogs) { console.log("\n"); console.log("═".repeat(70)); console.log(` SESSION LOGS: ${orchestrator.description}`); console.log(` Started: ${new Date(allLogs[0]?.timestamp || Date.now()).toISOString()}`); console.log(` Total entries: ${allLogs.length}`); console.log("═".repeat(70)); console.log(""); for (const entry of allLogs) console.log(formatLogLine(entry)); console.log(""); console.log("═".repeat(70)); console.log(` Full logs saved to: ${logger.logFile}`); console.log("═".repeat(70)); console.log(""); } })); yield* effect.Deferred.await(shutdown); }); const runApp = (orchestrator, services, runtimeConfig) => { let requestShutdown = null; let signalCount = 0; let forceExitTimer = null; const forceExit = () => { console.log("\n[Dev] Force exit"); process.exit(0); }; const program = effect.Effect.scoped(runDevSession(orchestrator, (shutdown) => { requestShutdown = shutdown; })).pipe(effect.Effect.provide(require_service_descriptor.ServiceDescriptorMapLive(services)), effect.Effect.provide(require_service_descriptor.DevRuntimeConfigLive(runtimeConfig)), effect.Effect.provide(_effect_platform_node_NodeContext.layer), effect.Effect.catchAllDefect((defect) => effect.Effect.sync(() => { console.error("[Dev] Unhandled defect in orchestrator:", defect); }))); const handleSignal = () => { signalCount++; if (signalCount > 1) { forceExit(); return; } console.log("\n[Dev] Shutting down..."); forceExitTimer = setTimeout(forceExit, 5e3); requestShutdown?.(); }; process.on("SIGINT", handleSignal); process.on("SIGTERM", handleSignal); effect.Effect.runPromiseExit(program).then((exit) => { if (forceExitTimer) clearTimeout(forceExitTimer); process.exit(effect.Exit.isSuccess(exit) ? 0 : 0); }); }; const devApp = runApp; const startApp = runApp; //#endregion exports.devApp = devApp; exports.startApp = startApp; //# sourceMappingURL=dev-session.cjs.map