All files / src/graph graph.ts

100% Statements 37/37
100% Branches 24/24
100% Functions 11/11
100% Lines 37/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                              189x 189x 189x 189x     783x 782x 782x 782x         635x             635x   635x 635x 635x   635x 635x 635x       1507x       936x 8125x 8125x 8125x 7140x   985x           99x 452x 445x   7x         1002x 1002x 166x 160x   6x         1496x 1496x 99x 98x   1x        
export interface GraphNode {
  id: string;
  data: any;
  order?: number;
}
 
export interface GraphEdge {
  id: string;
  source: string;
  target: string;
  data: any;
  order?: number;
}
 
export class DirectedGraph {
  private nodes: Map<string, GraphNode> = new Map();
  private edges: Map<string, GraphEdge> = new Map();
  private outgoing: Map<string, GraphEdge[]> = new Map();
  private incoming: Map<string, GraphEdge[]> = new Map();
 
  public addNode(id: string, data: any, order?: number): void {
    if (!this.nodes.has(id)) {
      this.nodes.set(id, { id, data, order });
      this.outgoing.set(id, []);
      this.incoming.set(id, []);
    }
  }
 
  public addEdge(edge: GraphEdge): void {
    const fullEdge: GraphEdge = {
      id: edge.id,
      source: edge.source,
      target: edge.target,
      data: edge.data,
      order: edge.order ?? 0,
    };
    this.edges.set(fullEdge.id, fullEdge);
 
    const outList = this.outgoing.get(fullEdge.source) || [];
    outList.push(fullEdge);
    this.outgoing.set(fullEdge.source, outList);
 
    const inList = this.incoming.get(fullEdge.target) || [];
    inList.push(fullEdge);
    this.incoming.set(fullEdge.target, inList);
  }
 
  public getNode(id: string): GraphNode | undefined {
    return this.nodes.get(id);
  }
 
  public getNodes(): GraphNode[] {
    return Array.from(this.nodes.values()).sort((a, b) => {
      const oA = a.order ?? 0;
      const oB = b.order ?? 0;
      if (oA !== oB) {
        return oA - oB;
      }
      return a.id.localeCompare(b.id);
    });
  }
 
  public getEdges(): GraphEdge[] {
    // Edge order is always defined here: addEdge defaults it to 0.
    return Array.from(this.edges.values()).sort((a, b) => {
      if (a.order !== b.order) {
        return a.order! - b.order!;
      }
      return a.id.localeCompare(b.id);
    });
  }
 
  public outEdges(nodeId: string): GraphEdge[] {
    const list = this.outgoing.get(nodeId) || [];
    return [...list].sort((a, b) => {
      if (a.order !== b.order) {
        return a.order! - b.order!;
      }
      return a.id.localeCompare(b.id);
    });
  }
 
  public inEdges(nodeId: string): GraphEdge[] {
    const list = this.incoming.get(nodeId) || [];
    return [...list].sort((a, b) => {
      if (a.order !== b.order) {
        return a.order! - b.order!;
      }
      return a.id.localeCompare(b.id);
    });
  }
}