effect 3.18.4 → 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.
Files changed (51) hide show
  1. package/HashRing/package.json +6 -0
  2. package/dist/cjs/Array.js.map +1 -1
  3. package/dist/cjs/Effect.js.map +1 -1
  4. package/dist/cjs/Graph.js +290 -177
  5. package/dist/cjs/Graph.js.map +1 -1
  6. package/dist/cjs/HashRing.js +257 -0
  7. package/dist/cjs/HashRing.js.map +1 -0
  8. package/dist/cjs/JSONSchema.js +39 -8
  9. package/dist/cjs/JSONSchema.js.map +1 -1
  10. package/dist/cjs/TestClock.js +8 -8
  11. package/dist/cjs/TestClock.js.map +1 -1
  12. package/dist/cjs/index.js +4 -2
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/internal/version.js +1 -1
  15. package/dist/dts/Array.d.ts +3 -3
  16. package/dist/dts/Array.d.ts.map +1 -1
  17. package/dist/dts/Effect.d.ts +6 -1
  18. package/dist/dts/Effect.d.ts.map +1 -1
  19. package/dist/dts/Graph.d.ts +147 -49
  20. package/dist/dts/Graph.d.ts.map +1 -1
  21. package/dist/dts/HashRing.d.ts +158 -0
  22. package/dist/dts/HashRing.d.ts.map +1 -0
  23. package/dist/dts/JSONSchema.d.ts +3 -2
  24. package/dist/dts/JSONSchema.d.ts.map +1 -1
  25. package/dist/dts/Types.d.ts +1 -1
  26. package/dist/dts/Types.d.ts.map +1 -1
  27. package/dist/dts/index.d.ts +5 -0
  28. package/dist/dts/index.d.ts.map +1 -1
  29. package/dist/esm/Array.js.map +1 -1
  30. package/dist/esm/Effect.js.map +1 -1
  31. package/dist/esm/Graph.js +286 -175
  32. package/dist/esm/Graph.js.map +1 -1
  33. package/dist/esm/HashRing.js +245 -0
  34. package/dist/esm/HashRing.js.map +1 -0
  35. package/dist/esm/JSONSchema.js +35 -6
  36. package/dist/esm/JSONSchema.js.map +1 -1
  37. package/dist/esm/TestClock.js +8 -8
  38. package/dist/esm/TestClock.js.map +1 -1
  39. package/dist/esm/index.js +5 -0
  40. package/dist/esm/index.js.map +1 -1
  41. package/dist/esm/internal/version.js +1 -1
  42. package/package.json +9 -1
  43. package/src/Array.ts +4 -4
  44. package/src/Effect.ts +6 -1
  45. package/src/Graph.ts +415 -218
  46. package/src/HashRing.ts +387 -0
  47. package/src/JSONSchema.ts +39 -9
  48. package/src/TestClock.ts +9 -9
  49. package/src/Types.ts +3 -1
  50. package/src/index.ts +6 -0
  51. package/src/internal/version.ts +1 -1
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
  }
@@ -1225,15 +1229,19 @@ const edgeCount = graph => graph.edges.size;
1225
1229
  */
1226
1230
  exports.edgeCount = edgeCount;
