everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
335 lines (333 loc) • 11.7 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_merge = require('../merge.cjs');
const require_types = require('../types.cjs');
const require_resolve = require('../descriptor/resolve.cjs');
const require_fastkv = require('../fastkv.cjs');
const require_config = require('../config.cjs');
const require_helpers = require('../cli/utils/helpers.cjs');
const require_cli_init = require('../cli/init.cjs');
let node_fs = require("node:fs");
let node_path = require("node:path");
let node_url = require("node:url");
//#region src/resolution/session.ts
async function defaultImportModule(path) {
return import(
/* webpackIgnore: true */
(0, node_url.pathToFileURL)(path).href
);
}
function resolveIo(io) {
return {
fetchBosConfig: io?.fetchBosConfig ?? (async (bosUrl, registry) => require_fastkv.fetchBosConfigFromFastKv(bosUrl, registry)),
readFileOrNull: io?.readFileOrNull ?? (async (path) => (0, node_fs.existsSync)(path) ? (0, node_fs.readFileSync)(path, "utf-8") : null),
importModule: io?.importModule ?? defaultImportModule
};
}
async function loadNodeConfig(ref, baseDir, io, registry) {
if (ref.startsWith("bos://")) return await io.fetchBosConfig(ref, registry);
const resolvedPath = (0, node_path.isAbsolute)(ref) ? ref : (0, node_path.resolve)(baseDir, ref);
if (require_config.isAppDescriptorPath(resolvedPath)) return io.importModule === defaultImportModule ? require_config.loadAppDescriptorConfig(resolvedPath) : require_config.parseAppDescriptorModule(await io.importModule(resolvedPath), resolvedPath);
const content = await io.readFileOrNull(resolvedPath);
if (content === null) throw new require_config.ConfigLoadError({
path: resolvedPath,
message: `Config file not found: ${resolvedPath}`
});
return JSON.parse(content);
}
async function walkExtends(ref, baseDir, visited, chain, options) {
if (visited.has(ref)) throw new require_config.CircularExtendsError({
chain: [...visited, ref],
message: `Circular extends detected: ${[...visited, ref].join(" -> ")}`
});
let config;
let nodeBaseDir = baseDir;
let sourceDir;
if (ref.startsWith("bos://") && options.collectCatalogs) {
const parsed = require_helpers.parseBosRef(ref);
if (!parsed) throw new require_config.ConfigLoadError({
path: ref,
message: `Invalid bos ref: ${ref}`
});
const sourceResult = await require_cli_init.resolveSourceDir({
extendsAccount: parsed.account,
extendsGateway: parsed.gateway
});
config = sourceResult.parentConfig;
sourceDir = sourceResult.sourceDir || void 0;
options.registerCleanup?.(sourceResult.cleanup);
nodeBaseDir = sourceDir ?? baseDir;
} else if (ref.startsWith("bos://")) config = await loadNodeConfig(ref, baseDir, options.io, options.registry);
else {
const resolvedPath = (0, node_path.isAbsolute)(ref) ? ref : (0, node_path.resolve)(baseDir, ref);
nodeBaseDir = (0, node_path.dirname)(resolvedPath);
config = await loadNodeConfig(ref, nodeBaseDir, options.io, options.registry);
}
chain.push(ref);
const extendsRef = config.extends ? require_merge.resolveExtendsRef(config.extends, options.env) : void 0;
if (!extendsRef) {
await options.visit?.({
ref,
config,
baseDir: nodeBaseDir,
sourceDir,
isLeaf: true
});
return config;
}
const parsedParentRef = require_config.parseExtendsTarget(extendsRef);
const parentBaseDir = require_config.getConfigBaseDir(parsedParentRef.configPath, nodeBaseDir);
if (options.collectCatalogs && !canResolveExtendsRef(parsedParentRef.configPath, sourceDir ?? nodeBaseDir)) {
await options.visit?.({
ref,
config,
baseDir: nodeBaseDir,
sourceDir,
isLeaf: true
});
return config;
}
const nextVisited = new Set(visited);
nextVisited.add(ref);
const parent = await walkExtends(parsedParentRef.configPath, parentBaseDir, nextVisited, chain, options);
await options.visit?.({
ref,
config,
baseDir: nodeBaseDir,
sourceDir,
isLeaf: false
});
return require_merge.mergeBosConfigWithExtends(parent, config);
}
function canResolveExtendsRef(parentRef, baseDir) {
if (parentRef.startsWith("bos://")) return require_helpers.parseBosRef(parentRef) !== null;
if (!baseDir) return false;
return (0, node_fs.existsSync)((0, node_path.resolve)(baseDir, parentRef));
}
function dispatchEntry(request, cwd) {
if (request?.path) {
const configPath = request.path;
if (configPath.startsWith("bos://")) return {
entry: configPath,
baseDir: cwd,
root: cwd,
path: configPath,
remote: true
};
const baseDir = (0, node_path.dirname)(configPath);
return {
entry: configPath,
baseDir,
root: baseDir,
path: configPath,
remote: false
};
}
if (request?.bosUrl) return {
entry: request.bosUrl,
baseDir: cwd,
root: cwd,
path: null,
remote: true
};
if (request?.account) {
if (!request.gateway) throw new require_config.ConfigExtendsError({ message: `Remote account "${request.account}" requires a gateway` });
return {
entry: `bos://${request.account}/${request.gateway}`,
baseDir: cwd,
root: cwd,
path: null,
remote: true
};
}
if (request?.sourceDir) {
const sourceRoot = (0, node_path.resolve)(request.sourceDir);
const configPath = require_config.findConfigPath(sourceRoot);
if (!configPath) return null;
return {
entry: configPath,
baseDir: (0, node_path.dirname)(configPath),
root: sourceRoot,
path: configPath,
remote: false
};
}
const configPath = require_config.findConfigPath(cwd);
if (!configPath) return null;
const baseDir = (0, node_path.dirname)(configPath);
return {
entry: configPath,
baseDir,
root: baseDir,
path: configPath,
remote: false
};
}
function isConfigNotFound(error) {
return error instanceof Error && error.message.startsWith("No config found for ");
}
var ResolutionSession = class ResolutionSession {
config;
runtime;
root;
path;
chain;
rawConfig;
catalog;
repository;
#env;
#warnings;
constructor(parts) {
this.config = parts.config;
this.runtime = parts.runtime;
this.root = parts.root;
this.path = parts.path;
this.chain = parts.chain;
this.rawConfig = parts.rawConfig;
this.catalog = parts.catalog;
this.repository = parts.repository;
this.#env = parts.env;
this.#warnings = parts.warnings;
}
static fromParts(parts) {
return new ResolutionSession({
config: parts.config,
runtime: parts.runtime,
root: parts.root,
path: parts.source?.path ?? null,
chain: parts.source?.extended ?? [],
rawConfig: null,
catalog: null,
repository: void 0,
env: require_config.defaultConfigEnv(),
warnings: [...parts.warnings ?? []]
});
}
get warnings() {
const drained = [...this.#warnings];
this.#warnings.length = 0;
return drained;
}
async buildRuntime(sources) {
if (!this.config) throw new require_config.ConfigNotfoundError({ message: "No config loaded in this resolution session" });
const config = this.config;
const env = sources?.env ?? this.#env;
return require_config.runWithConfigWarningSink(this.#warnings, () => require_config.buildRuntimeConfig(config, this.root, env, {
plugins: sources?.plugins ?? {},
uiSource: sources?.uiSource,
apiSource: sources?.apiSource,
authSource: sources?.authSource,
hostSource: sources?.hostSource
}));
}
};
/**
* Opens a resolution session for the requested config source. In development,
* a local leaf's `bos.dev.ts` overlay (beside the discovered entry config) is
* merged child-wins over the extended input before parsing; overlays are never
* read for `bos://` entries or in production/staging.
*/
async function openResolution(request, io) {
const resolvedIo = resolveIo(io);
const env = request?.env ?? require_config.defaultConfigEnv();
const runtimeEnv = env === "staging" ? "production" : env;
const cwd = request?.cwd ?? process.cwd();
const collectCatalogs = request?.collectCatalogs === true;
const warnings = [];
const cleanups = [];
try {
const parts = await require_config.runWithConfigWarningSink(warnings, async () => {
const dispatch = dispatchEntry(request, cwd);
if (!dispatch) return null;
const chain = [];
const catalogs = [];
let repository;
let rawConfig;
try {
const mergedRaw = await walkExtends(dispatch.entry, dispatch.baseDir, /* @__PURE__ */ new Set(), chain, {
env,
registry: request?.registry,
io: resolvedIo,
collectCatalogs,
registerCleanup: (fn) => cleanups.push(fn),
visit: async (link) => {
if (link.ref === dispatch.entry) rawConfig = link.config;
if (!collectCatalogs) return;
catalogs.push(link.sourceDir ? require_cli_init.readWorkspaceCatalog(link.sourceDir) : {});
const repositoryValue = link.config.repository;
if (typeof repositoryValue === "string") repository = repositoryValue;
}
});
let overlayMerged = mergedRaw;
if (env === "development" && !dispatch.remote) {
const overlayPath = (0, node_path.join)((0, node_path.dirname)(dispatch.entry), require_config.DEV_OVERLAY_FILENAME);
if (await resolvedIo.readFileOrNull(overlayPath) !== null) {
const overlay = require_config.parseDevOverlayModule(await resolvedIo.importModule(overlayPath), overlayPath);
overlayMerged = require_resolve.applyDevOverlay(mergedRaw, overlay);
}
}
const config = await require_config.resolveConfigComposableEntries(require_types.BosConfigSchema.parse(overlayMerged), dispatch.baseDir, runtimeEnv);
const pluginRuntime = await require_config.buildRuntimePluginsForConfig(config, dispatch.baseDir, runtimeEnv, request?.remotePlugins) ?? {};
return {
config,
runtime: await require_config.buildRuntimeConfig(config, dispatch.baseDir, runtimeEnv, { plugins: pluginRuntime }),
root: dispatch.root,
path: dispatch.path,
chain,
rawConfig: rawConfig ?? null,
catalog: collectCatalogs ? Object.assign({}, ...[...catalogs].reverse()) : null,
repository
};
} catch (error) {
if (dispatch.remote && isConfigNotFound(error)) return null;
throw error;
}
});
if (!parts) return null;
return new ResolutionSession({
...parts,
env: runtimeEnv,
warnings
});
} finally {
for (const cleanup of [...cleanups].reverse()) await cleanup();
}
}
async function walkExtendsChain(entry, options) {
const baseDir = entry.startsWith("bos://") ? process.cwd() : (0, node_path.dirname)(entry);
const chain = [];
const ownedCleanups = [];
try {
return {
chain,
config: await walkExtends(entry, baseDir, /* @__PURE__ */ new Set(), chain, {
env: options.env ?? "development",
registry: options.registry,
io: resolveIo(options.io),
collectCatalogs: options.collectCatalogs === true,
visit: options.visit,
registerCleanup: options.registerCleanup ?? ((fn) => ownedCleanups.push(fn))
})
};
} finally {
for (const cleanup of [...ownedCleanups].reverse()) await cleanup();
}
}
/**
* @internal — resolves the fully merged (parent-merged, un-parsed)
* `BosConfigInput` for a config entry. Used by config.ts's
* `resolveComposableReference` for plugin-entry extends resolution.
*/
async function resolveMergedConfigInput(entry, options) {
return walkExtends(entry, options?.baseDir ?? process.cwd(), /* @__PURE__ */ new Set(), [], {
env: options?.env ?? "development",
registry: options?.registry,
io: resolveIo(options?.io),
collectCatalogs: false
});
}
//#endregion
exports.ResolutionSession = ResolutionSession;
exports.openResolution = openResolution;
exports.resolveMergedConfigInput = resolveMergedConfigInput;
exports.walkExtendsChain = walkExtendsChain;
//# sourceMappingURL=session.cjs.map