export class Vertex { constructor(data) { this.data = data; this.edges = { from: [], to: [], }; } getEdges(label, away) { return this.edges[away ? "from" : "to"].filter( (edge) => edge.label === label ); } } export class Edge { constructor(label, from, to, data) { this.from = from; this.to = to; this.label = label; this.data = data; } } export class CategorizedEdges {} export class Graph { constructor() { this.vertices = new Map(); this.edgeLabels = new Map(); this.nextVertexId = 0; } addVertex(id, data) { // Support simple case of auto-incremented numeric ids if (typeof id === "object") { data = id; id = this.nextVertexId++; } const vertex = new Vertex(data); this.vertices.set(id, vertex); return this; } getVertex(id) { return this.vertices.get(id); } getVertexData(id) { return this.getVertex(id)?.data; } getVertices() { return Array.from(this.vertices.values()).map(({ data }) => data); } getEdge(label, from, to) { const edges = this.edgeLabels.get(label); return edges?.get(JSON.stringify({ from, to })); } setEdge(label, from, to, edge) { let edges = this.edgeLabels.get(label); if (!edges) { edges = new Map(); this.edgeLabels.set(label, edges); } edges.set(JSON.stringify({ from, to }), edge); } addEdge(label, from, to, data) { if (this.getEdge(label, from, to)) { throw new Error(`Edge ${label} from ${from} to ${to} already exists`); } const edge = new Edge(label, from, to, data); this.setEdge(label, from, to, edge); this.getVertex(from).edges.from.push(edge); this.getVertex(to).edges.to.push(edge); return this; } getEdges(label, from, to) { const edgeLabels = label ? [label] : Array.from(this.edgeLabels.keys()); return edgeLabels.flatMap((edgeLabel) => { const edges = this.edgeLabels.get(edgeLabel); return Array.from(edges?.values() || []).filter((edge) => { const matchFrom = from === null || from === undefined || from === edge.from; const matchTo = to === null || to === undefined || to === edge.to; return matchFrom && matchTo; }); }); } }