UNPKG

@theguild/federation-composition

Version:

Open Source Composition library for Apollo Federation

228 lines (227 loc) 7.08 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DepGraphCycleError = exports.DepGraph = void 0; function createDFS(edges, leavesOnly, result, circular) { const visited = {}; return function (start) { if (visited[start]) { return; } const inCurrentPath = {}; const currentPath = []; const todo = []; todo.push({ node: start, processed: false }); while (todo.length > 0) { const current = todo[todo.length - 1]; const processed = current.processed; const node = current.node; if (!processed) { if (visited[node]) { todo.pop(); continue; } else if (inCurrentPath[node]) { if (circular) { todo.pop(); continue; } currentPath.push(node); throw new DepGraphCycleError(currentPath); } inCurrentPath[node] = true; currentPath.push(node); const nodeEdges = edges[node]; for (let i = nodeEdges.length - 1; i >= 0; i--) { todo.push({ node: nodeEdges[i], processed: false }); } current.processed = true; } else { todo.pop(); currentPath.pop(); inCurrentPath[node] = false; visited[node] = true; if (!leavesOnly || edges[node].length === 0) { result.push(node); } } } }; } class DepGraph { nodes = {}; outgoingEdges = {}; incomingEdges = {}; circular; constructor(opts) { this.circular = opts?.circular ?? false; } size() { return Object.keys(this.nodes).length; } addNode(name, data) { if (!this.hasNode(name)) { if (arguments.length === 2) { this.nodes[name] = data; } else { this.nodes[name] = name; } this.outgoingEdges[name] = []; this.incomingEdges[name] = []; } } removeNode(name) { if (this.hasNode(name)) { delete this.nodes[name]; delete this.outgoingEdges[name]; delete this.incomingEdges[name]; [this.incomingEdges, this.outgoingEdges].forEach(edgeList => { Object.keys(edgeList).forEach(key => { const idx = edgeList[key].indexOf(name); if (idx >= 0) { edgeList[key].splice(idx, 1); } }); }); } } hasNode(name) { return this.nodes.hasOwnProperty(name); } getNodeData(name) { if (this.hasNode(name)) { return this.nodes[name]; } else { throw new Error('Node does not exist: ' + name); } } setNodeData(name, data) { if (this.hasNode(name)) { this.nodes[name] = data; } else { throw new Error('Node does not exist: ' + name); } } addDependency(from, to) { if (!this.hasNode(from)) { throw new Error('Node does not exist: ' + from); } if (!this.hasNode(to)) { throw new Error('Node does not exist: ' + to); } if (this.outgoingEdges[from].indexOf(to) === -1) { this.outgoingEdges[from].push(to); } if (this.incomingEdges[to].indexOf(from) === -1) { this.incomingEdges[to].push(from); } return true; } removeDependency(from, to) { let idx; if (this.hasNode(from)) { idx = this.outgoingEdges[from].indexOf(to); if (idx >= 0) { this.outgoingEdges[from].splice(idx, 1); } } if (this.hasNode(to)) { idx = this.incomingEdges[to].indexOf(from); if (idx >= 0) { this.incomingEdges[to].splice(idx, 1); } } } directDependenciesOf(name) { if (this.hasNode(name)) { return this.outgoingEdges[name].slice(0); } else { throw new Error('Node does not exist: ' + name); } } directDependantsOf(name) { if (this.hasNode(name)) { return this.incomingEdges[name].slice(0); } else { throw new Error('Node does not exist: ' + name); } } dependenciesOf(name, leavesOnly) { if (this.hasNode(name)) { const result = []; const DFS = createDFS(this.outgoingEdges, leavesOnly, result, this.circular); DFS(name); const idx = result.indexOf(name); if (idx >= 0) { result.splice(idx, 1); } return result; } else { throw new Error('Node does not exist: ' + name); } } dependantsOf(name, leavesOnly) { if (this.hasNode(name)) { const result = []; const DFS = createDFS(this.incomingEdges, leavesOnly, result, this.circular); DFS(name); const idx = result.indexOf(name); if (idx >= 0) { result.splice(idx, 1); } return result; } else { throw new Error('Node does not exist: ' + name); } } overallOrder(leavesOnly) { const result = []; const keys = Object.keys(this.nodes); if (keys.length === 0) { return result; } else { if (!this.circular) { const CycleDFS = createDFS(this.outgoingEdges, false, [], this.circular); keys.forEach(function (n) { CycleDFS(n); }); } const DFS = createDFS(this.outgoingEdges, leavesOnly, result, this.circular); keys .filter(node => this.incomingEdges[node].length === 0) .forEach(n => { DFS(n); }); if (this.circular) { keys .filter(node => result.indexOf(node) === -1) .forEach(function (n) { DFS(n); }); } return result; } } entryNodes() { return Object.keys(this.nodes).filter(node => this.incomingEdges[node].length === 0); } directDependentsOf = this.directDependantsOf; dependentsOf = this.dependantsOf; } exports.DepGraph = DepGraph; class DepGraphCycleError extends Error { cyclePath; constructor(cyclePath) { super('Dependency Cycle Found: ' + cyclePath.join(' -> ')); this.cyclePath = cyclePath; } } exports.DepGraphCycleError = DepGraphCycleError;