effect 3.18.5 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/Graph.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.values = exports.updateNode = exports.updateEdge = exports.undirected = exports.topo = exports.toGraphViz = exports.stronglyConnectedComponents = exports.reverse = exports.removeNode = exports.removeEdge = exports.nodes = exports.nodeCount = exports.neighborsDirected = exports.neighbors = exports.mutate = exports.mapNodes = exports.mapEdges = exports.isGraph = exports.isBipartite = exports.isAcyclic = exports.indices = exports.hasNode = exports.hasEdge = exports.getNode = exports.getEdge = exports.floydWarshall = exports.findNodes = exports.findNode = exports.findEdges = exports.findEdge = exports.filterNodes = exports.filterMapNodes = exports.filterMapEdges = exports.filterEdges = exports.externals = exports.entries = exports.endMutation = exports.edges = exports.edgeCount = exports.directed = exports.dijkstra = exports.dfsPostOrder = exports.dfs = exports.connectedComponents = exports.bfs = exports.bellmanFord = exports.beginMutation = exports.astar = exports.addNode = exports.addEdge = exports.Walker = exports.TypeId = exports.Edge = void 0;
6
+ exports.values = exports.updateNode = exports.updateEdge = exports.undirected = exports.topo = exports.toMermaid = exports.toGraphViz = exports.stronglyConnectedComponents = exports.reverse = exports.removeNode = exports.removeEdge = exports.nodes = exports.nodeCount = exports.neighborsDirected = exports.neighbors = exports.mutate = exports.mapNodes = exports.mapEdges = exports.isGraph = exports.isBipartite = exports.isAcyclic = exports.indices = exports.hasNode = exports.hasEdge = exports.getNode = exports.getEdge = exports.floydWarshall = exports.findNodes = exports.findNode = exports.findEdges = exports.findEdge = exports.filterNodes = exports.filterMapNodes = exports.filterMapEdges = exports.filterEdges = exports.externals = exports.entries = exports.endMutation = exports.edges = exports.edgeCount = exports.directed = exports.dijkstra = exports.dfsPostOrder = exports.dfs = exports.connectedComponents = exports.bfs = exports.bellmanFord = exports.beginMutation = exports.astar = exports.addNode = exports.addEdge = exports.Walker = exports.TypeId = exports.GraphError = exports.Edge = void 0;
7
7
  var Data = _interopRequireWildcard(require("./Data.js"));
8
8
  var Equal = _interopRequireWildcard(require("./Equal.js"));
9
9
  var _Function = require("./Function.js");
@@ -17,17 +17,6 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
17
17
  * @since 3.18.0
18
18
  */
19
19
 
20
- /**
21
- * Safely get a value from a Map, returning an Option.
22
- * Uses explicit key presence check with map.has() for better safety.
23
- * @internal
24
- */
25
- const getMapSafe = (map, key) => {
26
- if (map.has(key)) {
27
- return Option.some(map.get(key));
28
- }
29
- return Option.none();
30
- };
31
20
  /**
32
21
  * Unique identifier for Graph instances.
33
22
  *
@@ -113,6 +102,21 @@ const ProtoGraph = {
113
102
  }
114
103
  };
115
104
  // =============================================================================
105
+ // Errors
106
+ // =============================================================================
107
+ /**
108
+ * Error thrown when a graph operation fails.
109
+ *
110
+ * @since 3.18.0
111
+ * @category errors
112
+ */
113
+ class GraphError extends /*#__PURE__*/Data.TaggedError("GraphError") {}
114
+ /** @internal */
115
+ exports.GraphError = GraphError;
116
+ const missingNode = node => new GraphError({
117
+ message: `Node ${node} does not exist`
118
+ });
119
+ // =============================================================================
116
120
  // Constructors
117
121
  // =============================================================================
118
122
  /** @internal */
