UNPKG

everything-dev

Version:

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

344 lines (342 loc) 12.2 kB
const require_runtime = require('../_virtual/_rolldown/runtime.cjs'); const require_app = require('../app.cjs'); const require_infra = require('../cli/infra.cjs'); const require_service_descriptor = require('../service-descriptor.cjs'); const require_types = require('./types.cjs'); let node_path = require("node:path"); let effect = require("effect"); let node_crypto = require("node:crypto"); //#region src/infra/planner.ts const DEFAULT_HOST_PORT = 3e3; const DEFAULT_API_PORT = 3001; const DEFAULT_AUTH_PORT = 3002; const DEFAULT_UI_PORT = 3003; const DEFAULT_PLUGIN_PORT_START = 3010; const POSTGRES_USER = "everythingdev"; const POSTGRES_PASSWORD = "everythingdev"; function workspaceKey(configDir) { return (0, node_crypto.createHash)("sha256").update((0, node_path.resolve)(configDir)).digest("hex").slice(0, 12); } function normalizeCliPorts(input) { return { host: input.port, api: input.apiPort, auth: input.authPort, ui: input.uiPort, uiSsr: void 0, pluginsStart: input.pluginPortStart, plugins: input.plugins }; } function allocateServices(cliPorts, plugins, configDir) { return effect.Effect.gen(function* () { const wKey = workspaceKey(configDir); const persisted = require_infra.loadPortState(configDir).devPorts; const allocator = yield* require_app.PortAllocator; const hostPort = yield* allocator.pickAvailable(cliPorts.host ?? persisted?.host ?? DEFAULT_HOST_PORT); const apiPort = yield* allocator.pickAvailable(cliPorts.api ?? persisted?.api ?? DEFAULT_API_PORT); const authPort = yield* allocator.pickAvailable(cliPorts.auth ?? persisted?.auth ?? DEFAULT_AUTH_PORT); const uiPort = yield* allocator.pickAvailable(cliPorts.ui ?? persisted?.ui ?? DEFAULT_UI_PORT); const uiSsrPort = yield* allocator.pickAvailable(cliPorts.uiSsr ?? uiPort + 1); const pluginApiPorts = {}; const pluginUiPorts = {}; const pluginKeys = Object.keys(plugins).sort(); const pluginStart = cliPorts.pluginsStart ?? persisted?.pluginPortStart ?? DEFAULT_PLUGIN_PORT_START; let nextPluginPort = pluginStart; for (const pluginId of pluginKeys) { const pluginCfg = plugins[pluginId]; const preferred = cliPorts.plugins?.[pluginId]?.api ?? nextPluginPort; const pluginPort = yield* allocator.pickAvailable(preferred); pluginApiPorts[pluginId] = pluginPort; nextPluginPort = pluginPort + 1; if (pluginCfg?.source === "local" && pluginCfg?.localPath && pluginCfg?.ui?.source === "local") { const uiPreferred = cliPorts.plugins?.[pluginId]?.ui ?? nextPluginPort; const pluginUiPort = yield* allocator.pickAvailable(uiPreferred); pluginUiPorts[pluginId] = pluginUiPort; nextPluginPort = pluginUiPort + 1; } } const resolved = { host: hostPort, api: apiPort, auth: authPort, ui: uiPort, uiSsr: uiSsrPort, plugins: Object.fromEntries(Object.entries(pluginApiPorts).map(([k, v]) => [k, { api: v, ui: pluginUiPorts[k] }])), postgres: {}, redis: {} }; const devPortsState = { host: hostPort, api: apiPort, auth: authPort, ui: uiPort, pluginPortStart: pluginStart }; const claimPorts = { host: hostPort, api: apiPort, auth: authPort, ui: uiPort, uiSsr: uiSsrPort }; for (const [id, port] of Object.entries(pluginApiPorts)) claimPorts[`plugin:${id}`] = port; for (const [id, port] of Object.entries(pluginUiPorts)) claimPorts[`plugin-ui:${id}`] = port; return { ports: resolved, claims: [{ resourceKey: `workspace:${wKey}`, pid: process.pid, configDir, ports: claimPorts, startedAt: Date.now() }], devPortsState }; }).pipe(effect.Effect.mapError((portErr) => new require_types.InfraError({ phase: "allocate-services", message: `Port allocation failed: ${String(portErr)}`, cause: portErr }))); } function allocateDatabases(runtimeConfig, configDir) { return effect.Effect.gen(function* () { const persisted = require_infra.loadPortState(configDir); const allSecrets = require_infra.getSecretGroups(runtimeConfig).flatMap((group) => group.secrets); const originMap = require_infra.buildOriginMap(configDir, runtimeConfig); const allocator = yield* require_app.PortAllocator; const infraDatabases = yield* effect.Effect.sync(() => require_infra.buildDatabaseConfigs(allSecrets, originMap, { ...persisted.postgresPorts })); const infraRedis = yield* effect.Effect.sync(() => require_infra.buildRedisConfigs(allSecrets, originMap, { ...persisted.redisPorts })); const postgres = {}; const dbs = []; for (const db of infraDatabases) { const port = yield* allocator.pickAvailable(db.port); postgres[db.slug] = port; dbs.push({ secret: db.secret, slug: db.slug, port, dbName: db.databaseName, containerName: db.containerName, volumeName: db.volumeName, url: `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${db.slug}_db` }); } const redis = {}; const redisPlans = []; for (const r of infraRedis) { const port = yield* allocator.pickAvailable(r.port); redis[r.slug] = port; redisPlans.push({ secret: r.secret, slug: r.slug, port, containerName: r.containerName, volumeName: r.volumeName, url: `redis://localhost:${port}` }); } return { postgres, redis, dbs, redisPlans }; }).pipe(effect.Effect.mapError((portErr) => new require_types.InfraError({ phase: "allocate-databases", message: `Database port allocation failed: ${String(portErr)}`, cause: portErr }))); } function buildServiceDescriptors(runtimeConfig, resolvedPorts) { const descriptors = []; if (runtimeConfig.host) { const isLocal = runtimeConfig.host.source === "local"; descriptors.push({ key: "host", source: runtimeConfig.host.source, url: isLocal ? `http://localhost:${resolvedPorts.host}` : runtimeConfig.host.url, port: isLocal ? resolvedPorts.host : void 0, localPath: isLocal ? runtimeConfig.host.localPath : void 0 }); } if (runtimeConfig.api) { const isLocal = runtimeConfig.api.source === "local"; descriptors.push({ key: "api", source: runtimeConfig.api.source, url: isLocal ? `http://localhost:${resolvedPorts.api}` : runtimeConfig.api.url, port: isLocal ? resolvedPorts.api : void 0, localPath: isLocal ? runtimeConfig.api.localPath : void 0 }); } if (runtimeConfig.auth) { const isLocal = runtimeConfig.auth.source === "local"; descriptors.push({ key: "auth", source: runtimeConfig.auth.source, url: isLocal ? `http://localhost:${resolvedPorts.auth}` : runtimeConfig.auth.url, port: isLocal ? resolvedPorts.auth : void 0, localPath: isLocal ? runtimeConfig.auth.localPath : void 0 }); } if (runtimeConfig.ui) { const isLocal = runtimeConfig.ui.source === "local"; descriptors.push({ key: "ui", source: runtimeConfig.ui.source, url: isLocal ? `http://localhost:${resolvedPorts.ui}` : runtimeConfig.ui.url, port: isLocal ? resolvedPorts.ui : void 0, localPath: isLocal ? runtimeConfig.ui.localPath : void 0 }); if (isLocal && resolvedPorts.uiSsr) descriptors.push({ key: "ui-ssr", source: "local", url: `http://localhost:${resolvedPorts.uiSsr}`, port: resolvedPorts.uiSsr, localPath: runtimeConfig.ui.localPath }); } if (runtimeConfig.plugins) for (const [pluginId, pluginCfg] of Object.entries(runtimeConfig.plugins)) { const pluginIsLocal = pluginCfg.source === "local"; const p = resolvedPorts.plugins[pluginId]; if (pluginIsLocal && p?.api) descriptors.push({ key: `plugin:${pluginId}`, source: "local", url: `http://localhost:${p.api}`, port: p.api, localPath: pluginCfg.localPath }); if (!pluginIsLocal && pluginCfg.url) descriptors.push({ key: `plugin:${pluginId}`, source: "remote", url: pluginCfg.url, port: void 0, localPath: void 0 }); if (pluginIsLocal && p?.ui && pluginCfg.ui?.source === "local") descriptors.push({ key: `plugin-ui:${pluginId}`, source: "local", url: `http://localhost:${p.ui}`, port: p.ui, localPath: pluginCfg.ui?.localPath }); } return descriptors; } function buildLaunchSpec(runtimeConfig, resolvedPorts) { const hostPort = runtimeConfig.host?.source === "local" ? resolvedPorts.host : runtimeConfig.host?.port; const corsOrigin = hostPort ? `http://localhost:${hostPort}` : `http://localhost:3000`; return { port: resolvedPorts.host, hostUrl: runtimeConfig.host?.url, corsOrigin, env: { ...resolvedPorts.host ? { PORT: String(resolvedPorts.host) } : {}, ...resolvedPorts.api ? { API_PORT: String(resolvedPorts.api) } : {}, ...resolvedPorts.ui ? { UI_PORT: String(resolvedPorts.ui) } : {}, ...resolvedPorts.auth ? { AUTH_PORT: String(resolvedPorts.auth) } : {} }, runtimeConfig }; } function buildComposeModel(dbs, redisPlans) { return { databases: dbs, redis: redisPlans }; } function buildEnvGenerated(resolvedPorts, dbs, redisPlans) { const env = {}; if (resolvedPorts.host) env.CORS_ORIGIN = `http://localhost:${resolvedPorts.host}`; for (const db of dbs) env[db.secret] = db.url; for (const r of redisPlans) env[r.secret] = r.url; return env; } function planInfra(input) { return effect.Effect.gen(function* () { const cliPorts = normalizeCliPorts(input.cli); const wKey = workspaceKey(input.configDir); const { ports: svcPorts, claims, devPortsState } = yield* allocateServices(cliPorts, input.bosConfig.plugins ?? {}, input.configDir); const { dbs, redisPlans, postgres: pgPorts, redis: rdPorts } = yield* allocateDatabases(input.bosConfig, input.configDir); const resolvedPorts = { ...svcPorts, postgres: pgPorts, redis: rdPorts }; require_infra.savePortState(input.configDir, { postgresPorts: pgPorts, redisPorts: rdPorts, devPorts: devPortsState }); const hostIsLocal = input.bosConfig.host?.source === "local"; const apiIsLocal = input.bosConfig.api?.source === "local"; const uiIsLocal = input.bosConfig.ui?.source === "local"; const authIsLocal = input.bosConfig.auth?.source === "local"; const assignedRuntimeConfig = { ...input.bosConfig, host: resolvedPorts.host ? { ...input.bosConfig.host, port: resolvedPorts.host, url: `http://localhost:${resolvedPorts.host}`, remoteUrl: !hostIsLocal ? input.bosConfig.host.remoteUrl ?? input.bosConfig.host.url : void 0 } : input.bosConfig.host, api: apiIsLocal && resolvedPorts.api ? { ...input.bosConfig.api, port: resolvedPorts.api, url: `http://localhost:${resolvedPorts.api}` } : input.bosConfig.api, ui: uiIsLocal && resolvedPorts.ui ? { ...input.bosConfig.ui, port: resolvedPorts.ui, url: `http://localhost:${resolvedPorts.ui}` } : input.bosConfig.ui, auth: authIsLocal && resolvedPorts.auth && input.bosConfig.auth ? { ...input.bosConfig.auth, port: resolvedPorts.auth, url: `http://localhost:${resolvedPorts.auth}` } : input.bosConfig.auth, plugins: input.bosConfig.plugins ? Object.fromEntries(Object.entries(input.bosConfig.plugins).map(([id, p]) => { const pluginPort = resolvedPorts.plugins[id]; if (p.source === "local" && pluginPort?.api) return [id, { ...p, port: pluginPort.api, url: `http://localhost:${pluginPort.api}` }]; return [id, p]; })) : void 0 }; const serviceDescriptors = buildServiceDescriptors(input.bosConfig, resolvedPorts); const launch = buildLaunchSpec(input.bosConfig, resolvedPorts); const composeModel = buildComposeModel(dbs, redisPlans); const envGenerated = buildEnvGenerated(resolvedPorts, dbs, redisPlans); const packages = serviceDescriptors.map((d) => d.key); const description = require_service_descriptor.buildDescription(new Map(serviceDescriptors.map((d) => [d.key, d]))); const orchestrator = { packages, env: {}, description, port: resolvedPorts.host, interactive: input.cli.interactive }; return { workspaceKey: wKey, cliPorts, resolvedPorts, runtimeConfig: assignedRuntimeConfig, launch, description, serviceDescriptors: new Map(serviceDescriptors.map((d) => [d.key, d])), envGenerated, composeModel, claims, orchestrator }; }); } //#endregion exports.planInfra = planInfra; //# sourceMappingURL=planner.cjs.map