UNPKG

everything-dev

Version:

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

353 lines (351 loc) • 12.6 kB
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_services_registry_fastkv = require('../registry/fastkv.cjs'); const require_load = require('./load.cjs'); const require_runtime = require('./runtime.cjs'); const require_bos_ref = require('../../utils/bos-ref.cjs'); const require_catalog = require('../workspace/catalog.cjs'); const require_init_source = require('./init-source.cjs'); const require_unknown_keys = require('./unknown-keys.cjs'); let node_fs = require("node:fs"); let node_path = require("node:path"); let node_url = require("node:url"); let effect = require("effect"); //#region src/services/resolution/session.ts var ResolutionSessionService = class extends effect.Context.Service()("bos/ResolutionSessionService") {}; 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_services_registry_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_load.isAppDescriptorPath(resolvedPath)) return io.importModule === defaultImportModule ? require_load.loadAppDescriptorConfig(resolvedPath) : require_load.parseAppDescriptorModule(await io.importModule(resolvedPath), resolvedPath); const content = await io.readFileOrNull(resolvedPath); if (content === null) throw new require_load.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_load.CircularExtendsError({ chain: [...visited, ref], message: `Circular extends detected: ${[...visited, ref].join(" -> ")}` }); let config; let nodeBaseDir = baseDir; let sourceDir; if (options.seedConfig) config = options.seedConfig; else if (ref.startsWith("bos://") && options.collectCatalogs) { const parsed = require_bos_ref.parseBosRef(ref); if (!parsed) throw new require_load.ConfigLoadError({ path: ref, message: `Invalid bos ref: ${ref}` }); const sourceResult = await require_init_source.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_load.parseExtendsTarget(extendsRef); const parentBaseDir = require_load.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, seedConfig: void 0 }); 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_bos_ref.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_load.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_load.findConfigPath(sourceRoot); if (!configPath) return null; return { entry: configPath, baseDir: (0, node_path.dirname)(configPath), root: sourceRoot, path: configPath, remote: false }; } const configPath = require_load.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; unknownAuthoredKeys; 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.unknownAuthoredKeys = parts.unknownAuthoredKeys ?? []; 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, unknownAuthoredKeys: [], catalog: null, repository: void 0, env: require_load.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_load.ConfigNotfoundError({ message: "No config loaded in this resolution session" }); const config = this.config; const env = sources?.env ?? this.#env; return require_load.runWithConfigWarningSink(this.#warnings, () => require_runtime.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_load.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_load.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_catalog.readWorkspaceCatalog(link.sourceDir) : {}); const repositoryValue = link.config.repository; if (typeof repositoryValue === "string") repository = repositoryValue; } }); let overlayMerged = mergedRaw; let devOverlay; if (env === "development" && !dispatch.remote) { const overlayPath = (0, node_path.join)((0, node_path.dirname)(dispatch.entry), require_load.DEV_OVERLAY_FILENAME); if (await resolvedIo.readFileOrNull(overlayPath) !== null) { devOverlay = require_load.parseDevOverlayModule(await resolvedIo.importModule(overlayPath), overlayPath); overlayMerged = require_resolve.applyDevOverlay(mergedRaw, devOverlay); } } const unknownAuthoredKeys = [...require_unknown_keys.collectUnknownKeys(rawConfig ?? null, require_types.BosConfigSchema), ...devOverlay ? require_unknown_keys.collectUnknownKeys(devOverlay, require_types.BosConfigSchema) : []]; const config = await require_load.resolveConfigComposableEntries(require_types.BosConfigSchema.parse(overlayMerged), dispatch.baseDir, runtimeEnv); const pluginRuntime = await require_runtime.buildRuntimePluginsForConfig(config, dispatch.baseDir, runtimeEnv, request?.remotePlugins) ?? {}; return { config, runtime: await require_runtime.buildRuntimeConfig(config, dispatch.baseDir, runtimeEnv, { plugins: pluginRuntime }), root: dispatch.root, path: dispatch.path, chain, rawConfig: rawConfig ?? null, unknownAuthoredKeys, 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, seedConfig: options.seedConfig, 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, seedConfig: options?.seedConfig }); } //#endregion exports.ResolutionSession = ResolutionSession; exports.ResolutionSessionService = ResolutionSessionService; exports.openResolution = openResolution; exports.resolveMergedConfigInput = resolveMergedConfigInput; exports.walkExtendsChain = walkExtendsChain; //# sourceMappingURL=session.cjs.map