UNPKG

everything-dev

Version:

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

167 lines (166 loc) 5.33 kB
//#region src/dag.ts function normalizeToNodes(config) { const nodes = /* @__PURE__ */ new Map(); if (config.api?.url) nodes.set("api", { key: "api", kind: "api", name: config.api.name, url: config.api.url, entry: config.api.entry, source: config.api.source, localPath: config.api.localPath, port: config.api.port, proxy: config.api.proxy, variables: config.api.variables, secrets: config.api.secrets, integrity: config.api.integrity, shared: config.api.shared, dependsOn: config.api.dependsOn, sourceOrigin: "config", singletonKey: `api:${config.api.url}` }); if (config.auth?.url) nodes.set("auth", { key: "auth", kind: "auth", name: config.auth.name, url: config.auth.url, entry: config.auth.entry, source: config.auth.source, extendsRef: config.auth.extendsRef, localPath: config.auth.localPath, port: config.auth.port, proxy: config.auth.proxy, variables: config.auth.variables, secrets: config.auth.secrets, integrity: config.auth.integrity, shared: config.auth.shared, dependsOn: config.auth.dependsOn, sourceOrigin: "config", singletonKey: `auth:${config.auth.url}` }); if (config.ui?.url) nodes.set("ui", { key: "ui", kind: "ui", name: config.ui.name, url: config.ui.url, entry: config.ui.entry, source: config.ui.source, localPath: config.ui.localPath, port: config.ui.port, integrity: config.ui.integrity, dependsOn: config.ui.dependsOn, sourceOrigin: "config", singletonKey: `ui:${config.ui.url}` }); for (const [key, plugin] of Object.entries(config.plugins ?? {})) { if (!plugin.url) continue; nodes.set(key, { key, kind: "plugin", name: plugin.name, url: plugin.url, entry: plugin.entry, source: plugin.source, extendsRef: plugin.extendsRef, localPath: plugin.localPath, port: plugin.port, proxy: plugin.proxy, variables: plugin.variables, secrets: plugin.secrets, integrity: plugin.integrity, shared: plugin.shared, ui: plugin.ui, routes: plugin.routes, dependsOn: plugin.dependsOn, sourceOrigin: "config", singletonKey: `plugin:${key}:${plugin.url}` }); } return nodes; } function manifestPluginsToNodes(plugins) { if (!plugins?.length) return []; return plugins.map((p) => ({ key: p.key, kind: "plugin", name: p.name, url: p.url, entry: `${p.url}/mf-manifest.json`, source: "remote", dependsOn: p.dependsOn, secrets: p.secrets, variables: p.variables, sourceOrigin: "manifest", singletonKey: `plugin:${p.key}:${p.url}` })); } function buildDependencyDAG(config) { const nodes = config.nodes && Object.keys(config.nodes).length > 0 ? new Map(Object.entries(config.nodes)) : normalizeToNodes(config); return { nodes, sorted: topologicalSort(nodes) }; } function getImplicitApiDependencies(nodes) { return [...nodes.values()].filter((n) => n.kind !== "api" && n.kind !== "ui").map((n) => n.key); } function topologicalSort(nodes) { const inDegree = /* @__PURE__ */ new Map(); const adjacency = /* @__PURE__ */ new Map(); for (const key of nodes.keys()) { inDegree.set(key, 0); adjacency.set(key, /* @__PURE__ */ new Set()); } for (const [key, node] of nodes) for (const dep of node.dependsOn ?? []) if (nodes.has(dep)) { adjacency.get(dep).add(key); inDegree.set(key, (inDegree.get(key) ?? 0) + 1); } const apiNode = nodes.get("api"); if (apiNode && !apiNode.dependsOn?.length) { for (const depKey of getImplicitApiDependencies(nodes)) if (!adjacency.get(depKey).has("api")) { adjacency.get(depKey).add("api"); inDegree.set("api", (inDegree.get("api") ?? 0) + 1); } } const queue = []; for (const [key, degree] of inDegree) if (degree === 0) queue.push(key); queue.sort(); const result = []; while (queue.length > 0) { const current = queue.shift(); result.push(current); const neighbors = adjacency.get(current); if (neighbors) for (const neighbor of neighbors) { const newDegree = (inDegree.get(neighbor) ?? 0) - 1; inDegree.set(neighbor, newDegree); if (newDegree === 0) { const insertPos = queue.findIndex((k) => k > neighbor); if (insertPos === -1) queue.push(neighbor); else queue.splice(insertPos, 0, neighbor); } } } if (result.length !== nodes.size) { const cyclic = [...nodes.keys()].filter((k) => !result.includes(k)); throw new Error(`Circular dependency detected among: ${cyclic.join(" -> ")}. Check dependsOn declarations in bos.config.json.`); } return result; } function mergeManifestNodes(configNodes, manifestNodes) { const merged = new Map(configNodes); for (const node of manifestNodes) { if (merged.has(node.key) && merged.get(node.key).sourceOrigin === "config") continue; merged.set(node.key, node); } return merged; } function getDependenciesForNode(node, allNodes) { if (node.kind === "api" && !node.dependsOn?.length) return getImplicitApiDependencies(allNodes).map((k) => allNodes.get(k)).filter(Boolean); return (node.dependsOn ?? []).map((k) => allNodes.get(k)).filter(Boolean); } function getSingletonKey(node) { return node.singletonKey ?? `${node.kind}:${node.key}:${node.url}`; } //#endregion export { buildDependencyDAG, getDependenciesForNode, getImplicitApiDependencies, getSingletonKey, manifestPluginsToNodes, mergeManifestNodes, normalizeToNodes, topologicalSort }; //# sourceMappingURL=dag.mjs.map