UNPKG

everything-dev

Version:

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

219 lines (217 loc) • 10.4 kB
const require_services_registry_fastkv = require('../registry/fastkv.cjs'); const require_load = require('../resolution/load.cjs'); const require_process_env = require('../../env/process-env.cjs'); const require_service_descriptor = require('../dev/service-descriptor.cjs'); const require_runtime = require('../resolution/runtime.cjs'); const require_source = require('./source.cjs'); const require_driver = require('../../db/driver.cjs'); const require_project_env = require('../../env/project-env.cjs'); const require_artifacts = require('../codegen/artifacts.cjs'); const require_planner = require('../../infra/planner.cjs'); const require_progress = require('../dev/progress.cjs'); const require_boot = require('../dev/boot.cjs'); const require_published = require('../registry/published.cjs'); let effect = require("effect"); //#region src/services/start/boot.ts var StartFetchFailed = class extends effect.Data.TaggedError("StartFetchFailed") {}; var StartRemoteConfigMissing = class extends effect.Data.TaggedError("StartRemoteConfigMissing") {}; var StartConfigMissing = class extends effect.Data.TaggedError("StartConfigMissing") {}; /** * A production start booting from an explicit config has declared that config * as its truth — the host's SnapshotWatch must not override it with the * registry pointer mid-run (a fixture-booted stack carrying a live identity * would otherwise adopt a foreign generation 30 seconds in, and a * config-path production boot is the operator's declared truth). The watch * stays on for identity-driven boots — adopting published config changes is * its feature. An operator-set interval always wins. */ function setSnapshotWatchIntervalForBoot(source, env, processEnv = process.env) { if (processEnv.BOS_SNAPSHOT_WATCH_INTERVAL_MS !== void 0) return; if (env !== "production" || !source.configPath) return; processEnv.BOS_SNAPSHOT_WATCH_INTERVAL_MS = "0"; } const startBootstrap = (deps, input) => effect.Effect.gen(function* () { const shell = yield* require_project_env.captureShellEnv; const projectEnv = yield* require_project_env.ProjectEnv; const sessionRoot = deps.session?.root ?? process.cwd(); yield* require_boot.loadEnvStep(projectEnv, sessionRoot); yield* require_progress.emitProgress({ phase: "config", status: "running" }); const bosEnv = input.env ?? (require_process_env.getProcessEnv("BOS_ENV") === "staging" ? "staging" : "production"); const explicitConfig = require_source.resolveStartConfigSource(input, { BOS_ACCOUNT: require_process_env.getProcessEnv("BOS_ACCOUNT"), BOS_GATEWAY: require_process_env.getProcessEnv("BOS_GATEWAY") }); const registry = input.registry ?? require_process_env.getProcessEnv("BOS_REGISTRY"); setSnapshotWatchIntervalForBoot(explicitConfig, bosEnv); let config = null; let remoteConfig = null; if (explicitConfig.configPath) config = deps.session?.config ?? null; else if (explicitConfig.registry) { const { account, domain } = explicitConfig.registry; const expectedUrl = require_services_registry_fastkv.buildRegistryConfigUrl(account, domain, registry); remoteConfig = yield* effect.Effect.tryPromise({ try: () => require_published.resolvePublishedConfig(account, domain, registry), catch: (error) => new StartFetchFailed({ message: `Failed to fetch config for bos://${account}/${domain}: ${error instanceof Error ? error.message : "Unknown error"}\nExpected URL: ${expectedUrl}` }) }); if (remoteConfig) config = remoteConfig; else return yield* new StartRemoteConfigMissing({ message: `No config found at bos://${account}/${domain}. Verify the account and gateway are correct and the config has been published.\nExpected URL: ${expectedUrl}` }); } else config = deps.session?.config ?? null; if (!config) return yield* new StartConfigMissing({}); if (!explicitConfig.configPath) { if (explicitConfig.registry?.account) config = { ...config, account: explicitConfig.registry.account }; if (explicitConfig.registry?.domain) config = { ...config, domain: explicitConfig.registry.domain }; } const baseConfig = config; const port = input.port ?? require_runtime.getHostDevelopmentPort(baseConfig.app.host.development); const isStaging = bosEnv === "staging"; const runtimePlugins = yield* effect.Effect.tryPromise({ try: () => require_runtime.buildRuntimePluginsForConfig(baseConfig, sessionRoot, "production"), catch: (cause) => new require_boot.DevStepError({ phase: "resolve runtime plugins", cause }) }); const runtimeConfig = yield* effect.Effect.tryPromise({ try: () => require_runtime.buildRuntimeConfig(baseConfig, sessionRoot, "production", { uiSource: "remote", apiSource: "remote", authSource: "remote", hostSource: "remote", plugins: runtimePlugins }), catch: (cause) => new require_boot.DevStepError({ phase: "build runtime config", cause }) }); if (isStaging && baseConfig.staging?.domain) runtimeConfig.domain = baseConfig.staging.domain; if (isStaging) runtimeConfig.env = "staging"; yield* require_progress.emitProgress({ phase: "generate artifacts", status: "running" }); yield* effect.Effect.tryPromise({ try: () => require_artifacts.generateCodeArtifacts(sessionRoot, baseConfig, { env: "production", runtimeConfig }), catch: (cause) => new require_boot.DevStepError({ phase: "generate artifacts", cause }) }); yield* require_progress.emitProgress({ phase: "generate artifacts", status: "done" }); const productionEnv = {}; const warnings = []; const localhostOrigin = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i; const corsOrigin = require_process_env.getProcessEnv("CORS_ORIGIN"); if (corsOrigin !== void 0 && localhostOrigin.test(corsOrigin) && require_source.isRegistryStart(explicitConfig)) { warnings.push(`CORS_ORIGIN is a localhost origin (${corsOrigin}) in a registry production start — overriding with the configured domain`); require_process_env.deleteProcessEnv("CORS_ORIGIN"); require_process_env.deleteProcessEnv("BASE_URL"); } if (!require_process_env.getProcessEnv("CORS_ORIGIN") && baseConfig.domain) { const defaultOrigin = `https://${isStaging ? baseConfig.staging?.domain ?? baseConfig.domain : baseConfig.domain}`; productionEnv.CORS_ORIGIN = defaultOrigin; productionEnv.BASE_URL = defaultOrigin; warnings.push(`CORS_ORIGIN defaulting to ${defaultOrigin}`); } const requiredSecrets = /* @__PURE__ */ new Set(); const missingSecrets = []; if (runtimeConfig.host.secrets) for (const s of runtimeConfig.host.secrets) requiredSecrets.add(s); if (runtimeConfig.auth?.secrets) for (const s of runtimeConfig.auth.secrets) requiredSecrets.add(s); if (runtimeConfig.api?.secrets) for (const s of runtimeConfig.api.secrets) requiredSecrets.add(s); for (const plugin of Object.values(runtimeConfig.plugins ?? {})) if (plugin.secrets) for (const s of plugin.secrets) { if (s.endsWith("_DATABASE_URL")) continue; requiredSecrets.add(s); } for (const secret of requiredSecrets) { const value = require_process_env.getProcessEnv(secret); if (!value || value.length === 0) missingSecrets.push(secret); } if (missingSecrets.length > 0) warnings.push(`Missing ${missingSecrets.length} secret(s): ${missingSecrets.join(", ")}`); for (const [secret, value] of Object.entries(require_process_env.processEnvRecord())) { if (!value || !secret.endsWith("_DATABASE_URL")) continue; if (require_driver.isRailwayPublicProxyUrl(value)) { const host = new URL(value).hostname; warnings.push(`${secret} points at Railway's public proxy (${host}) — its TLS certificate is self-signed, so connections use unverified TLS. Prefer the private *.railway.internal URL when the database runs in the same Railway project.`); } } if (!require_process_env.operatorEnvRecord().API_DATABASE_URL && !require_process_env.operatorEnvRecord().AUTH_DATABASE_URL) warnings.push("No *_DATABASE_URL set — plugins without explicit URLs boot on ephemeral pglite and their data will not survive restarts. Set API_DATABASE_URL/AUTH_DATABASE_URL in .env for production."); const stagingEnvVars = isStaging ? { BOS_GATEWAY: baseConfig.staging?.domain ?? baseConfig.domain ?? "" } : {}; const plan = yield* require_planner.planInfra({ configDir: sessionRoot, bosConfig: runtimeConfig, operatorEnv: require_process_env.operatorEnvRecord(), cli: { port: input.port, ssr: false, proxy: false, hostSource: "remote", uiSource: "remote", apiSource: "remote", authSource: "remote", interactive: input.interactive } }); const services = require_service_descriptor.buildServiceDescriptorMap(plan.runtimeConfig); const summary = { configSource: explicitConfig.configPath ? explicitConfig.configPath : remoteConfig ? `bos://${explicitConfig.registry?.account}/${explicitConfig.registry?.domain}` : require_load.findConfigPath() ?? "bos.config.json", configSourceHttp: remoteConfig && explicitConfig.registry ? require_services_registry_fastkv.buildRegistryConfigUrl(explicitConfig.registry.account, explicitConfig.registry.domain, registry) : void 0, account: baseConfig.account, domain: baseConfig.domain ?? void 0, modules: { host: plan.runtimeConfig.host.remoteUrl ?? plan.runtimeConfig.host.url ?? "local", ui: plan.runtimeConfig.ui.url ?? "local", api: plan.runtimeConfig.api.url ?? "local", auth: plan.runtimeConfig.auth?.url ?? void 0 }, warnings }; const orchestrator = { packages: ["host"], env: { NODE_ENV: "production", ...productionEnv, ...stagingEnvVars, ...plan.launch.env }, description: `${isStaging ? "Staging" : "Production"} Mode (${baseConfig.account})`, port: plan.resolvedPorts.host ?? port, interactive: input.interactive }; yield* require_progress.emitProgress({ phase: "config", status: "done" }); return { session: { orchestrator, services, runtimeConfig: plan.runtimeConfig, envGenerated: plan.envGenerated, shellEnv: shell }, summary, url: plan.launch.hostUrl ?? `http://localhost:${plan.resolvedPorts.host ?? port}` }; }); //#endregion exports.StartConfigMissing = StartConfigMissing; exports.StartFetchFailed = StartFetchFailed; exports.StartRemoteConfigMissing = StartRemoteConfigMissing; exports.setSnapshotWatchIntervalForBoot = setSnapshotWatchIntervalForBoot; exports.startBootstrap = startBootstrap; //# sourceMappingURL=boot.cjs.map