everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
380 lines (378 loc) • 14.3 kB
JavaScript
const require_service_descriptor = require('../service-descriptor.cjs');
const require_app = require('../app.cjs');
const require_infra = require('../cli/infra.cjs');
const require_types = require('./types.cjs');
const require_materializer = require('./materializer.cjs');
const require_port_ownership = require('./port-ownership.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 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,
pluginsStart: input.pluginPortStart,
plugins: input.plugins
};
}
const PLUGIN_START_OFFSET = 10;
const BLOCK_STEP = 100;
function allocateServices(cliPorts, plugins, configDir, auth, sources) {
return effect.Effect.gen(function* () {
const wKey = workspaceKey(configDir);
const persisted = require_infra.loadPortState(configDir).devPorts;
const allocator = yield* require_app.PortAllocator;
const base = cliPorts.host ?? persisted?.host ?? DEFAULT_HOST_PORT;
const pluginStartPreferred = cliPorts.pluginsStart ?? persisted?.pluginPortStart ?? base + PLUGIN_START_OFFSET;
const pluginStartPinned = cliPorts.pluginsStart !== void 0 || persisted?.pluginPortStart !== void 0;
const entries = [];
const addEntry = (key, flag, persistedValue, derived) => {
if (flag !== void 0) entries.push({
key,
preferred: flag,
pinned: true
});
else if (persistedValue !== void 0) entries.push({
key,
preferred: persistedValue,
pinned: true
});
else entries.push({
key,
preferred: derived,
pinned: false
});
};
addEntry("host", cliPorts.host, void 0, base);
if (sources?.api !== "remote") addEntry("api", cliPorts.api, persisted?.api, base + 1);
if (sources?.auth !== "remote") addEntry("auth", cliPorts.auth, persisted?.auth, base + 2);
if (sources?.ui !== "remote") addEntry("ui", cliPorts.ui, persisted?.ui, base + 3);
const pluginKeys = Object.keys(plugins).sort();
const isAuthMirrorFor = (pluginId, pluginCfg) => pluginCfg !== void 0 && require_service_descriptor.isAuthMirrorPluginEntry(auth, pluginId, pluginCfg);
let pluginSlot = 0;
for (const pluginId of pluginKeys) {
const pluginCfg = plugins[pluginId];
const pluginIsLocal = pluginCfg?.source === "local";
const slotBase = pluginStartPinned ? pluginStartPreferred : base + PLUGIN_START_OFFSET;
if (!isAuthMirrorFor(pluginId, pluginCfg) && pluginIsLocal) {
addEntry(`plugin:${pluginId}`, cliPorts.plugins?.[pluginId]?.api, void 0, slotBase + pluginSlot);
pluginSlot += 1;
}
if (pluginCfg?.source === "local" && pluginCfg?.localPath && pluginCfg?.ui?.source === "local") {
addEntry(`plugin-ui:${pluginId}`, cliPorts.plugins?.[pluginId]?.ui, void 0, slotBase + pluginSlot);
pluginSlot += 1;
}
}
const allocation = yield* allocator.acquireBlock({
base,
step: BLOCK_STEP,
entries
});
if (allocation.base !== base) {
yield* effect.Effect.logError(`[Dev] Preferred port block starting at ${base} is occupied — using ${allocation.base} (this run only; restarts will retry ${base})`);
for (const conflict of allocation.conflicts.slice(0, 6)) if (conflict.claimed) yield* effect.Effect.logError(`[Dev] port ${conflict.port} (${conflict.key}) — live bos session (registry claim)`);
else {
const owner = yield* require_port_ownership.ownerOfPort(conflict.port);
yield* effect.Effect.logError(`[Dev] port ${conflict.port} (${conflict.key}) — ${owner ? `pid ${owner.pid} (${owner.command})` : "foreign holder (lsof unavailable)"}`);
}
}
const pluginApiPorts = {};
const pluginUiPorts = {};
for (const pluginId of pluginKeys) {
const api = allocation.ports[`plugin:${pluginId}`];
if (api !== void 0) pluginApiPorts[pluginId] = api;
const ui = allocation.ports[`plugin-ui:${pluginId}`];
if (ui !== void 0) pluginUiPorts[pluginId] = ui;
}
const resolved = {
host: allocation.ports.host,
api: allocation.ports.api,
auth: allocation.ports.auth,
ui: allocation.ports.ui,
plugins: Object.fromEntries(pluginKeys.map((k) => [k, {
api: pluginApiPorts[k],
ui: pluginUiPorts[k]
}]))
};
const devPortsState = {
host: cliPorts.host,
api: cliPorts.api,
auth: cliPorts.auth,
ui: cliPorts.ui,
pluginPortStart: cliPorts.pluginsStart
};
const claimPorts = {};
for (const [key, port] of Object.entries(allocation.ports)) claimPorts[key] = port;
return {
ports: resolved,
claims: [{
resourceKey: `workspace:${wKey}`,
pid: process.pid,
configDir,
ports: claimPorts,
startedAt: yield* effect.Clock.currentTimeMillis
}],
devPortsState
};
}).pipe(effect.Effect.mapError((portErr) => new require_types.InfraError({
phase: "allocate-services",
message: `Port allocation failed: ${String(portErr)}`,
cause: portErr
})));
}
function allocateDatabases(runtimeConfig) {
return effect.Effect.gen(function* () {
const allSecrets = require_infra.getSecretGroups(runtimeConfig).flatMap((group) => group.secrets);
const allocator = yield* require_app.PortAllocator;
const conventionalDatabases = require_infra.buildConventionalDatabases(allSecrets);
const conventionalRedis = require_infra.buildConventionalRedis(allSecrets);
const dbs = [];
const allocatedByDesired = /* @__PURE__ */ new Map();
for (const db of conventionalDatabases) {
let port;
if (allocatedByDesired.has(db.port)) port = allocatedByDesired.get(db.port);
else {
port = yield* allocator.pickAvailable(db.port);
allocatedByDesired.set(db.port, port);
}
dbs.push({
secret: db.secret,
slug: db.slug,
port,
dbName: db.databaseName,
url: `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${db.databaseName}`
});
}
const redisPlans = [];
for (const r of conventionalRedis) {
const port = yield* allocator.pickAvailable(r.port);
redisPlans.push({
secret: r.secret,
slug: r.slug,
port,
url: `redis://localhost:${port}`
});
}
return {
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 (runtimeConfig.plugins) for (const [pluginId, pluginCfg] of Object.entries(runtimeConfig.plugins)) {
const pluginIsLocal = pluginCfg.source === "local";
const p = resolvedPorts.plugins[pluginId];
const isAuthMirror = require_service_descriptor.isAuthMirrorPluginEntry(runtimeConfig.auth, pluginId, pluginCfg);
if (pluginIsLocal && p?.api && !isAuthMirror) descriptors.push({
key: `plugin:${pluginId}`,
source: "local",
url: `http://localhost:${p.api}`,
port: p.api,
localPath: pluginCfg.localPath
});
if (!pluginIsLocal && pluginCfg.url && !isAuthMirror) 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 buildEnvGenerated(resolvedPorts, dbs, redisPlans) {
const env = {};
if (resolvedPorts.host) {
env.CORS_ORIGIN = `http://localhost:${resolvedPorts.host}`;
env.BASE_URL = `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, input.bosConfig.auth, {
host: input.bosConfig.host?.source,
api: input.bosConfig.api?.source,
auth: input.bosConfig.auth?.source,
ui: input.bosConfig.ui?.source
});
const { dbs, redisPlans } = yield* allocateDatabases(input.bosConfig);
const resolvedPorts = { ...svcPorts };
const previousDevPorts = require_infra.loadPortState(input.configDir).devPorts ?? {};
const devPorts = {
host: devPortsState.host ?? previousDevPorts.host,
api: devPortsState.api ?? previousDevPorts.api,
auth: devPortsState.auth ?? previousDevPorts.auth,
ui: devPortsState.ui ?? previousDevPorts.ui,
pluginPortStart: devPortsState.pluginPortStart ?? previousDevPorts.pluginPortStart
};
if (require_materializer.shouldPersistPortState()) require_infra.savePortState(input.configDir, { devPorts });
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}`,
ssrUrl: void 0
} : 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 isAuthMirror = require_service_descriptor.isAuthMirrorPluginEntry(input.bosConfig.auth, id, p);
const pluginPort = resolvedPorts.plugins[id];
const patchedUi = (() => {
if (p.ui?.source !== "local" || !p.ui?.localPath || !pluginPort?.ui) return p.ui;
return {
...p.ui,
port: pluginPort.ui,
url: `http://localhost:${pluginPort.ui}`,
ssrUrl: void 0
};
})();
if (isAuthMirror) return [id, {
...p,
ui: patchedUi
}];
if (p.source === "local" && pluginPort?.api) return [id, {
...p,
port: pluginPort.api,
url: `http://localhost:${pluginPort.api}`,
ui: patchedUi
}];
return [id, p];
})) : void 0
};
const serviceDescriptors = buildServiceDescriptors(assignedRuntimeConfig, resolvedPorts);
const launch = buildLaunchSpec(input.bosConfig, resolvedPorts);
const envGenerated = buildEnvGenerated(resolvedPorts, dbs, redisPlans);
const packages = serviceDescriptors.map((d) => d.key);
const descriptionMap = new Map(serviceDescriptors.map((d) => [d.key, d]));
const description = require_service_descriptor.buildDescription(descriptionMap);
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,
claims,
orchestrator
};
});
}
//#endregion
exports.buildEnvGenerated = buildEnvGenerated;
exports.buildLaunchSpec = buildLaunchSpec;
exports.buildServiceDescriptors = buildServiceDescriptors;
exports.planInfra = planInfra;
exports.workspaceKey = workspaceKey;
//# sourceMappingURL=planner.cjs.map