1227
1231
  const neighbors = (graph, nodeIndex) => {
1228
- const adjacencyList = getMapSafe(graph.adjacency, nodeIndex);
1229
- if (Option.isNone(adjacencyList)) {
1232
+ // For undirected graphs, use the specialized helper that returns the other endpoint
1233
+ if (graph.type === "undirected") {
1234
+ return getUndirectedNeighbors(graph, nodeIndex);
1235
+ }
1236
+ const adjacencyList = graph.adjacency.get(nodeIndex);
1237
+ if (adjacencyList === undefined) {
1230
1238
  return [];
1231
1239
  }
1232
1240
  const result = [];
1233
- for (const edgeIndex of adjacencyList.value) {
1234
- const edge = getMapSafe(graph.edges, edgeIndex);
1235
- if (Option.isSome(edge)) {
1236
- 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);
1237
1245
  }
1238
1246
  }
1239
1247
  return result;
@@ -1267,24 +1275,21 @@ const neighbors = (graph, nodeIndex) => {
1267
1275
  exports.neighbors = neighbors;
1268
1276
  const neighborsDirected = (graph, nodeIndex, direction) => {
1269
1277
  const adjacencyMap = direction === "incoming" ? graph.reverseAdjacency : graph.adjacency;
1270
- const adjacencyList = getMapSafe(adjacencyMap, nodeIndex);
1271
- if (Option.isNone(adjacencyList)) {
1278
+ const adjacencyList = adjacencyMap.get(nodeIndex);
1279
+ if (adjacencyList === undefined) {
1272
1280
  return [];
1273
1281
  }
1274
1282
  const result = [];
1275
- for (const edgeIndex of adjacencyList.value) {
1276
- const edge = getMapSafe(graph.edges, edgeIndex);
1277
- if (Option.isSome(edge)) {
1283
+ for (const edgeIndex of adjacencyList) {
1284
+ const edge = graph.edges.get(edgeIndex);
1285
+ if (edge !== undefined) {
1278
1286
  // For incoming direction, we want the source node instead of target
1279
- const neighborNode = direction === "incoming" ? edge.value.source : edge.value.target;
1287
+ const neighborNode = direction === "incoming" ? edge.source : edge.target;
1280
1288
  result.push(neighborNode);
1281
1289
  }
1282
1290
  }
1283
1291
  return result;
1284
1292
  };
1285
- // =============================================================================
1286
- // GraphViz Export
1287
- // =============================================================================
1288
1293
  /**
1289
1294
  * Exports a graph to GraphViz DOT format for visualization.
1290
1295
  *
@@ -1341,6 +1346,98 @@ const toGraphViz = (graph, options) => {
1341
1346
  lines.push("}");
1342
1347
  return lines.join("\n");
1343
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
+ };
1344
1441
  // =============================================================================
1345
1442
  // =============================================================================
1346
1443
  // Graph Structure Analysis Algorithms (Phase 5A)
@@ -1379,7 +1476,7 @@ const toGraphViz = (graph, options) => {
1379
1476
  * @since 3.18.0
1380
1477
  * @category algorithms
1381
1478
  */
1382
- exports.toGraphViz = toGraphViz;
1479
+ exports.toMermaid = toMermaid;
1383
1480
  const isAcyclic = graph => {
1384
1481
  // Use existing cycle flag if available
1385
1482
  if (Option.isSome(graph.isAcyclic)) {
@@ -1525,13 +1622,13 @@ exports.isBipartite = isBipartite;
1525
1622
  const getUndirectedNeighbors = (graph, nodeIndex) => {
1526
1623
  const neighbors = new Set();
1527
1624
  // Check edges where this node is the source
1528
- const adjacencyList = getMapSafe(graph.adjacency, nodeIndex);
1529
- if (Option.isSome(adjacencyList)) {
1530
- for (const edgeIndex of adjacencyList.value) {
1531
- const edge = getMapSafe(graph.edges, edgeIndex);
1532
- 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) {
1533
1630
  // For undirected graphs, the neighbor is the other endpoint
1534
- const otherNode = edge.value.source === nodeIndex ? edge.value.target : edge.value.source;
1631
+ const otherNode = edge.source === nodeIndex ? edge.target : edge.source;
1535
1632
  neighbors.add(otherNode);
1536
1633
  }
1537
1634
  }
@@ -1666,12 +1763,12 @@ const stronglyConnectedComponents = graph => {
1666
1763
  visited.add(node);
1667
1764
  scc.push(node);
1668
1765
  // Use reverse adjacency (transpose graph)
1669
- const reverseAdjacency = getMapSafe(graph.reverseAdjacency, node);
1670
- if (Option.isSome(reverseAdjacency)) {
1671
- for (const edgeIndex of reverseAdjacency.value) {
1672
- const edge = getMapSafe(graph.edges, edgeIndex);
1673
- if (Option.isSome(edge)) {
1674
- 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;
1675
1772
  if (!visited.has(predecessor)) {
1676
1773
  stack.push(predecessor);
1677
1774
  }
@@ -1702,7 +1799,7 @@ const stronglyConnectedComponents = graph => {
1702
1799
  * Graph.addEdge(mutable, b, c, 2)
1703
1800
  * })
1704
1801
  *
1705
- * const result = Graph.dijkstra(graph, 0, 2, (edgeData) => edgeData)
1802
+ * const result = Graph.dijkstra(graph, { source: 0, target: 2, cost: (edgeData) => edgeData })
1706
1803
  * if (Option.isSome(result)) {
1707
1804
  * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C
1708
1805
  * console.log(result.value.distance) // 7 - total distance
@@ -1713,20 +1810,25 @@ const stronglyConnectedComponents = graph => {
1713
1810
  * @category algorithms
1714
1811
  */
1715
1812
  exports.stronglyConnectedComponents = stronglyConnectedComponents;
1716
- const dijkstra = (graph, source, target, edgeWeight) => {
1813
+ const dijkstra = (graph, config) => {
1814
+ const {
1815
+ cost,
1816
+ source,
1817
+ target
1818
+ } = config;
1717
1819
  // Validate that source and target nodes exist
1718
1820
  if (!graph.nodes.has(source)) {
1719
- throw new Error(`Source node ${source} does not exist`);
1821
+ throw missingNode(source);
1720
1822
  }
1721
1823
  if (!graph.nodes.has(target)) {
1722
- throw new Error(`Target node ${target} does not exist`);
1824
+ throw missingNode(target);
1723
1825
  }
1724
1826
  // Early return if source equals target
1725
1827
  if (source === target) {
1726
1828
  return Option.some({
1727
1829
  path: [source],
1728
1830
  distance: 0,
1729
- edgeWeights: []
1831
+ costs: []
1730
1832
  });
1731
1833
  }
1732
1834
  // Distance tracking and priority queue simulation
@@ -1766,13 +1868,13 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1766
1868
  // Get current distance
1767
1869
  const currentDistance = distances.get(currentNode);
1768
1870
  // Examine all outgoing edges
1769
- const adjacencyList = getMapSafe(graph.adjacency, currentNode);
1770
- if (Option.isSome(adjacencyList)) {
1771
- for (const edgeIndex of adjacencyList.value) {
1772
- const edge = getMapSafe(graph.edges, edgeIndex);
1773
- if (Option.isSome(edge)) {
1774
- const neighbor = edge.value.target;
1775
- 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);
1776
1878
  // Validate non-negative weights
1777
1879
  if (weight < 0) {
1778
1880
  throw new Error(`Dijkstra's algorithm requires non-negative edge weights, found ${weight}`);
@@ -1784,7 +1886,7 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1784
1886
  distances.set(neighbor, newDistance);
1785
1887
  previous.set(neighbor, {
1786
1888
  node: currentNode,
1787
- edgeData: edge.value.data
1889
+ edgeData: edge.data
1788
1890
  });
1789
1891
  // Add to priority queue if not visited
1790
1892
  if (!visited.has(neighbor)) {
@@ -1805,13 +1907,13 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1805
1907
  }
1806
1908
  // Reconstruct path
1807
1909
  const path = [];
1808
- const edgeWeights = [];
1910
+ const costs = [];
1809
1911
  let currentNode = target;
1810
1912
  while (currentNode !== null) {
1811
1913
  path.unshift(currentNode);
1812
1914
  const prev = previous.get(currentNode);
1813
1915
  if (prev !== null) {
1814
- edgeWeights.unshift(prev.edgeData);
1916
+ costs.unshift(prev.edgeData);
1815
1917
  currentNode = prev.node;
1816
1918
  } else {
1817
1919
  currentNode = null;
@@ -1820,7 +1922,7 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1820
1922
  return Option.some({
1821
1923
  path,
1822
1924
  distance: targetDistance,
1823
- edgeWeights
1925
+ costs
1824
1926
  });
1825
1927
  };
1826
1928
  /**
@@ -1851,7 +1953,7 @@ const dijkstra = (graph, source, target, edgeWeight) => {
1851
1953
  * @category algorithms
1852
1954
  */
1853
1955
  exports.dijkstra = dijkstra;
1854
- const floydWarshall = (graph, edgeWeight) => {
1956
+ const floydWarshall = (graph, cost) => {
1855
1957
  // Get all nodes for Floyd-Warshall algorithm (needs array for nested iteration)
1856
1958
  const allNodes = Array.from(graph.nodes.keys());
1857
1959
  // Initialize distance matrix
@@ -1871,7 +1973,7 @@ const floydWarshall = (graph, edgeWeight) => {
1871
1973
  }
1872
1974
  // Set edge weights
1873
1975
  for (const [, edgeData] of graph.edges) {
1874
- const weight = edgeWeight(edgeData.data);
1976
+ const weight = cost(edgeData.data);
1875
1977
  const i = edgeData.source;
1876
1978
  const j = edgeData.target;
1877
1979
  // Use minimum weight if multiple edges exist
@@ -1904,17 +2006,17 @@ const floydWarshall = (graph, edgeWeight) => {
1904
2006
  }
1905
2007
  // Build result paths and edge weights
1906
2008
  const paths = new Map();
1907
- const resultEdgeWeights = new Map();
2009
+ const resultCosts = new Map();
1908
2010
  for (const i of allNodes) {
1909
2011
  paths.set(i, new Map());
1910
- resultEdgeWeights.set(i, new Map());
2012
+ resultCosts.set(i, new Map());
1911
2013
  for (const j of allNodes) {
1912
2014
  if (i === j) {
1913
2015
  paths.get(i).set(j, [i]);
1914
- resultEdgeWeights.get(i).set(j, []);
2016
+ resultCosts.get(i).set(j, []);
1915
2017
  } else if (dist.get(i).get(j) === Infinity) {
1916
2018
  paths.get(i).set(j, null);
1917
- resultEdgeWeights.get(i).set(j, []);
2019
+ resultCosts.get(i).set(j, []);
1918
2020
  } else {
1919
2021
  // Reconstruct path iteratively
1920
2022
  const path = [];
@@ -1932,14 +2034,14 @@ const floydWarshall = (graph, edgeWeight) => {
1932
2034
  path.push(current);
1933
2035
  }
1934
2036
  paths.get(i).set(j, path);
1935
- resultEdgeWeights.get(i).set(j, weights);
2037
+ resultCosts.get(i).set(j, weights);
1936
2038
  }
1937
2039
  }
1938
2040
  }
1939
2041
  return {
1940
2042
  distances: dist,
1941
2043
  paths,
1942
- edgeWeights: resultEdgeWeights
2044
+ costs: resultCosts
1943
2045
  };
1944
2046
  };
1945
2047
  /**
@@ -1965,7 +2067,7 @@ const floydWarshall = (graph, edgeWeight) => {
1965
2067
  * const heuristic = (nodeData: {x: number, y: number}, targetData: {x: number, y: number}) =>
1966
2068
  * Math.abs(nodeData.x - targetData.x) + Math.abs(nodeData.y - targetData.y)
1967
2069
  *
1968
- * const result = Graph.astar(graph, 0, 2, (edgeData) => edgeData, heuristic)
2070
+ * const result = Graph.astar(graph, { source: 0, target: 2, cost: (edgeData) => edgeData, heuristic })
1969
2071
  * if (Option.isSome(result)) {
1970
2072
  * console.log(result.value.path) // [0, 1, 2] - shortest path
1971
2073
  * console.log(result.value.distance) // 2 - total distance
@@ -1976,25 +2078,31 @@ const floydWarshall = (graph, edgeWeight) => {
1976
2078
  * @category algorithms
1977
2079
  */
1978
2080
  exports.floydWarshall = floydWarshall;
1979
- const astar = (graph, source, target, edgeWeight, heuristic) => {
2081
+ const astar = (graph, config) => {
2082
+ const {
2083
+ cost,
2084
+ heuristic,
2085
+ source,
2086
+ target
2087
+ } = config;
1980
2088
  // Validate that source and target nodes exist
1981
2089
  if (!graph.nodes.has(source)) {
1982
- throw new Error(`Source node ${source} does not exist`);
2090
+ throw missingNode(source);
1983
2091
  }
1984
2092
  if (!graph.nodes.has(target)) {
1985
- throw new Error(`Target node ${target} does not exist`);
2093
+ throw missingNode(target);
1986
2094
  }
1987
2095
  // Early return if source equals target
1988
2096
  if (source === target) {
1989
2097
  return Option.some({
1990
2098
  path: [source],
1991
2099
  distance: 0,
1992
- edgeWeights: []
2100
+ costs: []
1993
2101
  });
1994
2102
  }
1995
2103
  // Get target node data for heuristic calculations
1996
- const targetNodeData = getMapSafe(graph.nodes, target);
1997
- if (Option.isNone(targetNodeData)) {
2104
+ const targetNodeData = graph.nodes.get(target);
2105
+ if (targetNodeData === undefined) {
1998
2106
  throw new Error(`Target node ${target} data not found`);
1999
2107
  }
2000
2108
  // Distance tracking (g-score) and f-score (g + h)
@@ -2010,9 +2118,9 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2010
2118
  previous.set(node, null);
2011
2119
  }
2012
2120
  // Calculate initial f-score for source
2013
- const sourceNodeData = getMapSafe(graph.nodes, source);
2014
- if (Option.isSome(sourceNodeData)) {
2015
- const h = heuristic(sourceNodeData.value, targetNodeData.value);
2121
+ const sourceNodeData = graph.nodes.get(source);
2122
+ if (sourceNodeData !== undefined) {
2123
+ const h = heuristic(sourceNodeData, targetNodeData);
2016
2124
  fScore.set(source, h);
2017
2125
  }
2018
2126
  // Priority queue using f-score (total estimated cost)
@@ -2042,13 +2150,13 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2042
2150
  // Get current g-score
2043
2151
  const currentGScore = gScore.get(currentNode);
2044
2152
  // Examine all outgoing edges
2045
- const adjacencyList = getMapSafe(graph.adjacency, currentNode);
2046
- if (Option.isSome(adjacencyList)) {
2047
- for (const edgeIndex of adjacencyList.value) {
2048
- const edge = getMapSafe(graph.edges, edgeIndex);
2049
- if (Option.isSome(edge)) {
2050
- const neighbor = edge.value.target;
2051
- 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);
2052
2160
  // Validate non-negative weights
2053
2161
  if (weight < 0) {
2054
2162
  throw new Error(`A* algorithm requires non-negative edge weights, found ${weight}`);
@@ -2061,12 +2169,12 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2061
2169
  gScore.set(neighbor, tentativeGScore);
2062
2170
  previous.set(neighbor, {
2063
2171
  node: currentNode,
2064
- edgeData: edge.value.data
2172
+ edgeData: edge.data
2065
2173
  });
2066
2174
  // Calculate f-score using heuristic
2067
- const neighborNodeData = getMapSafe(graph.nodes, neighbor);
2068
- if (Option.isSome(neighborNodeData)) {
2069
- const h = heuristic(neighborNodeData.value, targetNodeData.value);
2175
+ const neighborNodeData = graph.nodes.get(neighbor);
2176
+ if (neighborNodeData !== undefined) {
2177
+ const h = heuristic(neighborNodeData, targetNodeData);
2070
2178
  const f = tentativeGScore + h;
2071
2179
  fScore.set(neighbor, f);
2072
2180
  // Add to open set if not visited
@@ -2089,13 +2197,13 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2089
2197
  }
2090
2198
  // Reconstruct path
2091
2199
  const path = [];
2092
- const edgeWeights = [];
2200
+ const costs = [];
2093
2201
  let currentNode = target;
2094
2202
  while (currentNode !== null) {
2095
2203
  path.unshift(currentNode);
2096
2204
  const prev = previous.get(currentNode);
2097
2205
  if (prev !== null) {
2098
- edgeWeights.unshift(prev.edgeData);
2206
+ costs.unshift(prev.edgeData);
2099
2207
  currentNode = prev.node;
2100
2208
  } else {
2101
2209
  currentNode = null;
@@ -2104,7 +2212,7 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2104
2212
  return Option.some({
2105
2213
  path,
2106
2214
  distance: targetGScore,
2107
- edgeWeights
2215
+ costs
2108
2216
  });
2109
2217
  };
2110
2218
  /**
@@ -2127,7 +2235,7 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2127
2235
  * Graph.addEdge(mutable, a, c, 5)
2128
2236
  * })
2129
2237
  *
2130
- * const result = Graph.bellmanFord(graph, 0, 2, (edgeData) => edgeData)
2238
+ * const result = Graph.bellmanFord(graph, { source: 0, target: 2, cost: (edgeData) => edgeData })
2131
2239
  * if (Option.isSome(result)) {
2132
2240
  * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C
2133
2241
  * console.log(result.value.distance) // 2 - total distance
@@ -2138,20 +2246,25 @@ const astar = (graph, source, target, edgeWeight, heuristic) => {
2138
2246
  * @category algorithms
2139
2247
  */
2140
2248
  exports.astar = astar;
2141
- const bellmanFord = (graph, source, target, edgeWeight) => {
2249
+ const bellmanFord = (graph, config) => {
2250
+ const {
2251
+ cost,
2252
+ source,
2253
+ target
2254
+ } = config;
2142
2255
  // Validate that source and target nodes exist
2143
2256
  if (!graph.nodes.has(source)) {
2144
- throw new Error(`Source node ${source} does not exist`);
2257
+ throw missingNode(source);
2145
2258
  }
2146
2259
  if (!graph.nodes.has(target)) {
2147
- throw new Error(`Target node ${target} does not exist`);
2260
+ throw missingNode(target);
2148
2261
  }
2149
2262
  // Early return if source equals target
2150
2263
  if (source === target) {
2151
2264
  return Option.some({
2152
2265
  path: [source],
2153
2266
  distance: 0,
2154
- edgeWeights: []
2267
+ costs: []
2155
2268
  });
2156
2269
  }
2157
2270
  // Initialize distances and predecessors
@@ -2165,7 +2278,7 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2165
2278
  // Collect all edges for relaxation
2166
2279
  const edges = [];
2167
2280
  for (const [, edgeData] of graph.edges) {
2168
- const weight = edgeWeight(edgeData.data);
2281
+ const weight = cost(edgeData.data);
2169
2282
  edges.push({
2170
2283
  source: edgeData.source,
2171
2284
  target: edgeData.target,
@@ -2208,12 +2321,12 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2208
2321
  if (affectedNodes.has(node)) continue;
2209
2322
  affectedNodes.add(node);
2210
2323
  // Add all nodes reachable from this node
2211
- const adjacencyList = getMapSafe(graph.adjacency, node);
2212
- if (Option.isSome(adjacencyList)) {
2213
- for (const edgeIndex of adjacencyList.value) {
2214
- const edge = getMapSafe(graph.edges, edgeIndex);
2215
- if (Option.isSome(edge)) {
2216
- 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);
2217
2330
  }
2218
2331
  }
2219
2332
  }
@@ -2231,13 +2344,13 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2231
2344
  }
2232
2345
  // Reconstruct path
2233
2346
  const path = [];
2234
- const edgeWeights = [];
2347
+ const costs = [];
2235
2348
  let currentNode = target;
2236
2349
  while (currentNode !== null) {
2237
2350
  path.unshift(currentNode);
2238
2351
  const prev = previous.get(currentNode);
2239
2352
  if (prev !== null) {
2240
- edgeWeights.unshift(prev.edgeData);
2353
+ costs.unshift(prev.edgeData);
2241
2354
  currentNode = prev.node;
2242
2355
  } else {
2243
2356
  currentNode = null;
@@ -2246,7 +2359,7 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2246
2359
  return Option.some({
2247
2360
  path,
2248
2361
  distance: targetDistance,
2249
- edgeWeights
2362
+ costs
2250
2363
  });
2251
2364
  };
2252
2365
  /**
@@ -2267,7 +2380,7 @@ const bellmanFord = (graph, source, target, edgeWeight) => {
2267
2380
  * })
2268
2381
  *
2269
2382
  * // Both traversal and element iterators return NodeWalker
2270
- * const dfsNodes: Graph.NodeWalker<string> = Graph.dfs(graph, { startNodes: [0] })
2383
+ * const dfsNodes: Graph.NodeWalker<string> = Graph.dfs(graph, { start: [0] })
2271
2384
  * const allNodes: Graph.NodeWalker<string> = Graph.nodes(graph)
2272
2385
  *
2273
2386
  * // Common interface for working with node iterables
@@ -2304,7 +2417,7 @@ class Walker {
2304
2417
  * Graph.addEdge(mutable, a, b, 1)
2305
2418
  * })
2306
2419
  *
2307
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2420
+ * const dfs = Graph.dfs(graph, { start: [0] })
2308
2421
  *
2309
2422
  * // Map to just the node data
2310
2423
  * const values = Array.from(dfs.visit((index, data) => data))
@@ -2337,7 +2450,7 @@ class Walker {
2337
2450
  * Graph.addEdge(mutable, a, b, 1)
2338
2451
  * })
2339
2452
  *
2340
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2453
+ * const dfs = Graph.dfs(graph, { start: [0] })
2341
2454
  *
2342
2455
  * // Map to just the node data
2343
2456
  * const values = Array.from(dfs.visit((index, data) => data))
@@ -2369,7 +2482,7 @@ class Walker {
2369
2482
  * Graph.addEdge(mutable, a, b, 1)
2370
2483
  * })
2371
2484
  *
2372
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2485
+ * const dfs = Graph.dfs(graph, { start: [0] })
2373
2486
  * const indices = Array.from(Graph.indices(dfs))
2374
2487
  * console.log(indices) // [0, 1]
2375
2488
  * ```
@@ -2392,7 +2505,7 @@ const indices = walker => walker.visit((index, _) => index);
2392
2505
  * Graph.addEdge(mutable, a, b, 1)
2393
2506
  * })
2394
2507
  *
2395
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2508
+ * const dfs = Graph.dfs(graph, { start: [0] })
2396
2509
  * const values = Array.from(Graph.values(dfs))
2397
2510
  * console.log(values) // ["A", "B"]
2398
2511
  * ```
@@ -2415,7 +2528,7 @@ const values = walker => walker.visit((_, data) => data);
2415
2528
  * Graph.addEdge(mutable, a, b, 1)
2416
2529
  * })
2417
2530
  *
2418
- * const dfs = Graph.dfs(graph, { startNodes: [0] })
2531
+ * const dfs = Graph.dfs(graph, { start: [0] })
2419
2532
  * const entries = Array.from(Graph.entries(dfs))
2420
2533
  * console.log(entries) // [[0, "A"], [1, "B"]]
2421
2534
  * ```
@@ -2444,7 +2557,7 @@ const entries = walker => walker.visit((index, data) => [index, data]);
2444
2557
  * })
2445
2558
  *
2446
2559
  * // Start from a specific node
2447
- * const dfs1 = Graph.dfs(graph, { startNodes: [0] })
2560
+ * const dfs1 = Graph.dfs(graph, { start: [0] })
2448
2561
  * for (const nodeIndex of Graph.indices(dfs1)) {
2449
2562
  * console.log(nodeIndex) // Traverses in DFS order: 0, 1, 2
2450
2563
  * }
@@ -2459,17 +2572,17 @@ const entries = walker => walker.visit((index, data) => [index, data]);
2459
2572
  */
2460
2573
  exports.entries = entries;
2461
2574
  const dfs = (graph, config = {}) => {
2462
- const startNodes = config.startNodes ?? [];
2575
+ const start = config.start ?? [];
2463
2576
  const direction = config.direction ?? "outgoing";
2464
2577
  // Validate that all start nodes exist
2465
- for (const nodeIndex of startNodes) {
2578
+ for (const nodeIndex of start) {
2466
2579
  if (!hasNode(graph, nodeIndex)) {
2467
- throw new Error(`Start node ${nodeIndex} does not exist`);
2580
+ throw missingNode(nodeIndex);
2468
2581
  }
2469
2582
  }
2470
2583
  return new Walker(f => ({
2471
2584
  [Symbol.iterator]: () => {
2472
- const stack = [...startNodes];
2585
+ const stack = [...start];
2473
2586
  const discovered = new Set();
2474
2587
  const nextMapped = () => {
2475
2588
  while (stack.length > 0) {
@@ -2478,8 +2591,8 @@ const dfs = (graph, config = {}) => {
2478
2591
  continue;
2479
2592
  }
2480
2593
  discovered.add(current);
2481
- const nodeDataOption = getMapSafe(graph.nodes, current);
2482
- if (Option.isNone(nodeDataOption)) {
2594
+ const nodeDataOption = graph.nodes.get(current);
2595
+ if (nodeDataOption === undefined) {
2483
2596
  continue;
2484
2597
  }
2485
2598
  const neighbors = neighborsDirected(graph, current, direction);
@@ -2491,7 +2604,7 @@ const dfs = (graph, config = {}) => {
2491
2604
  }
2492
2605
  return {
2493
2606
  done: false,
2494
- value: f(current, nodeDataOption.value)
2607
+ value: f(current, nodeDataOption)
2495
2608
  };
2496
2609
  }
2497
2610
  return {
@@ -2524,7 +2637,7 @@ const dfs = (graph, config = {}) => {
2524
2637
  * })
2525
2638
  *
2526
2639
  * // Start from a specific node
2527
- * const bfs1 = Graph.bfs(graph, { startNodes: [0] })
2640
+ * const bfs1 = Graph.bfs(graph, { start: [0] })
2528
2641
  * for (const nodeIndex of Graph.indices(bfs1)) {
2529
2642
  * console.log(nodeIndex) // Traverses in BFS order: 0, 1, 2
2530
2643
  * }
@@ -2539,17 +2652,17 @@ const dfs = (graph, config = {}) => {
2539
2652
  */
2540
2653
  exports.dfs = dfs;
2541
2654
  const bfs = (graph, config = {}) => {
2542
- const startNodes = config.startNodes ?? [];
2655
+ const start = config.start ?? [];
2543
2656
  const direction = config.direction ?? "outgoing";
2544
2657
  // Validate that all start nodes exist
2545
- for (const nodeIndex of startNodes) {
2658
+ for (const nodeIndex of start) {
2546
2659
  if (!hasNode(graph, nodeIndex)) {
2547
- throw new Error(`Start node ${nodeIndex} does not exist`);
2660
+ throw missingNode(nodeIndex);
2548
2661
  }
2549
2662
  }
2550
2663
  return new Walker(f => ({
2551
2664
  [Symbol.iterator]: () => {
2552
- const queue = [...startNodes];
2665
+ const queue = [...start];
2553
2666
  const discovered = new Set();
2554
2667
  const nextMapped = () => {
2555
2668
  while (queue.length > 0) {
@@ -2638,7 +2751,7 @@ const topo = (graph, config = {}) => {
2638
2751
  // Validate that all initial nodes exist
2639
2752
  for (const nodeIndex of initials) {
2640
2753
  if (!hasNode(graph, nodeIndex)) {
2641
- throw new Error(`Initial node ${nodeIndex} does not exist`);
2754
+ throw missingNode(nodeIndex);
2642
2755
  }
2643
2756
  }
2644
2757
  return new Walker(f => ({
@@ -2723,7 +2836,7 @@ const topo = (graph, config = {}) => {
2723
2836
  * })
2724
2837
  *
2725
2838
  * // Postorder: children before parents
2726
- * const postOrder = Graph.dfsPostOrder(graph, { startNodes: [0] })
2839
+ * const postOrder = Graph.dfsPostOrder(graph, { start: [0] })
2727
2840
  * for (const node of postOrder) {
2728
2841
  * console.log(node) // 1, 2, 0
2729
2842
  * }
@@ -2734,12 +2847,12 @@ const topo = (graph, config = {}) => {
2734
2847
  */
2735
2848
  exports.topo = topo;
2736
2849
  const dfsPostOrder = (graph, config = {}) => {
2737
- const startNodes = config.startNodes ?? [];
2850
+ const start = config.start ?? [];
2738
2851
  const direction = config.direction ?? "outgoing";
2739
2852
  // Validate that all start nodes exist
2740
- for (const nodeIndex of startNodes) {
2853
+ for (const nodeIndex of start) {
2741
2854
  if (!hasNode(graph, nodeIndex)) {
2742
- throw new Error(`Start node ${nodeIndex} does not exist`);
2855
+ throw missingNode(nodeIndex);
2743
2856
  }
2744
2857
  }
2745
2858
  return new Walker(f => ({
@@ -2748,9 +2861,9 @@ const dfsPostOrder = (graph, config = {}) => {
2748
2861
  const discovered = new Set();
2749
2862
  const finished = new Set();
2750
2863
  // Initialize stack with start nodes
2751
- for (let i = startNodes.length - 1; i >= 0; i--) {
2864
+ for (let i = start.length - 1; i >= 0; i--) {
2752
2865
  stack.push({
2753
- node: startNodes[i],
2866
+ node: start[i],
2754
2867
  visitedChildren: false
2755
2868
  });
2756
2869
  }
@@ -2939,9 +3052,9 @@ const externals = (graph, config = {}) => {
2939
3052
  let current = nodeIterator.next();
2940
3053
  while (!current.done) {
2941
3054
  const [nodeIndex, nodeData] = current.value;
2942
- const adjacencyList = getMapSafe(adjacencyMap, nodeIndex);
3055
+ const adjacencyList = adjacencyMap.get(nodeIndex);
2943
3056
  // Node is external if it has no edges in the specified direction
2944
- if (Option.isNone(adjacencyList) || adjacencyList.value.length === 0) {
3057
+ if (adjacencyList === undefined || adjacencyList.length === 0) {
2945
3058
  return {
2946
3059
  done: false,
2947
3060
  value: f(nodeIndex, nodeData)