A directed and undirected multi-graph library with comprehensive graph algorithms
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive suite of graph algorithms including shortest paths, traversal methods, cycle detection, topological sorting, and strongly connected components analysis.
Single-source shortest paths algorithm supporting custom weight and edge functions.
/**
* Computes shortest paths from source node to all reachable nodes
* @param g - Graph instance
* @param source - Starting node ID
* @param weightFn - Optional function to get edge weight (default: constant 1)
* @param edgeFn - Optional function to get edges (default: outEdges for directed, nodeEdges for undirected)
* @returns Object mapping node IDs to {distance: number, predecessor?: string}
* @throws Error if negative edge weights are encountered
*/
function dijkstra(
g: Graph,
source: string,
weightFn?: (edge: EdgeObj) => number,
edgeFn?: (v: string) => EdgeObj[]
): DijkstraResult;
interface DijkstraResult {
[nodeId: string]: {
distance: number;
predecessor?: string;
};
}Usage Examples:
import { Graph, alg } from "graphlib";
const g = new Graph();
g.setEdge("a", "b", { weight: 4 });
g.setEdge("a", "c", { weight: 2 });
g.setEdge("b", "c", { weight: 1 });
g.setEdge("c", "d", { weight: 5 });
// Use edge weights
const paths = alg.dijkstra(g, "a", (edge) => g.edge(edge).weight);
console.log(paths.d.distance); // 8 (a->c->d)
console.log(paths.d.predecessor); // "c"
// Uniform weights (default)
const uniformPaths = alg.dijkstra(g, "a");
console.log(uniformPaths.d.distance); // 3 (a->c->d)All-pairs shortest paths using Dijkstra's algorithm for each source node.
/**
* Computes shortest paths between all pairs of nodes using Dijkstra
* @param g - Graph instance
* @param weightFn - Optional function to get edge weight
* @param edgeFn - Optional function to get edges
* @returns Nested object: {sourceId: {targetId: {distance, predecessor}}}
*/
function dijkstraAll(
g: Graph,
weightFn?: (edge: EdgeObj) => number,
edgeFn?: (v: string) => EdgeObj[]
): AllPairsResult;
interface AllPairsResult {
[sourceId: string]: DijkstraResult;
}All-pairs shortest paths algorithm with O(V³) complexity, handles negative weights.
/**
* Computes shortest paths between all pairs using Floyd-Warshall algorithm
* @param g - Graph instance
* @param weightFn - Optional function to get edge weight
* @param edgeFn - Optional function to get edges
* @returns Nested object: {sourceId: {targetId: {distance, predecessor}}}
*/
function floydWarshall(
g: Graph,
weightFn?: (edge: EdgeObj) => number,
edgeFn?: (v: string) => EdgeObj[]
): AllPairsResult;Usage Examples:
// Graph with negative weights
const g = new Graph();
g.setEdge("a", "b", { weight: -2 });
g.setEdge("b", "c", { weight: 3 });
g.setEdge("a", "c", { weight: 4 });
const allPaths = alg.floydWarshall(g, (edge) => g.edge(edge).weight);
console.log(allPaths.a.c.distance); // 1 (via b)
console.log(allPaths.a.c.predecessor); // "b"Configurable depth-first search with preorder and postorder options.
/**
* Performs depth-first search traversal
* @param g - Graph instance
* @param vs - Starting node(s) (string or array of strings)
* @param order - "pre" for preorder, "post" for postorder
* @returns Array of node IDs in traversal order
* @throws Error if starting node doesn't exist
*/
function dfs(g: Graph, vs: string | string[], order: "pre" | "post"): string[];
/**
* Preorder DFS traversal (visits node before children)
* @param g - Graph instance
* @param vs - Starting node(s)
*/
function preorder(g: Graph, vs: string | string[]): string[];
/**
* Postorder DFS traversal (visits node after children)
* @param g - Graph instance
* @param vs - Starting node(s)
*/
function postorder(g: Graph, vs: string | string[]): string[];Usage Examples:
const g = new Graph();
g.setPath(["a", "b", "c"]);
g.setEdge("a", "d");
g.setEdge("d", "e");
console.log(alg.preorder(g, "a")); // ["a", "b", "c", "d", "e"]
console.log(alg.postorder(g, "a")); // ["c", "b", "e", "d", "a"]
// Multiple starting points
console.log(alg.dfs(g, ["a", "f"], "pre")); // Includes disconnected nodesTopological ordering of directed acyclic graphs.
/**
* Returns topological ordering of the graph
* @param g - Graph instance (must be directed)
* @returns Array of node IDs in topological order
* @throws CycleException if graph contains cycles
*/
function topsort(g: Graph): string[];
/**
* Exception thrown when topological sort encounters cycles
*/
class CycleException extends Error {
constructor();
}Usage Examples:
// Task dependency graph
const tasks = new Graph();
tasks.setEdge("design", "implement");
tasks.setEdge("implement", "test");
tasks.setEdge("design", "docs");
try {
const order = alg.topsort(tasks);
console.log(order); // ["design", "implement", "docs", "test"] (or valid variant)
} catch (e) {
if (e instanceof alg.topsort.CycleException) {
console.log("Cannot sort: graph has cycles");
}
}Methods for detecting and analyzing cycles in graphs.
/**
* Returns true if graph has no cycles
* @param g - Graph instance
*/
function isAcyclic(g: Graph): boolean;
/**
* Returns all cycles in the graph
* @param g - Graph instance
* @returns Array of cycles, each cycle is an array of node IDs
*/
function findCycles(g: Graph): string[][];Usage Examples:
const g = new Graph();
g.setPath(["a", "b", "c"]);
g.setEdge("c", "a"); // Creates cycle
console.log(alg.isAcyclic(g)); // false
console.log(alg.findCycles(g)); // [["a", "b", "c"]]
// Remove cycle-creating edge
g.removeEdge("c", "a");
console.log(alg.isAcyclic(g)); // trueAnalysis of strongly connected components in directed graphs.
/**
* Returns strongly connected components using Tarjan's algorithm
* @param g - Graph instance
* @returns Array of components, each component is an array of node IDs
*/
function tarjan(g: Graph): string[][];
/**
* Returns strongly connected components (alias for tarjan)
* @param g - Graph instance
* @returns Array of components
*/
function components(g: Graph): string[][];Usage Examples:
const g = new Graph();
// Create graph with SCCs
g.setEdge("a", "b");
g.setEdge("b", "c");
g.setEdge("c", "a"); // SCC: {a, b, c}
g.setEdge("c", "d");
g.setEdge("d", "e");
g.setEdge("e", "d"); // SCC: {d, e}
const sccs = alg.tarjan(g);
console.log(sccs); // [["a", "b", "c"], ["d", "e"]]Minimum spanning tree algorithms for weighted graphs.
/**
* Computes minimum spanning tree using Prim's algorithm
* @param g - Graph instance (must be connected for meaningful result)
* @param weightFn - Required function to get edge weights
* @returns New Graph instance containing MST edges
* @throws Error if graph is not connected
*/
function prim(g: Graph, weightFn: (edge: EdgeObj) => number): Graph;Usage Examples:
const g = new Graph({ directed: false }); // Undirected for MST
g.setEdge("a", "b", { weight: 4 });
g.setEdge("a", "c", { weight: 2 });
g.setEdge("b", "c", { weight: 1 });
g.setEdge("b", "d", { weight: 5 });
g.setEdge("c", "d", { weight: 8 });
g.setEdge("c", "e", { weight: 10 });
g.setEdge("d", "e", { weight: 2 });
const mst = alg.prim(g, (edge) => g.edge(edge).weight);
console.log(mst.edges().length); // 4 (n-1 edges for n nodes)
// Calculate total MST weight
let totalWeight = 0;
mst.edges().forEach(edge => {
totalWeight += g.edge(edge).weight;
});
console.log(totalWeight); // Minimum possible total weightMost algorithms accept an optional weightFn parameter:
// Use edge labels as weights
const weightFn = (edge) => g.edge(edge);
// Use specific property as weight
const weightFn = (edge) => g.edge(edge).distance;
// Constant weight (default behavior)
const weightFn = (edge) => 1;
// Dynamic weight calculation
const weightFn = (edge) => {
const edgeData = g.edge(edge);
return edgeData.distance * edgeData.difficulty;
};Some algorithms accept an optional edgeFn parameter to customize edge traversal:
// Default behaviors:
// - Directed graphs: outEdges (follow edge direction)
// - Undirected graphs: nodeEdges (all connected edges)
// Custom edge function for reverse traversal
const reverseEdgeFn = (v) => g.inEdges(v);
// Filtered edge function
const filteredEdgeFn = (v) => {
return g.outEdges(v).filter(edge => {
const edgeData = g.edge(edge);
return edgeData.enabled !== false;
});
};Common algorithm errors and exceptions:
topsort when graph contains cycles (not a standard Error)// Time-based weights
const timeWeightFn = (edge) => {
const edgeData = g.edge(edge);
const currentHour = new Date().getHours();
return edgeData.baseTime * (edgeData.trafficMultiplier[currentHour] || 1);
};
// Multi-criteria weights
const multiCriteriaFn = (edge) => {
const data = g.edge(edge);
return data.distance * 0.6 + data.cost * 0.4;
};// Find shortest paths in acyclic subgraph
if (alg.isAcyclic(g)) {
const topOrder = alg.topsort(g);
const paths = alg.dijkstra(g, topOrder[0]);
} else {
// Handle cyclic graph differently
const cycles = alg.findCycles(g);
console.log("Graph has cycles:", cycles);
}