2018-08-17 05:20:21 -05:00
|
|
|
export class Edge {
|
2021-04-15 07:21:06 -05:00
|
|
|
inputNode?: Node;
|
|
|
|
outputNode?: Node;
|
2018-08-17 05:20:21 -05:00
|
|
|
|
2019-08-01 07:38:34 -05:00
|
|
|
_linkTo(node: Node, direction: number) {
|
2018-08-17 05:20:21 -05:00
|
|
|
if (direction <= 0) {
|
|
|
|
node.inputEdges.push(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direction >= 0) {
|
|
|
|
node.outputEdges.push(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
node.edges.push(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
link(inputNode: Node, outputNode: Node) {
|
2018-10-12 07:15:44 -05:00
|
|
|
if (!inputNode) {
|
|
|
|
throw Error('inputNode is required');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!outputNode) {
|
|
|
|
throw Error('outputNode is required');
|
|
|
|
}
|
|
|
|
|
2018-08-17 05:20:21 -05:00
|
|
|
this.unlink();
|
|
|
|
this.inputNode = inputNode;
|
|
|
|
this.outputNode = outputNode;
|
|
|
|
|
|
|
|
this._linkTo(inputNode, 1);
|
|
|
|
this._linkTo(outputNode, -1);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
unlink() {
|
|
|
|
let pos;
|
2018-08-26 10:14:40 -05:00
|
|
|
const inode = this.inputNode;
|
|
|
|
const onode = this.outputNode;
|
2018-08-17 05:20:21 -05:00
|
|
|
|
|
|
|
if (!(inode && onode)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = inode.edges.indexOf(this);
|
|
|
|
if (pos > -1) {
|
|
|
|
inode.edges.splice(pos, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = onode.edges.indexOf(this);
|
|
|
|
if (pos > -1) {
|
|
|
|
onode.edges.splice(pos, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = inode.outputEdges.indexOf(this);
|
|
|
|
if (pos > -1) {
|
|
|
|
inode.outputEdges.splice(pos, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = onode.inputEdges.indexOf(this);
|
|
|
|
if (pos > -1) {
|
|
|
|
onode.inputEdges.splice(pos, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Node {
|
|
|
|
name: string;
|
|
|
|
edges: Edge[];
|
|
|
|
inputEdges: Edge[];
|
|
|
|
outputEdges: Edge[];
|
|
|
|
|
|
|
|
constructor(name: string) {
|
|
|
|
this.name = name;
|
|
|
|
this.edges = [];
|
|
|
|
this.inputEdges = [];
|
|
|
|
this.outputEdges = [];
|
|
|
|
}
|
|
|
|
|
Chore: Fix all Typescript strict null errors (#26204)
* Chore: Fix typescript strict null errors
* Added new limit
* Fixed ts issue
* fixed tests
* trying to fix type inference
* Fixing more ts errors
* Revert tsconfig option
* Fix
* Fixed code
* More fixes
* fix tests
* Updated snapshot
* Chore: More ts strict null fixes
* More fixes in some really messed up azure config components
* More fixes, current count: 441
* 419
* More fixes
* Fixed invalid initial state in explore
* Fixing tests
* Fixed tests
* Explore fix
* More fixes
* Progress
* Sub 300
* Now at 218
* Progress
* Update
* Progress
* Updated tests
* at 159
* fixed tests
* Progress
* YAy blow 100! at 94
* 10,9,8,7,6,5,4,3,2,1... lift off
* Fixed tests
* Fixed more type errors
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-07-10 05:46:59 -05:00
|
|
|
getEdgeFrom(from: string | Node): Edge | null | undefined {
|
2018-08-17 05:20:21 -05:00
|
|
|
if (!from) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof from === 'object') {
|
2021-04-15 07:21:06 -05:00
|
|
|
return this.inputEdges.find((e) => e.inputNode?.name === from.name);
|
2018-08-17 05:20:21 -05:00
|
|
|
}
|
|
|
|
|
2021-04-15 07:21:06 -05:00
|
|
|
return this.inputEdges.find((e) => e.inputNode?.name === from);
|
2018-08-17 05:20:21 -05:00
|
|
|
}
|
|
|
|
|
Chore: Fix all Typescript strict null errors (#26204)
* Chore: Fix typescript strict null errors
* Added new limit
* Fixed ts issue
* fixed tests
* trying to fix type inference
* Fixing more ts errors
* Revert tsconfig option
* Fix
* Fixed code
* More fixes
* fix tests
* Updated snapshot
* Chore: More ts strict null fixes
* More fixes in some really messed up azure config components
* More fixes, current count: 441
* 419
* More fixes
* Fixed invalid initial state in explore
* Fixing tests
* Fixed tests
* Explore fix
* More fixes
* Progress
* Sub 300
* Now at 218
* Progress
* Update
* Progress
* Updated tests
* at 159
* fixed tests
* Progress
* YAy blow 100! at 94
* 10,9,8,7,6,5,4,3,2,1... lift off
* Fixed tests
* Fixed more type errors
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-07-10 05:46:59 -05:00
|
|
|
getEdgeTo(to: string | Node): Edge | null | undefined {
|
2018-08-17 05:20:21 -05:00
|
|
|
if (!to) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof to === 'object') {
|
2021-04-15 07:21:06 -05:00
|
|
|
return this.outputEdges.find((e) => e.outputNode?.name === to.name);
|
2018-08-17 05:20:21 -05:00
|
|
|
}
|
|
|
|
|
2021-04-15 07:21:06 -05:00
|
|
|
return this.outputEdges.find((e) => e.outputNode?.name === to);
|
2018-08-17 05:20:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
getOptimizedInputEdges(): Edge[] {
|
2022-09-02 11:39:09 -05:00
|
|
|
const toBeRemoved: Edge[] = [];
|
2021-01-20 00:59:48 -06:00
|
|
|
this.inputEdges.forEach((e) => {
|
2021-04-15 07:21:06 -05:00
|
|
|
const inputEdgesNodes = e.inputNode?.inputEdges.map((e) => e.inputNode);
|
2018-08-17 05:20:21 -05:00
|
|
|
|
2021-04-15 07:21:06 -05:00
|
|
|
inputEdgesNodes?.forEach((n) => {
|
|
|
|
const edgeToRemove = n?.getEdgeTo(this.name);
|
2018-08-17 05:20:21 -05:00
|
|
|
if (edgeToRemove) {
|
|
|
|
toBeRemoved.push(edgeToRemove);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-01-20 00:59:48 -06:00
|
|
|
return this.inputEdges.filter((e) => toBeRemoved.indexOf(e) === -1);
|
2018-08-17 05:20:21 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Graph {
|
2022-07-22 10:10:10 -05:00
|
|
|
nodes: Record<string, Node> = {};
|
2018-08-17 05:20:21 -05:00
|
|
|
|
|
|
|
constructor() {}
|
|
|
|
|
|
|
|
createNode(name: string): Node {
|
|
|
|
const n = new Node(name);
|
|
|
|
this.nodes[name] = n;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
createNodes(names: string[]): Node[] {
|
2019-08-01 07:38:34 -05:00
|
|
|
const nodes: Node[] = [];
|
2021-01-20 00:59:48 -06:00
|
|
|
names.forEach((name) => {
|
2018-08-17 05:20:21 -05:00
|
|
|
nodes.push(this.createNode(name));
|
|
|
|
});
|
|
|
|
return nodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
link(input: string | string[] | Node | Node[], output: string | string[] | Node | Node[]): Edge[] {
|
|
|
|
let inputArr = [];
|
|
|
|
let outputArr = [];
|
2019-08-01 07:38:34 -05:00
|
|
|
const inputNodes: Node[] = [];
|
|
|
|
const outputNodes: Node[] = [];
|
2018-08-17 05:20:21 -05:00
|
|
|
|
|
|
|
if (input instanceof Array) {
|
|
|
|
inputArr = input;
|
|
|
|
} else {
|
|
|
|
inputArr = [input];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (output instanceof Array) {
|
|
|
|
outputArr = output;
|
|
|
|
} else {
|
|
|
|
outputArr = [output];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let n = 0; n < inputArr.length; n++) {
|
|
|
|
const i = inputArr[n];
|
|
|
|
if (typeof i === 'string') {
|
2018-10-12 07:15:44 -05:00
|
|
|
const n = this.getNode(i);
|
|
|
|
if (!n) {
|
|
|
|
throw Error(`cannot link input node named ${i} since it doesn't exist in graph`);
|
|
|
|
}
|
|
|
|
inputNodes.push(n);
|
2018-08-17 05:20:21 -05:00
|
|
|
} else {
|
|
|
|
inputNodes.push(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let n = 0; n < outputArr.length; n++) {
|
|
|
|
const i = outputArr[n];
|
|
|
|
if (typeof i === 'string') {
|
2018-10-12 07:15:44 -05:00
|
|
|
const n = this.getNode(i);
|
|
|
|
if (!n) {
|
|
|
|
throw Error(`cannot link output node named ${i} since it doesn't exist in graph`);
|
|
|
|
}
|
|
|
|
outputNodes.push(n);
|
2018-08-17 05:20:21 -05:00
|
|
|
} else {
|
|
|
|
outputNodes.push(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-01 07:38:34 -05:00
|
|
|
const edges: Edge[] = [];
|
2021-01-20 00:59:48 -06:00
|
|
|
inputNodes.forEach((input) => {
|
|
|
|
outputNodes.forEach((output) => {
|
2018-08-17 05:20:21 -05:00
|
|
|
edges.push(this.createEdge().link(input, output));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return edges;
|
|
|
|
}
|
|
|
|
|
2022-07-22 10:10:10 -05:00
|
|
|
descendants(nodes: Node[] | string[]): Set<Node> {
|
|
|
|
if (!nodes.length) {
|
|
|
|
return new Set();
|
|
|
|
}
|
|
|
|
|
|
|
|
const initialNodes = new Set(
|
|
|
|
isStringArray(nodes) ? nodes.map((n) => this.nodes[n]).filter((n) => n !== undefined) : nodes
|
|
|
|
);
|
|
|
|
|
|
|
|
return this.descendantsRecursive(initialNodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
private descendantsRecursive(nodes: Set<Node>, descendants = new Set<Node>()): Set<Node> {
|
|
|
|
for (const node of nodes) {
|
|
|
|
const newDescendants = new Set<Node>();
|
|
|
|
for (const { inputNode } of node.inputEdges) {
|
|
|
|
if (inputNode && !descendants.has(inputNode)) {
|
|
|
|
descendants.add(inputNode);
|
|
|
|
newDescendants.add(inputNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.descendantsRecursive(newDescendants, descendants);
|
|
|
|
}
|
|
|
|
|
|
|
|
return descendants;
|
|
|
|
}
|
|
|
|
|
2018-08-17 05:20:21 -05:00
|
|
|
createEdge(): Edge {
|
|
|
|
return new Edge();
|
|
|
|
}
|
|
|
|
|
|
|
|
getNode(name: string): Node {
|
|
|
|
return this.nodes[name];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const printGraph = (g: Graph) => {
|
2021-01-20 00:59:48 -06:00
|
|
|
Object.keys(g.nodes).forEach((name) => {
|
2018-08-17 05:20:21 -05:00
|
|
|
const n = g.nodes[name];
|
2021-04-15 07:21:06 -05:00
|
|
|
let outputEdges = n.outputEdges.map((e: Edge) => e.outputNode?.name).join(', ');
|
2018-08-17 05:20:21 -05:00
|
|
|
if (!outputEdges) {
|
|
|
|
outputEdges = '<none>';
|
|
|
|
}
|
2021-04-15 07:21:06 -05:00
|
|
|
let inputEdges = n.inputEdges.map((e: Edge) => e.inputNode?.name).join(', ');
|
2018-08-17 05:20:21 -05:00
|
|
|
if (!inputEdges) {
|
|
|
|
inputEdges = '<none>';
|
|
|
|
}
|
|
|
|
console.log(`${n.name}:\n - links to: ${outputEdges}\n - links from: ${inputEdges}`);
|
|
|
|
});
|
|
|
|
};
|
2022-07-22 10:10:10 -05:00
|
|
|
|
|
|
|
function isStringArray(arr: unknown[]): arr is string[] {
|
|
|
|
return arr.length > 0 && typeof arr[0] === 'string';
|
|
|
|
}
|