@@ -344,7 +348,7 @@ const addNode = (mutable, data) => {
344
348
  * @category getters
345
349
  */
346
350
  exports.addNode = addNode;
347
- const getNode = (graph, nodeIndex) => getMapSafe(graph.nodes, nodeIndex);
351
+ const getNode = (graph, nodeIndex) => graph.nodes.has(nodeIndex) ? Option.some(graph.nodes.get(nodeIndex)) : Option.none();
348
352
  /**
349
353
  * Checks if a node with the given index exists in the graph.
350
354
  *
@@ -923,10 +927,10 @@ const invalidateCycleFlagOnAddition = mutable => {
923
927
  const addEdge = (mutable, source, target, data) => {
924
928
  // Validate that both nodes exist
925
929
  if (!mutable.nodes.has(source)) {
926
- throw new Error(`Source node ${source} does not exist`);
930
+ throw missingNode(source);
927
931
  }
928
932
  if (!mutable.nodes.has(target)) {
929
- throw new Error(`Target node ${target} does not exist`);
933
+ throw missingNode(target);
930
934
  }
931
935
  const edgeIndex = mutable.nextEdgeIndex;
932
936
  // Create edge data
@@ -937,23 +941,23 @@ const addEdge = (mutable, source, target, data) => {
937
941
  });
938
942
  mutable.edges.set(edgeIndex, edgeData);
939
943
  // Update adjacency lists
940
- const sourceAdjacency = getMapSafe(mutable.adjacency, source);
941
- if (Option.isSome(sourceAdjacency)) {
942
- sourceAdjacency.value.push(edgeIndex);
944
+ const sourceAdjacency = mutable.adjacency.get(source);
945
+ if (sourceAdjacency !== undefined) {
946
+ sourceAdjacency.push(edgeIndex);
943
947
  }
944
- const targetReverseAdjacency = getMapSafe(mutable.reverseAdjacency, target);
945
- if (Option.isSome(targetReverseAdjacency)) {
946
- targetReverseAdjacency.value.push(edgeIndex);
948
+ const targetReverseAdjacency = mutable.reverseAdjacency.get(target);
949
+ if (targetReverseAdjacency !== undefined) {
950
+ targetReverseAdjacency.push(edgeIndex);
947
951
  }
948
952
  // For undirected graphs, add reverse connections
949
953
  if (mutable.type === "undirected") {
950
- const targetAdjacency = getMapSafe(mutable.adjacency, target);
951
- if (Option.isSome(targetAdjacency)) {
952
- targetAdjacency.value.push(edgeIndex);
954
+ const targetAdjacency = mutable.adjacency.get(target);
955
+ if (targetAdjacency !== undefined) {
956
+ targetAdjacency.push(edgeIndex);
953
957
  }
954
- const sourceReverseAdjacency = getMapSafe(mutable.reverseAdjacency, source);
955
- if (Option.isSome(sourceReverseAdjacency)) {
956
- sourceReverseAdjacency.value.push(edgeIndex);
958
+ const sourceReverseAdjacency = mutable.reverseAdjacency.get(source);
959
+ if (sourceReverseAdjacency !== undefined) {
960
+ sourceReverseAdjacency.push(edgeIndex);
957
961
  }
958
962
  }
959
963
  // Update allocators
@@ -992,16 +996,16 @@ const removeNode = (mutable, nodeIndex) => {
992
996
  // Collect all incident edges for removal
993
997
  const edgesToRemove = [];
994
998
  // Get outgoing edges
995
- const outgoingEdges = getMapSafe(mutable.adjacency, nodeIndex);
996
- if (Option.isSome(outgoingEdges)) {
997
- for (const edge of outgoingEdges.value) {
999
+ const outgoingEdges = mutable.adjacency.get(nodeIndex);
1000
+ if (outgoingEdges !== undefined) {
1001
+ for (const edge of outgoingEdges) {
998
1002
  edgesToRemove.push(edge);
999
1003
  }
1000
1004
  }
1001
1005
  // Get incoming edges
1002
- const incomingEdges = getMapSafe(mutable.reverseAdjacency, nodeIndex);
1003
- if (Option.isSome(incomingEdges)) {
1004
- for (const edge of incomingEdges.value) {
1006
+ const incomingEdges = mutable.reverseAdjacency.get(nodeIndex);
1007
+ if (incomingEdges !== undefined) {
1008
+ for (const edge of incomingEdges) {
1005
1009
  edgesToRemove.push(edge);
1006
1010
  }
1007
1011
  }
@@ -1050,43 +1054,43 @@ const removeEdge = (mutable, edgeIndex) => {
1050
1054
  exports.removeEdge = removeEdge;
1051
1055
  const removeEdgeInternal = (mutable, edgeIndex) => {
1052
1056
  // Get edge data
1053
- const edge = getMapSafe(mutable.edges, edgeIndex);
1054
- if (Option.isNone(edge)) {
1057
+ const edge = mutable.edges.get(edgeIndex);
1058
+ if (edge === undefined) {
1055
1059
  return false; // Edge doesn't exist, no mutation occurred
1056
1060
  }
1057
1061
  const {
1058
1062
  source,
1059
1063
  target
1060
- } = edge.value;
1064
+ } = edge;
1061
1065
  // Remove from adjacency lists
1062
- const sourceAdjacency = getMapSafe(mutable.adjacency, source);
1063
- if (Option.isSome(sourceAdjacency)) {
1064
- const index = sourceAdjacency.value.indexOf(edgeIndex);
1066
+ const sourceAdjacency = mutable.adjacency.get(source);
1067
+ if (sourceAdjacency !== undefined) {
1068
+ const index = sourceAdjacency.indexOf(edgeIndex);
1065
1069
  if (index !== -1) {
1066
- sourceAdjacency.value.splice(index, 1);
1070
+ sourceAdjacency.splice(index, 1);
1067
1071
  }
1068
1072
  }
1069
- const targetReverseAdjacency = getMapSafe(mutable.reverseAdjacency, target);
1070
- if (Option.isSome(targetReverseAdjacency)) {
1071
- const index = targetReverseAdjacency.value.indexOf(edgeIndex);
1073
+ const targetReverseAdjacency = mutable.reverseAdjacency.get(target);
1074
+ if (targetReverseAdjacency !== undefined) {
1075
+ const index = targetReverseAdjacency.indexOf(edgeIndex);
1072
1076
  if (index !== -1) {
1073
- targetReverseAdjacency.value.splice(index, 1);
1077
+ targetReverseAdjacency.splice(index, 1);
1074
1078
  }
1075
1079
  }
1076
1080
  // For undirected graphs, remove reverse connections
1077
1081
  if (mutable.type === "undirected") {
1078
- const targetAdjacency = getMapSafe(mutable.adjacency, target);
1079
- if (Option.isSome(targetAdjacency)) {
1080
- const index = targetAdjacency.value.indexOf(edgeIndex);
1082
+ const targetAdjacency = mutable.adjacency.get(target);
1083
+ if (targetAdjacency !== undefined) {
1084
+ const index = targetAdjacency.indexOf(edgeIndex);
1081
1085
  if (index !== -1) {
1082
- targetAdjacency.value.splice(index, 1);
1086
+ targetAdjacency.splice(index, 1);
1083
1087
  }
1084
1088
  }
1085
- const sourceReverseAdjacency = getMapSafe(mutable.reverseAdjacency, source);
1086
- if (Option.isSome(sourceReverseAdjacency)) {
1087
- const index = sourceReverseAdjacency.value.indexOf(edgeIndex);
1089
+ const sourceReverseAdjacency = mutable.reverseAdjacency.get(source);
1090
+ if (sourceReverseAdjacency !== undefined) {
1091
+ const index = sourceReverseAdjacency.indexOf(edgeIndex);
1088
1092
  if (index !== -1) {
1089
- sourceReverseAdjacency.value.splice(index, 1);
1093
+ sourceReverseAdjacency.splice(index, 1);
1090
1094
  }
1091
1095
  }
1092
1096
  }
@@ -1123,7 +1127,7 @@ const removeEdgeInternal = (mutable, edgeIndex) => {
1123
1127
  * @since 3.18.0
1124
1128
  * @category getters
1125
1129
  */
1126
- const getEdge = (graph, edgeIndex) => getMapSafe(graph.edges, edgeIndex);
1130
+ const getEdge = (graph, edgeIndex) => graph.edges.has(edgeIndex) ? Option.some(graph.edges.get(edgeIndex)) : Option.none();
1127
1131
  /**
1128
1132
  * Checks if an edge exists between two nodes in the graph.
1129
1133
  *
@@ -1154,14 +1158,14 @@ const getEdge = (graph, edgeIndex) => getMapSafe(graph.edges, edgeIndex);
1154
1158
  */
1155
1159
  exports.getEdge = getEdge;
1156
1160
  const hasEdge = (graph, source, target) => {
1157
- const adjacencyList = getMapSafe(graph.adjacency, source);
1158
- if (Option.isNone(adjacencyList)) {
1161
+ const adjacencyList = graph.adjacency.get(source);
1162
+ if (adjacencyList === undefined) {
1159
1163
  return false;
1160
1164
  }
1161
1165
  // Check if any edge in the adjacency list connects to the target
1162
- for (const edgeIndex of adjacencyList.value) {
1163
- const edge = getMapSafe(graph.edges, edgeIndex);
1164
- if (Option.isSome(edge) && edge.value.target === target) {
1166
+ for (const edgeIndex of adjacencyList) {
1167
+ const edge = graph.edges.get(edgeIndex);
1168
+ if (edge !== undefined && edge.target === target) {
1165
1169
  return true;
1166
1170
  }
1167
1171
  }
@@ -1229,15 +1233,15 @@ const neighbors = (graph, nodeIndex) => {
1229
1233
  if (graph.type === "undirected") {
1230
1234
  return getUndirectedNeighbors(graph, nodeIndex);
1231
1235
  }
1232
- const adjacencyList = getMapSafe(graph.adjacency, nodeIndex);
1233
- if (Option.isNone(adjacencyList)) {
1236
+ const adjacencyList = graph.adjacency.get(nodeIndex);
1237
+ if (adjacencyList === undefined) {
1234
1238
  return [];
1235
1239
  }
1236
1240
  const result = [];
1237
- for (const edgeIndex of adjacencyList.value) {
1238
- const edge = getMapSafe(graph.edges, edgeIndex);
1239
- if (Option.isSome(edge)) {
1240
- result.push(edge.value.target);
1241
+ for (const edgeIndex of adjacencyList) {
1242
+ const edge = graph.edges.get(edgeIndex);
1243
+ if (edge !== undefined) {
1244
+ result.push(edge.target);
1241
1245
  }
1242
1246
  }
1243
1247
  return result;
@@ -1271,24 +1275,21 @@ const neighbors = (graph, nodeIndex) => {
1271
1275
  exports.neighbors = neighbors;
1272
1276
  const neighborsDirected = (graph, nodeIndex, direction) => {
1273
1277
  const adjacencyMap = direction === "incoming" ? graph.reverseAdjacency : graph.adjacency;
1274
- const adjacencyList = getMapSafe(adjacencyMap, nodeIndex);
1275
- if (Option.isNone(adjacencyList)) {
1278
+ const adjacencyList = adjacencyMap.get(nodeIndex);
1279
+ if (adjacencyList === undefined) {
1276
1280
  return [];
1277
1281
  }
1278
1282
  const result = [];
1279
- for (const edgeIndex of adjacencyList.value) {
1280
- const edge = getMapSafe(graph.edges, edgeIndex);
1281
- if (Option.isSome(edge)) {
1283
+ for (const edgeIndex of adjacencyList) {
1284
+ const edge = graph.edges.get(edgeIndex);
1285
+ if (edge !== undefined) {
1282
1286
  // For incoming direction, we want the source node instead of target
1283
- const neighborNode = direction === "incoming" ? edge.value.source : edge.value.target;
1287
+ const neighborNode = direction === "incoming" ? edge.source : edge.target;
1284
1288
  result.push(neighborNode);
1285
1289
  }
1286
1290
  }
1287
1291
  return result;
1288
1292
  };
1289
- // =============================================================================
1290
- // GraphViz Export
1291
- // =============================================================================
1292
1293
  /**
1293
1294
  * Exports a graph to GraphViz DOT format for visualization.
1294
1295
  *
@@ -1345,6 +1346,98 @@ const toGraphViz = (graph, options) => {
1345
1346
  lines.push("}");
1346
1347
  return lines.join("\n");
1347
1348
  };
1349
+ /** @internal */
1350
+ exports.toGraphViz = toGraphViz;
1351
+ const escapeMermaidLabel = label => {
1352
+ // Escape special characters for Mermaid using HTML entity codes
1353
+ // According to: https://mermaid.js.org/syntax/flowchart.html#special-characters-that-break-syntax
1354
+ return label.replace(/#/g, "#35;").replace(/"/g, "#quot;").replace(/</g, "#lt;").replace(/>/g, "#gt;").replace(/&/g, "#amp;").replace(/\[/g, "#91;").replace(/\]/g, "#93;").replace(/\{/g, "#123;").replace(/\}/g, "#125;").replace(/\(/g, "#40;").replace(/\)/g, "#41;").replace(/\|/g, "#124;").replace(/\\/g, "#92;").replace(/\n/g, "<br/>");
1355
+ };
1356
+ /** @internal */
1357
+ const formatMermaidNode = (nodeId, label, shape) => {
1358
+ switch (shape) {
1359
+ case "rectangle":
1360
+ return `${nodeId}["${label}"]`;
1361
+ case "rounded":
1362
+ return `${nodeId}("${label}")`;
1363
+ case "circle":
1364
+ return `${nodeId}(("${label}"))`;
1365
+ case "diamond":
1366
+ return `${nodeId}{"${label}"}`;
1367
+ case "hexagon":
1368
+ return `${nodeId}{{"${label}"}}`;
1369
+ case "stadium":
1370
+ return `${nodeId}(["${label}"])`;
1371
+ case "subroutine":
1372
+ return `${nodeId}[["${label}"]]`;
1373
+ case "cylindrical":
1374
+ return `${nodeId}[("${label}")]`;
1375
+ }
1376
+ };
1377
+ /**
1378
+ * Exports a graph to Mermaid diagram format for visualization.
1379
+ *
1380
+ * @example
1381
+ * ```ts
1382
+ * import { Graph } from "effect"
1383
+ *
1384
+ * const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
1385
+ * const app = Graph.addNode(mutable, "App")
1386
+ * const db = Graph.addNode(mutable, "Database")
1387
+ * const cache = Graph.addNode(mutable, "Cache")
1388
+ * Graph.addEdge(mutable, app, db, 1)
1389
+ * Graph.addEdge(mutable, app, cache, 2)
1390
+ * })
1391
+ *
1392
+ * const mermaid = Graph.toMermaid(graph)
1393
+ * console.log(mermaid)
1394
+ * // flowchart TD
1395
+ * // 0["App"]
1396
+ * // 1["Database"]
1397
+ * // 2["Cache"]
1398
+ * // 0 -->|"1"| 1
1399
+ * // 0 -->|"2"| 2
1400
+ * ```
1401
+ *
1402
+ * @since 3.18.0
1403
+ * @category utils
1404
+ */
1405
+ const toMermaid = (graph, options) => {
1406
+ // Extract and validate options with defaults
1407
+ const {
1408
+ diagramType,
1409
+ direction = "TD",
1410
+ edgeLabel = data => String(data),
1411
+ nodeLabel = data => String(data),
1412
+ nodeShape = () => "rectangle"
1413
+ } = options ?? {};
1414
+ // Auto-detect diagram type if not specified
1415
+ const finalDiagramType = diagramType ?? (graph.type === "directed" ? "flowchart" : "graph");
1416
+ // Generate diagram header
1417
+ const lines = [];
1418
+ lines.push(`${finalDiagramType} ${direction}`);
1419
+ // Add nodes
1420
+ for (const [nodeIndex, nodeData] of graph.nodes) {
1421
+ const nodeId = String(nodeIndex);
1422
+ const label = escapeMermaidLabel(nodeLabel(nodeData));
1423
+ const shape = nodeShape(nodeData);
1424
+ const formattedNode = formatMermaidNode(nodeId, label, shape);
1425
+ lines.push(` ${formattedNode}`);
1426
+ }
1427
+ // Add edges
1428
+ const edgeOperator = finalDiagramType === "flowchart" ? "-->" : "---";
1429
+ for (const [, edgeData] of graph.edges) {
1430
+ const sourceId = String(edgeData.source);
1431
+ const targetId = String(edgeData.target);
1432
+ const label = escapeMermaidLabel(edgeLabel(edgeData.data));
1433
+ if (label) {
1434
+ lines.push(` ${sourceId} ${edgeOperator}|"${label}"| ${targetId}`);
1435
+ } else {
1436
+ lines.push(` ${sourceId} ${edgeOperator} ${targetId}`);
1437
+ }
1438
+ }
1439
+ return lines.join("\n");
1440
+ };
1348
1441
  // =============================================================================
1349
1442
  // =============================================================================
1350
1443
  // Graph Structure Analysis Algorithms (Phase 5A)
@@ -1383,7 +1476,7 @@ const toGraphViz = (graph, options) => {
1383
1476
  * @since 3.18.0
1384
1477
  * @category algorithms
1385
1478
  */
1386
- exports.toGraphViz = toGraphViz;
1479
+ exports.toMermaid = toMermaid;
1387
1480
  const isAcyclic = graph => {
1388
1481
  // Use existing cycle flag if available
1389
1482
  if (Option.isSome(graph.isAcyclic)) {
@@ -1529,13 +1622,13 @@ exports.isBipartite = isBipartite;
1529
1622
  const getUndirectedNeighbors = (graph, nodeIndex) => {
1530
1623
  const neighbors = new Set();
1531
1624
  // Check edges where this node is the source
1532
- const adjacencyList = getMapSafe(graph.adjacency, nodeIndex);
1533
- if (Option.isSome(adjacencyList)) {
1534
- for (const edgeIndex of adjacencyList.value) {
1535
- const edge = getMapSafe(graph.edges, edgeIndex);
1536
- if (Option.isSome(edge)) {
1625
+ const adjacencyList = graph.adjacency.get(nodeIndex);
1626
+ if (adjacencyList !== undefined) {
1627
+ for (const edgeIndex of adjacencyList) {
1628
+ const edge = graph.edges.get(edgeIndex);
1629
+ if (edge !== undefined) {
1537
1630
  // For undirected graphs, the neighbor is the other endpoint
1538
- const otherNode = edge.value.source === nodeIndex ? edge.value.target : edge.value.source;
1631
+ const otherNode = edge.source === nodeIndex ? edge.target : edge.source;
1539
1632
  neighbors.add(otherNode);
1540
1633
  }
1541
1634
  }
@@ -1670,12 +1763,12 @@ const stronglyConnectedComponents = graph => {
1670
1763
  visited.add(node);
1671
1764
  scc.push(node);
1672
1765
  // Use reverse adjacency (transpose graph)
1673
- const reverseAdjacency = getMapSafe(graph.reverseAdjacency, node);
1674
- if (Option.isSome(reverseAdjacency)) {
1675
- for (const edgeIndex of reverseAdjacency.value) {
1676
- const edge = getMapSafe(graph.edges, edgeIndex);
1677
- if (Option.isSome(edge)) {
1678
- const predecessor = edge.value.source;
1766
+ const reverseAdjacency = graph.reverseAdjacency.get(node);
1767
+ if (reverseAdjacency !== undefined) {
1768
+ for (const edgeIndex of reverseAdjacency) {
1769
+ const edge = graph.edges.get(edgeIndex);
1770
+ if (edge !== undefined) {
1771
+ const predecessor = edge.source;
1679
1772
  if (!visited.has(predecessor)) {
1680
1773
  stack.push(predecessor);
1681
1774
  }
@@ -1706,7 +1799,7 @@ const stronglyConnectedComponents = graph => {
1706
1799
  * Graph.addEdge(mutable, b, c, 2)
1707
1800
  * })
1708
1801
  *
1709
- * const result = Graph.dijkstra(graph, 0, 2, (edgeData) => edgeData)
1802
+ * const result = Graph.dijkstra(graph, { source: 0, target: 2, cost: (edgeData) => edgeData })
1710
1803
  * if (Option.isSome(result)) {
1711
1804
  * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C
1712
1805
  * console.log(result.value.distance) // 7 - total distance
@@ -1717,20 +1810,25 @@ const stronglyConnectedComponents = graph => {
1717
1810
  * @category algorithms
1718
1811
  */
1719
1812
  exports.stronglyConnectedComponents = stronglyConnectedComponents;
1720
- const dijkstra = (graph, source, target, edgeWeight) => {
1813
+ const dijkstra = (graph, config) => {
1814
+ const {
1815
+ cost,
1816
+ source,
1817
+ target
1818
+ } = config;
1721
1819
  // Validate that source and target nodes exist
1722
1820
  if (!graph.nodes.has(source)) {
1723
- throw new Error(`Source node ${source} does not exist`);
1821
+ throw missingNode(source);
1724
1822
  }
1725
1823
  if (!graph.nodes.has(target)) {
1726
- throw new Error(`Target node ${target} does not exist`);
1824
+ throw missingNode(target);
1727
1825
  }
1728
1826
  // Early return if source equals target
1729
1827
  if (source === target) {
1730
1828
  return Option.some({
1731
1829
  path: [source],
1732
1830
  distance: 0,
1733
- edgeWeights: []
1831
+ costs: []
1734
1832
  });
1735
1833
  }
1736
1834
  // Distance tracking and priority queue simulation
@@ -1770,13 +1868,13 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1770
1868
  // Get current distance
1771
1869
  const currentDistance = distances.get(currentNode);
1772
1870
  // Examine all outgoing edges
1773
- const adjacencyList = getMapSafe(graph.adjacency, currentNode);
1774
- if (Option.isSome(adjacencyList)) {
1775
- for (const edgeIndex of adjacencyList.value) {
1776
- const edge = getMapSafe(graph.edges, edgeIndex);
1777
- if (Option.isSome(edge)) {
1778
- const neighbor = edge.value.target;
1779
- const weight = edgeWeight(edge.value.data);
1871
+ const adjacencyList = graph.adjacency.get(currentNode);
1872
+ if (adjacencyList !== undefined) {
1873
+ for (const edgeIndex of adjacencyList) {
1874
+ const edge = graph.edges.get(edgeIndex);
1875
+ if (edge !== undefined) {
1876
+ const neighbor = edge.target;
1877
+ const weight = cost(edge.data);
1780
1878
  // Validate non-negative weights
1781
1879
  if (weight < 0) {
1782
1880
  throw new Error(`Dijkstra's algorithm requires non-negative edge weights, found ${weight}`);
@@ -1788,7 +1886,7 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1788
1886
  distances.set(neighbor, newDistance);
1789
1887
  previous.set(neighbor, {
1790
1888
  node: currentNode,
1791
- edgeData: edge.value.data
1889
+ edgeData: edge.data
1792
1890
  });
1793
1891
  // Add to priority queue if not visited
1794
1892
  if (!visited.has(neighbor)) {
@@ -1809,13 +1907,13 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1809
1907
  }
1810
1908
  // Reconstruct path
1811
1909
  const path = [];
1812
- const edgeWeights = [];
1910
+ const costs = [];
1813
1911
  let currentNode = target;
1814
1912
  while (currentNode !== null) {
1815
1913
  path.unshift(currentNode);
1816
1914
  const prev = previous.get(currentNode);
1817
1915
  if (prev !== null) {
1818
- edgeWeights.unshift(prev.edgeData);
1916
+ costs.unshift(prev.edgeData);
1819
1917
  currentNode = prev.node;
1820
1918
  } else {
1821
1919
  currentNode = null;
@@ -1824,7 +1922,7 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1824
1922
  return Option.some({
1825
1923
  path,
1826
1924
  distance: targetDistance,
1827
- edgeWeights
1925
+ costs
1828
1926
  });
1829
1927
  };
1830
1928
  /**
@@ -1855,7 +1953,7 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1855
1953
  * @category algorithms
1856
1954
  */
1857
1955
  exports.dijkstra = dijkstra;
1858
- const floydWarshall = (graph, edgeWeight) => {
1956
+ const floydWarshall = (graph, cost) => {
1859
1957
  // Get all nodes for Floyd-Warshall algorithm (needs array for nested iteration)
1860
1958
  const allNodes = Array.from(graph.nodes.keys());
1861
1959
  // Initialize distance matrix
@@ -1875,7 +1973,7 @@ const floydWarshall = (graph, edgeWeight) => {
1875
1973
  }
1876
1974
  // Set edge weights
1877
1975
  for (const [, edgeData] of graph.edges) {
1878
- const weight = edgeWeight(edgeData.data);
1976
+ const weight = cost(edgeData.data);
1879
1977
  const i = edgeData.source;
1880
1978
  const j = edgeData.target;
1881
1979
  // Use minimum weight if multiple edges exist
@@ -1908,17 +2006,17 @@ const floydWarshall = (graph, edgeWeight) => {
1908
2006
  }
1909
2007
  // Build result paths and edge weights
1910
2008
  const paths = new Map();
1911
- const resultEdgeWeights = new Map();
2009
+ const resultCosts = new Map();
1912
2010
  for (const i of allNodes) {
1913
2011
  paths.set(i, new Map());
1914
- resultEdgeWeights.set(i, new Map());
2012
+ resultCosts.set(i, new Map());
1915
2013
  for (const j of allNodes) {
1916
2014
  if (i === j) {
1917
2015
  paths.get(i).set(j, [i]);
1918
- resultEdgeWeights.get(i).set(j, []);
2016
+ resultCosts.get(i).set(j, []);
1919
2017
  } else if (dist.get(i).get(j) === Infinity) {
1920
2018
  paths.get(i).set(j, null);
1921
- resultEdgeWeights.get(i).set(j, []);
2019
+ resultCosts.get(i).set(j, []);
1922
2020
  } else {
1923
2021
  // Reconstruct path iteratively
1924
2022
  const path = [];
@@ -1936,14 +2034,14 @@ const floydWarshall = (graph, edgeWeight) => {
1936
2034
  path.push(current);
1937
2035
  }
1938
2036
  paths.get(i).set(j, path);
1939
- resultEdgeWeights.get(i).set(j, weights);
2037
+ resultCosts.get(i).set(j, weights);
1940
2038
  }
1941
2039
  }
1942
2040
  }
1943
2041
  return {
1944
2042
  distances: dist,
1945
2043
  paths,
1946
- edgeWeights: resultEdgeWeights
2044
+ costs: resultCosts
1947
2045
  };
1948
2046
  };
1949
2047
  /**
@@ -1969,7 +2067,7 @@ const floydWarshall = (graph, edgeWeight) => {
1969
2067
  * const heuristic = (nodeData: {x: number, y: number}, targetData: {x: number, y: number}) =>
1970
2068
  * Math.abs(nodeData.x - targetData.x) + Math.abs(nodeData.y - targetData.y)
1971
2069
  *
1972
- * const result = Graph.astar(graph, 0, 2, (edgeData) => edgeData, heuristic)
2070
+ * const result = Graph.astar(graph, { source: 0, target: 2, cost: (edgeData) => edgeData, heuristic })
1973
2071
  * if (Option.isSome(result)) {
1974
2072
  * console.log(result.value.path) // [0, 1, 2] - shortest path
1975
2073
  * console.log(result.value.distance) // 2 - total distance
@@ -1980,25 +2078,31 @@ const floydWarshall = (graph, edgeWeight) => {
1980
2078
  * @category algorithms
1981
2079
  */
1982
2080
  exports.floydWarshall = floydWarshall;
1983
- const astar = (graph, source, target, edgeWeight, heuristic) => {
2081
+ const astar = (graph, config) => {
2082
+ const {
2083
+ cost,
2084
+ heuristic,
2085
+ source,
2086
+ target
2087
+ } = config;
1984
2088
  // Validate that source and target nodes exist
1985
2089
  if (!graph.nodes.has(source)) {
1986
- throw new Error(`Source node ${source} does not exist`);
2090
+ throw missingNode(source);
1987
2091
  }
1988
2092
  if (!graph.nodes.has(target)) {
1989
- throw new Error(`Target node ${target} does not exist`);
2093
+ throw missingNode(target);
1990
2094
  }
1991
2095
  // Early return if source equals target
1992
2096
  if (source === target) {
1993
2097
  return Option.some({
1994
2098
  path: [source],
1995
2099
  distance: 0,
1996
- edgeWeights: []
2100
+ costs: []
1997
2101
  });
1998
2102
  }
1999
2103
  // Get target node data for heuristic calculations
2000
- const targetNodeData = getMapSafe(graph.nodes, target);
2001
- if (Option.isNone(targetNodeData)) {
2104
+ const targetNodeData = graph.nodes.get(target);
2105
+ if (targetNodeData === undefined) {
2002
2106
  throw new Error(`Target node ${target} data not found`);
2003
2107
  }
2004
2108
  // Distance tracking (g-score) and f-score (g + h)
@@ -2014,9 +2118,9 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2014
2118
  previous.set(node, null);
2015
2119
  }
2016
2120
  // Calculate initial f-score for source
2017
- const sourceNodeData = getMapSafe(graph.nodes, source);
2018
- if (Option.isSome(sourceNodeData)) {
2019
- const h = heuristic(sourceNodeData.value, targetNodeData.value);
2121
+ const sourceNodeData = graph.nodes.get(source);
2122
+ if (sourceNodeData !== undefined) {
2123
+ const h = heuristic(sourceNodeData, targetNodeData);
2020
2124
  fScore.set(source, h);
2021
2125
  }
2022
2126
  // Priority queue using f-score (total estimated cost)
@@ -2046,13 +2150,13 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2046
2150
  // Get current g-score
2047
2151
  const currentGScore = gScore.get(currentNode);
2048
2152
  // Examine all outgoing edges
2049
- const adjacencyList = getMapSafe(graph.adjacency, currentNode);
2050
- if (Option.isSome(adjacencyList)) {
2051
- for (const edgeIndex of adjacencyList.value) {
2052
- const edge = getMapSafe(graph.edges, edgeIndex);
2053
- if (Option.isSome(edge)) {
2054
- const neighbor = edge.value.target;
2055
- const weight = edgeWeight(edge.value.data);
2153
+ const adjacencyList = graph.adjacency.get(currentNode);
2154
+ if (adjacencyList !== undefined) {
2155
+ for (const edgeIndex of adjacencyList) {
2156
+ const edge = graph.edges.get(edgeIndex);
2157
+ if (edge !== undefined) {
2158
+ const neighbor = edge.target;
2159
+ const weight = cost(edge.data);
2056
2160
  // Validate non-negative weights
2057
2161
  if (weight < 0) {
2058
2162
  throw new Error(`A* algorithm requires non-negative edge weights, found ${weight}`);
@@ -2065,12 +2169,12 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2065
2169
  gScore.set(neighbor, tentativeGScore);
2066
2170
  previous.set(neighbor, {
2067
2171
  node: currentNode,
2068
- edgeData: edge.value.data
2172
+ edgeData: edge.data
2069
2173
  });
2070
2174
  // Calculate f-score using heuristic
2071
- const neighborNodeData = getMapSafe(graph.nodes, neighbor);
2072
- if (Option.isSome(neighborNodeData)) {
2073
- const h = heuristic(neighborNodeData.value, targetNodeData.value);
2175
+ const neighborNodeData = graph.nodes.get(neighbor);
2176
+ if (neighborNodeData !== undefined) {
2177
+ const h = heuristic(neighborNodeData, targetNodeData);
2074
2178
  const f = tentativeGScore + h;
2075
2179
  fScore.set(neighbor, f);
2076
2180
  // Add to open set if not visited
@@ -2093,13 +2197,13 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2093
2197
  }
2094
2198
  // Reconstruct path
2095
2199
  const path = [];
2096
- const edgeWeights = [];
2200
+ const costs = [];
2097
2201
  let currentNode = target;
2098
2202
  while (currentNode !== null) {
2099
2203
  path.unshift(currentNode);
2100
2204
  const prev = previous.get(currentNode);
2101
2205
  if (prev !== null) {
2102
- edgeWeights.unshift(prev.edgeData);
2206
+ costs.unshift(prev.edgeData);
2103
2207
  currentNode = prev.node;
2104
2208
  } else {
2105
2209
  currentNode = null;
@@ -2108,7 +2212,7 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2108
2212
  return Option.some({
2109
2213
  path,
2110
2214
  distance: targetGScore,
2111
- edgeWeights
2215
+ costs
2112
2216
  });
2113
2217
  };
2114
2218
  /**
@@ -2131,7 +2235,7 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2131
2235
  * Graph.addEdge(mutable, a, c, 5)
2132
2236
  * })
2133
2237
  *
2134
- * const result = Graph.bellmanFord(graph, 0, 2, (edgeData) => edgeData)
2238
+ * const result = Graph.bellmanFord(graph, { source: 0, target: 2, cost: (edgeData) => edgeData })
2135
2239
  * if (Option.isSome(result)) {
2136
2240
  * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C
2137
2241
  * console.log(result.value.distance) // 2 - total distance
@@ -2142,20 +2246,25 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2142
2246
  * @category algorithms
2143
2247
  */
2144
2248
  exports.astar = astar;
2145
- const bellmanFord = (graph, source, target, edgeWeight) => {
2249
+ const bellmanFord = (graph, config) => {
2250
+ const {
2251
+ cost,
2252
+ source,
2253
+ target
2254
+ } = config;
2146
2255
  // Validate that source and target nodes exist
2147
2256
  if (!graph.nodes.has(source)) {
2148
- throw new Error(`Source node ${source} does not exist`);
2257
+ throw missingNode(source);
2149
2258
  }
2150
2259
  if (!graph.nodes.has(target)) {
2151
- throw new Error(`Target node ${target} does not exist`);
2260
+ throw missingNode(target);
2152
2261
  }
2153
2262
  // Early return if source equals target
2154
2263
  if (source === target) {
2155
2264
  return Option.some({
2156
2265
  path: [source],
2157
2266
  distance: 0,
2158
- edgeWeights: []
2267
+ costs: []
2159
2268
  });
2160
2269
  }
2161
2270
  // Initialize distances and predecessors
@@ -2169,7 +2278,7 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2169
2278
  // Collect all edges for relaxation
2170
2279
  const edges = [];
2171
2280
  for (const [, edgeData] of graph.edges) {
2172
- const weight = edgeWeight(edgeData.data);
2281
+ const weight = cost(edgeData.data);
2173
2282
  edges.push({
2174
2283
  source: edgeData.source,
2175
2284
  target: edgeData.target,
@@ -2212,12 +2321,12 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2212
2321
  if (affectedNodes.has(node)) continue;
2213
2322
  affectedNodes.add(node);
2214
2323
  // Add all nodes reachable from this node
2215
- const adjacencyList = getMapSafe(graph.adjacency, node);
2216
- if (Option.isSome(adjacencyList)) {
2217
- for (const edgeIndex of adjacencyList.value) {
2218
- const edge = getMapSafe(graph.edges, edgeIndex);
2219
- if (Option.isSome(edge)) {
2220
- queue.push(edge.value.target);
2324
+ const adjacencyList = graph.adjacency.get(node);
2325
+ if (adjacencyList !== undefined) {
2326
+ for (const edgeIndex of adjacencyList) {
2327
+ const edge = graph.edges.get(edgeIndex);
2328
+ if (edge !== undefined) {
2329
+ queue.push(edge.target);
2221
2330
  }
2222
2331
  }
2223
2332
  }
@@ -2235,13 +2344,13 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2235
2344
  }
2236
2345
  // Reconstruct path
2237
2346
  const path = [];
2238
- const edgeWeights = [];
2347
+ const costs = [];
2239
2348
  let currentNode = target;
2240
2349
  while (currentNode !== null) {
2241
2350
  path.unshift(currentNode);
2242
2351
  const prev = previous.get(currentNode);
2243
2352
  if (prev !== null) {
2244
- edgeWeights.unshift(prev.edgeData);
2353
+ costs.unshift(prev.edgeData);
2245
2354
  currentNode = prev.node;
2246
2355
  } else {
2247
2356
  currentNode = null;
@@ -2250,7 +2359,7 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2250
2359
  return Option.some({
2251
2360
  path,
2252
2361
  distance: targetDistance,
2253
- edgeWeights
2362
+ costs
2254
2363
  });
2255
2364
  };
2256
2365
  /**
@@ -2271,7 +2380,7 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2271
2380
  * })
2272
2381
  *
2273
2382
  * // Both traversal and element iterators return NodeWalker
2274
- * const dfsNodes: Graph.NodeWalker<string> = Graph.dfs(graph, { startNodes: [0] })
2383
+ * const dfsNodes: Graph.NodeWalker<string> = Graph.dfs(graph, { start: [0] })
2275
2384
  * const allNodes: Graph.NodeWalker<string> = Graph.nodes(graph)
2276
2385
  *
2277
2386
  * // Common interface for working with node iterables
@@ -2308,7 +2417,7 @@ class Walker {
2308
2417
  * Graph.addEdge(mutable, a, b, 1)
2309
2418
  * })
2310
2419
  *
2311
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2420
+ * const dfs = Graph.dfs(graph, { start: [0] })
2312
2421
  *
2313
2422
  * // Map to just the node data
2314
2423
  * const values = Array.from(dfs.visit((index, data) => data))
@@ -2341,7 +2450,7 @@ class Walker {
2341
2450
  * Graph.addEdge(mutable, a, b, 1)
2342
2451
  * })
2343
2452
  *
2344
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2453
+ * const dfs = Graph.dfs(graph, { start: [0] })
2345
2454
  *
2346
2455
  * // Map to just the node data
2347
2456
  * const values = Array.from(dfs.visit((index, data) => data))
@@ -2373,7 +2482,7 @@ class Walker {
2373
2482
  * Graph.addEdge(mutable, a, b, 1)
2374
2483
  * })
2375
2484
  *
2376
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2485
+ * const dfs = Graph.dfs(graph, { start: [0] })
2377
2486
  * const indices = Array.from(Graph.indices(dfs))
2378
2487
  * console.log(indices) // [0, 1]
2379
2488
  * ```
@@ -2396,7 +2505,7 @@ const indices = walker => walker.visit((index, _) => index);
2396
2505
  * Graph.addEdge(mutable, a, b, 1)
2397
2506
  * })
2398
2507
  *
2399
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2508
+ * const dfs = Graph.dfs(graph, { start: [0] })
2400
2509
  * const values = Array.from(Graph.values(dfs))
2401
2510
  * console.log(values) // ["A", "B"]
2402
2511
  * ```
@@ -2419,7 +2528,7 @@ const values = walker => walker.visit((_, data) => data);
2419
2528
  * Graph.addEdge(mutable, a, b, 1)
2420
2529
  * })
2421
2530
  *
2422
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2531
+ * const dfs = Graph.dfs(graph, { start: [0] })
2423
2532
  * const entries = Array.from(Graph.entries(dfs))
2424
2533
  * console.log(entries) // [[0, "A"], [1, "B"]]
2425
2534
  * ```
@@ -2448,7 +2557,7 @@ const entries = walker => walker.visit((index, data) => [index, data]);
2448
2557
  * })
2449
2558
  *
2450
2559
  * // Start from a specific node
2451
- * const dfs1 = Graph.dfs(graph, { startNodes: [0] })
2560
+ * const dfs1 = Graph.dfs(graph, { start: [0] })
2452
2561
  * for (const nodeIndex of Graph.indices(dfs1)) {
2453
2562
  * console.log(nodeIndex) // Traverses in DFS order: 0, 1, 2
2454
2563
  * }
@@ -2463,17 +2572,17 @@ const entries = walker => walker.visit((index, data) => [index, data]);
2463
2572
  */
2464
2573
  exports.entries = entries;
2465
2574
  const dfs = (graph, config = {}) => {
2466
- const startNodes = config.startNodes ?? [];
2575
+ const start = config.start ?? [];
2467
2576
  const direction = config.direction ?? "outgoing";
2468
2577
  // Validate that all start nodes exist
2469
- for (const nodeIndex of startNodes) {
2578
+ for (const nodeIndex of start) {
2470
2579
  if (!hasNode(graph, nodeIndex)) {
2471
- throw new Error(`Start node ${nodeIndex} does not exist`);
2580
+ throw missingNode(nodeIndex);
2472
2581
  }
2473
2582
  }
2474
2583
  return new Walker(f => ({
2475
2584
  [Symbol.iterator]: () => {
2476
- const stack = [...startNodes];
2585
+ const stack = [...start];
2477
2586
  const discovered = new Set();
2478
2587
  const nextMapped = () => {
2479
2588
  while (stack.length > 0) {
@@ -2482,8 +2591,8 @@ const dfs = (graph, config = {}) => {
2482
2591
  continue;
2483
2592
  }
2484
2593
  discovered.add(current);
2485
- const nodeDataOption = getMapSafe(graph.nodes, current);
2486
- if (Option.isNone(nodeDataOption)) {
2594
+ const nodeDataOption = graph.nodes.get(current);
2595
+ if (nodeDataOption === undefined) {
2487
2596
  continue;
2488
2597
  }
2489
2598
  const neighbors = neighborsDirected(graph, current, direction);
@@ -2495,7 +2604,7 @@ const dfs = (graph, config = {}) => {
2495
2604
  }
2496
2605
  return {
2497
2606
  done: false,
2498
- value: f(current, nodeDataOption.value)
2607
+ value: f(current, nodeDataOption)
2499
2608
  };
2500
2609
  }
2501
2610
  return {
@@ -2528,7 +2637,7 @@ const dfs = (graph, config = {}) => {
2528
2637
  * })
2529
2638
  *
2530
2639
  * // Start from a specific node
2531
- * const bfs1 = Graph.bfs(graph, { startNodes: [0] })
2640
+ * const bfs1 = Graph.bfs(graph, { start: [0] })
2532
2641
  * for (const nodeIndex of Graph.indices(bfs1)) {
2533
2642
  * console.log(nodeIndex) // Traverses in BFS order: 0, 1, 2
2534
2643
  * }
@@ -2543,17 +2652,17 @@ const dfs = (graph, config = {}) => {
2543
2652
  */
2544
2653
  exports.dfs = dfs;
2545
2654
  const bfs = (graph, config = {}) => {
2546
- const startNodes = config.startNodes ?? [];
2655
+ const start = config.start ?? [];
2547
2656
  const direction = config.direction ?? "outgoing";
2548
2657
  // Validate that all start nodes exist
2549
- for (const nodeIndex of startNodes) {
2658
+ for (const nodeIndex of start) {
2550
2659
  if (!hasNode(graph, nodeIndex)) {
2551
- throw new Error(`Start node ${nodeIndex} does not exist`);
2660
+ throw missingNode(nodeIndex);
2552
2661
  }
2553
2662
  }
2554
2663
  return new Walker(f => ({
2555
2664
  [Symbol.iterator]: () => {
2556
- const queue = [...startNodes];
2665
+ const queue = [...start];
2557
2666
  const discovered = new Set();
2558
2667
  const nextMapped = () => {
2559
2668
  while (queue.length > 0) {
@@ -2642,7 +2751,7 @@ const topo = (graph, config = {}) => {
2642
2751
  // Validate that all initial nodes exist
2643
2752
  for (const nodeIndex of initials) {
2644
2753
  if (!hasNode(graph, nodeIndex)) {
2645
- throw new Error(`Initial node ${nodeIndex} does not exist`);
2754
+ throw missingNode(nodeIndex);
2646
2755
  }
2647
2756
  }
2648
2757
  return new Walker(f => ({
@@ -2727,7 +2836,7 @@ const topo = (graph, config = {}) => {
2727
2836
  * })
2728
2837
  *
2729
2838
  * // Postorder: children before parents
2730
- * const postOrder = Graph.dfsPostOrder(graph, { startNodes: [0] })
2839
+ * const postOrder = Graph.dfsPostOrder(graph, { start: [0] })
2731
2840
  * for (const node of postOrder) {
2732
2841
  * console.log(node) // 1, 2, 0
2733
2842
  * }
@@ -2738,12 +2847,12 @@ const topo = (graph, config = {}) => {
2738
2847
  */
2739
2848
  exports.topo = topo;
2740
2849
  const dfsPostOrder = (graph, config = {}) => {
2741
- const startNodes = config.startNodes ?? [];
2850
+ const start = config.start ?? [];
2742
2851
  const direction = config.direction ?? "outgoing";
2743
2852
  // Validate that all start nodes exist
2744
- for (const nodeIndex of startNodes) {
2853
+ for (const nodeIndex of start) {
2745
2854
  if (!hasNode(graph, nodeIndex)) {
2746
- throw new Error(`Start node ${nodeIndex} does not exist`);
2855
+ throw missingNode(nodeIndex);
2747
2856
  }
2748
2857
  }
2749
2858
  return new Walker(f => ({
@@ -2752,9 +2861,9 @@ const dfsPostOrder = (graph, config = {}) => {
2752
2861
  const discovered = new Set();
2753
2862
  const finished = new Set();
2754
2863
  // Initialize stack with start nodes
2755
- for (let i = startNodes.length - 1; i >= 0; i--) {
2864
+ for (let i = start.length - 1; i >= 0; i--) {
2756
2865
  stack.push({
2757
- node: startNodes[i],
2866
+ node: start[i],
2758
2867
  visitedChildren: false
2759
2868
  });
2760
2869
  }
@@ -2943,9 +3052,9 @@ const externals = (graph, config = {}) => {
2943
3052
  let current = nodeIterator.next();
2944
3053
  while (!current.done) {
2945
3054
  const [nodeIndex, nodeData] = current.value;
2946
- const adjacencyList = getMapSafe(adjacencyMap, nodeIndex);
3055
+ const adjacencyList = adjacencyMap.get(nodeIndex);
2947
3056
  // Node is external if it has no edges in the specified direction
2948
- if (Option.isNone(adjacencyList) || adjacencyList.value.length === 0) {
3057
+ if (adjacencyList === undefined || adjacencyList.length === 0) {
2949
3058
  return {
2950
3059
  done: false,
2951
3060
  value: f(nodeIndex, nodeData)