@statelyai/graph 0.5.0 → 0.6.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.
@@ -30,9 +30,10 @@ function resolveEdge(config) {
30
30
  id: config.id,
31
31
  sourceId: config.sourceId,
32
32
  targetId: config.targetId,
33
- label: config.label ?? "",
33
+ label: config.label ?? null,
34
34
  data: config.data
35
35
  };
36
+ if (config.weight !== void 0) edge.weight = config.weight;
36
37
  if (config.x !== void 0) edge.x = config.x;
37
38
  if (config.y !== void 0) edge.y = config.y;
38
39
  if (config.width !== void 0) edge.width = config.width;
@@ -570,6 +571,8 @@ function collectDescendants(graph, id) {
570
571
  /**
571
572
  * Breadth-first traversal generator yielding nodes level by level.
572
573
  *
574
+ * **O(V + E)** time, **O(V)** space.
575
+ *
573
576
  * @example
574
577
  * ```ts
575
578
  * import { createGraph, bfs } from '@statelyai/graph';
@@ -603,6 +606,8 @@ function* bfs(graph, startId) {
603
606
  /**
604
607
  * Depth-first traversal generator yielding nodes as visited.
605
608
  *
609
+ * **O(V + E)** time, **O(V)** space.
610
+ *
606
611
  * @example
607
612
  * ```ts
608
613
  * import { createGraph, dfs } from '@statelyai/graph';
@@ -648,9 +653,56 @@ function getSuccessorIds(graph, nodeId) {
648
653
  const idx = getIndex(graph);
649
654
  return (idx.outEdges.get(nodeId) ?? []).map((eid) => graph.edges[idx.edgeById.get(eid)].targetId);
650
655
  }
656
+ var MinPriorityQueue = class {
657
+ items = [];
658
+ constructor(compare) {
659
+ this.compare = compare;
660
+ }
661
+ get size() {
662
+ return this.items.length;
663
+ }
664
+ push(item) {
665
+ this.items.push(item);
666
+ this.bubbleUp(this.items.length - 1);
667
+ }
668
+ pop() {
669
+ if (this.items.length === 0) return void 0;
670
+ const first = this.items[0];
671
+ const last = this.items.pop();
672
+ if (this.items.length > 0) {
673
+ this.items[0] = last;
674
+ this.bubbleDown(0);
675
+ }
676
+ return first;
677
+ }
678
+ bubbleUp(index) {
679
+ let current = index;
680
+ while (current > 0) {
681
+ const parent = Math.floor((current - 1) / 2);
682
+ if (this.compare(this.items[current], this.items[parent]) >= 0) break;
683
+ [this.items[current], this.items[parent]] = [this.items[parent], this.items[current]];
684
+ current = parent;
685
+ }
686
+ }
687
+ bubbleDown(index) {
688
+ let current = index;
689
+ while (true) {
690
+ const left = current * 2 + 1;
691
+ const right = left + 1;
692
+ let smallest = current;
693
+ if (left < this.items.length && this.compare(this.items[left], this.items[smallest]) < 0) smallest = left;
694
+ if (right < this.items.length && this.compare(this.items[right], this.items[smallest]) < 0) smallest = right;
695
+ if (smallest === current) break;
696
+ [this.items[current], this.items[smallest]] = [this.items[smallest], this.items[current]];
697
+ current = smallest;
698
+ }
699
+ }
700
+ };
651
701
  /**
652
702
  * Checks whether the graph contains no cycles.
653
703
  *
704
+ * **O(V + E)** time.
705
+ *
654
706
  * @example
655
707
  * ```ts
656
708
  * import { createGraph, isAcyclic } from '@statelyai/graph';
@@ -711,6 +763,8 @@ function isAcyclicUndirected(graph) {
711
763
  * Returns connected components as arrays of nodes.
712
764
  * Treats all edges as undirected for connectivity.
713
765
  *
766
+ * **O(V + E)** time.
767
+ *
714
768
  * @example
715
769
  * ```ts
716
770
  * import { createGraph, getConnectedComponents } from '@statelyai/graph';
@@ -763,6 +817,8 @@ function getConnectedComponents(graph) {
763
817
  /**
764
818
  * Returns a topological ordering of nodes, or `null` if the graph is cyclic.
765
819
  *
820
+ * **O(V + E)** time (Kahn's algorithm).
821
+ *
766
822
  * @example
767
823
  * ```ts
768
824
  * import { createGraph, getTopologicalSort } from '@statelyai/graph';
@@ -806,6 +862,8 @@ function getTopologicalSort(graph) {
806
862
  /**
807
863
  * Checks whether a path exists between two nodes.
808
864
  *
865
+ * **O(V + E)** time (BFS) or **O((V + E) log V)** (Dijkstra when weighted).
866
+ *
809
867
  * @example
810
868
  * ```ts
811
869
  * import { createGraph, hasPath } from '@statelyai/graph';
@@ -828,6 +886,8 @@ function hasPath(graph, sourceId, targetId) {
828
886
  /**
829
887
  * Checks whether the graph is connected (all nodes reachable from any node).
830
888
  *
889
+ * **O(V + E)** time.
890
+ *
831
891
  * @example
832
892
  * ```ts
833
893
  * import { createGraph, isConnected } from '@statelyai/graph';
@@ -847,6 +907,8 @@ function isConnected(graph) {
847
907
  /**
848
908
  * Checks whether the graph is a tree (connected and acyclic).
849
909
  *
910
+ * **O(V + E)** time.
911
+ *
850
912
  * @example
851
913
  * ```ts
852
914
  * import { createGraph, isTree } from '@statelyai/graph';
@@ -905,7 +967,7 @@ function getNeighborEdges(graph, nodeId) {
905
967
  /**
906
968
  * Returns all shortest paths from a source node.
907
969
  * Returns all paths of equal minimum length per target (not just one).
908
- * Uses BFS by default; Dijkstra when `opts.getWeight` is provided.
970
+ * Uses BFS when all edges are unweighted; Dijkstra otherwise.
909
971
  */
910
972
  /** Compute distance + prev maps via BFS or Dijkstra. */
911
973
  function computeShortestDistances(graph, sourceId, getWeight) {
@@ -913,7 +975,7 @@ function computeShortestDistances(graph, sourceId, getWeight) {
913
975
  const prev = /* @__PURE__ */ new Map();
914
976
  dist.set(sourceId, 0);
915
977
  prev.set(sourceId, []);
916
- if (!getWeight) {
978
+ if (!getWeight && !graph.edges.some((e) => e.weight !== void 0)) {
917
979
  const queue = [sourceId];
918
980
  while (queue.length > 0) {
919
981
  const id = queue.shift();
@@ -935,19 +997,19 @@ function computeShortestDistances(graph, sourceId, getWeight) {
935
997
  }
936
998
  }
937
999
  } else {
1000
+ const effectiveWeight = getWeight ?? ((e) => e.weight ?? 1);
938
1001
  const visited = /* @__PURE__ */ new Set();
939
- const pq = [{
1002
+ const pq = new MinPriorityQueue((a, b) => a.dist - b.dist);
1003
+ pq.push({
940
1004
  id: sourceId,
941
1005
  dist: 0
942
- }];
943
- while (pq.length > 0) {
944
- let minIdx = 0;
945
- for (let i = 1; i < pq.length; i++) if (pq[i].dist < pq[minIdx].dist) minIdx = i;
946
- const { id, dist: d } = pq.splice(minIdx, 1)[0];
947
- if (visited.has(id)) continue;
1006
+ });
1007
+ while (pq.size > 0) {
1008
+ const { id, dist: d } = pq.pop();
1009
+ if (visited.has(id) || d !== dist.get(id)) continue;
948
1010
  visited.add(id);
949
1011
  for (const { neighborId, edge } of getNeighborEdges(graph, id)) {
950
- const newDist = d + getWeight(edge);
1012
+ const newDist = d + effectiveWeight(edge);
951
1013
  const existing = dist.get(neighborId);
952
1014
  if (existing === void 0 || newDist < existing) {
953
1015
  dist.set(neighborId, newDist);
@@ -996,6 +1058,9 @@ function* reconstructPaths(graph, prev, sourceNode, targetId) {
996
1058
  * Lazily yields all shortest paths from a source node.
997
1059
  * Use `getShortestPaths` for the full array.
998
1060
  *
1061
+ * **O(V + E)** time (BFS) or **O((V + E) log V)** (Dijkstra when weighted),
1062
+ * plus **O(P)** per path yielded where P is the path length.
1063
+ *
999
1064
  * @example
1000
1065
  * ```ts
1001
1066
  * import { createGraph, genShortestPaths } from '@statelyai/graph';
@@ -1027,6 +1092,8 @@ function* genShortestPaths(graph, opts) {
1027
1092
  * Returns all shortest paths from a source node as an array.
1028
1093
  * Delegates to `genShortestPaths` internally.
1029
1094
  *
1095
+ * **O(V + E)** time (BFS) or **O((V + E) log V)** (Dijkstra when weighted).
1096
+ *
1030
1097
  * @example
1031
1098
  * ```ts
1032
1099
  * import { createGraph, getShortestPaths } from '@statelyai/graph';
@@ -1050,6 +1117,8 @@ function getShortestPaths(graph, opts) {
1050
1117
  /**
1051
1118
  * Returns a single shortest path from source to target, or `undefined` if unreachable.
1052
1119
  *
1120
+ * **O(V + E)** time (BFS) or **O((V + E) log V)** (Dijkstra when weighted).
1121
+ *
1053
1122
  * @example
1054
1123
  * ```ts
1055
1124
  * import { createGraph, getShortestPath } from '@statelyai/graph';
@@ -1074,6 +1143,8 @@ function getShortestPath(graph, opts) {
1074
1143
  * Returns all simple (acyclic) paths from a source node as an array.
1075
1144
  * Delegates to `genSimplePaths` internally.
1076
1145
  *
1146
+ * **O(V!)** worst-case (exponential in dense graphs).
1147
+ *
1077
1148
  * @example
1078
1149
  * ```ts
1079
1150
  * import { createGraph, getSimplePaths } from '@statelyai/graph';
@@ -1099,6 +1170,8 @@ function getSimplePaths(graph, opts) {
1099
1170
  * Lazily yields all simple (acyclic) paths from a source node via DFS backtracking.
1100
1171
  * Use `getSimplePaths` for the full array.
1101
1172
  *
1173
+ * **O(V!)** worst-case (exponential in dense graphs).
1174
+ *
1102
1175
  * @example
1103
1176
  * ```ts
1104
1177
  * import { createGraph, genSimplePaths } from '@statelyai/graph';
@@ -1127,22 +1200,21 @@ function* genSimplePaths(graph, opts) {
1127
1200
  const targetId = opts?.to;
1128
1201
  const visited = /* @__PURE__ */ new Set();
1129
1202
  const currentSteps = [];
1130
- const found = [];
1131
- function dfsCollect(nodeId) {
1203
+ function* dfsCollect(nodeId) {
1132
1204
  visited.add(nodeId);
1133
1205
  if (targetId !== void 0) {
1134
1206
  if (nodeId === targetId) {
1135
- found.push({
1207
+ yield {
1136
1208
  source: sourceNode,
1137
1209
  steps: [...currentSteps]
1138
- });
1210
+ };
1139
1211
  visited.delete(nodeId);
1140
1212
  return;
1141
1213
  }
1142
- } else if (currentSteps.length > 0) found.push({
1214
+ } else if (currentSteps.length > 0) yield {
1143
1215
  source: sourceNode,
1144
1216
  steps: [...currentSteps]
1145
- });
1217
+ };
1146
1218
  for (const { neighborId, edge } of getNeighborEdges(graph, nodeId)) if (!visited.has(neighborId)) {
1147
1219
  const neighborNi = idx.nodeById.get(neighborId);
1148
1220
  const neighborNode = neighborNi !== void 0 ? graph.nodes[neighborNi] : graph.nodes.find((n) => n.id === neighborId);
@@ -1150,17 +1222,18 @@ function* genSimplePaths(graph, opts) {
1150
1222
  edge,
1151
1223
  node: neighborNode
1152
1224
  });
1153
- dfsCollect(neighborId);
1225
+ yield* dfsCollect(neighborId);
1154
1226
  currentSteps.pop();
1155
1227
  }
1156
1228
  visited.delete(nodeId);
1157
1229
  }
1158
- dfsCollect(sourceId);
1159
- yield* found;
1230
+ yield* dfsCollect(sourceId);
1160
1231
  }
1161
1232
  /**
1162
1233
  * Returns a single simple (acyclic) path from source to target, or `undefined` if unreachable.
1163
1234
  *
1235
+ * **O(V + E)** typical, **O(V!)** worst-case.
1236
+ *
1164
1237
  * @example
1165
1238
  * ```ts
1166
1239
  * import { createGraph, getSimplePath } from '@statelyai/graph';
@@ -1185,6 +1258,8 @@ function getSimplePath(graph, opts) {
1185
1258
  * Returns strongly connected components using Tarjan's algorithm.
1186
1259
  * Only meaningful for directed graphs.
1187
1260
  *
1261
+ * **O(V + E)** time.
1262
+ *
1188
1263
  * @example
1189
1264
  * ```ts
1190
1265
  * import { createGraph, getStronglyConnectedComponents } from '@statelyai/graph';
@@ -1244,6 +1319,8 @@ function getStronglyConnectedComponents(graph) {
1244
1319
  * Returns all elementary cycles as an array of paths.
1245
1320
  * Delegates to `genCycles` internally.
1246
1321
  *
1322
+ * **O((V + E) · C)** where C is the number of elementary cycles (can be exponential).
1323
+ *
1247
1324
  * @example
1248
1325
  * ```ts
1249
1326
  * import { createGraph, getCycles } from '@statelyai/graph';
@@ -1267,6 +1344,8 @@ function getCycles(graph) {
1267
1344
  * Lazily yields elementary cycles one at a time.
1268
1345
  * Use `getCycles` for the full array.
1269
1346
  *
1347
+ * **O((V + E) · C)** where C is the number of elementary cycles (can be exponential).
1348
+ *
1270
1349
  * @example
1271
1350
  * ```ts
1272
1351
  * import { createGraph, genCycles } from '@statelyai/graph';
@@ -1406,6 +1485,8 @@ function getNeighborEdgesAll(graph, nodeId) {
1406
1485
  * Returns a single canonical preorder (DFS visit-order) sequence.
1407
1486
  * Visits neighbors in the order they appear in the adjacency list.
1408
1487
  *
1488
+ * **O(V + E)** time.
1489
+ *
1409
1490
  * @example
1410
1491
  * ```ts
1411
1492
  * import { createGraph, getPreorder } from '@statelyai/graph';
@@ -1449,6 +1530,8 @@ function getPreorder(graph, opts) {
1449
1530
  * Returns a single canonical postorder (DFS finish-order) sequence.
1450
1531
  * Visits neighbors in the order they appear in the adjacency list.
1451
1532
  *
1533
+ * **O(V + E)** time.
1534
+ *
1452
1535
  * @example
1453
1536
  * ```ts
1454
1537
  * import { createGraph, getPostorder } from '@statelyai/graph';
@@ -1490,6 +1573,8 @@ function getPostorder(graph, opts) {
1490
1573
  /**
1491
1574
  * Returns all possible preorder sequences as an array. Can be exponential -- prefer `genPreorders`.
1492
1575
  *
1576
+ * **O(V! · V)** worst-case (exponential).
1577
+ *
1493
1578
  * @example
1494
1579
  * ```ts
1495
1580
  * import { createGraph, getPreorders } from '@statelyai/graph';
@@ -1513,6 +1598,8 @@ function getPreorders(graph, opts) {
1513
1598
  /**
1514
1599
  * Returns all possible postorder sequences as an array. Can be exponential -- prefer `genPostorders`.
1515
1600
  *
1601
+ * **O(V! · V)** worst-case (exponential).
1602
+ *
1516
1603
  * @example
1517
1604
  * ```ts
1518
1605
  * import { createGraph, getPostorders } from '@statelyai/graph';
@@ -1538,6 +1625,8 @@ function getPostorders(graph, opts) {
1538
1625
  * Different neighbor exploration orders yield different sequences.
1539
1626
  * Use `getPreorder()` for a single canonical ordering.
1540
1627
  *
1628
+ * **O(V! · V)** worst-case (exponential).
1629
+ *
1541
1630
  * @example
1542
1631
  * ```ts
1543
1632
  * import { createGraph, genPreorders } from '@statelyai/graph';
@@ -1602,6 +1691,8 @@ function* genPreorders(graph, opts) {
1602
1691
  * Different neighbor exploration orders yield different sequences.
1603
1692
  * Use `getPostorder()` for a single canonical ordering.
1604
1693
  *
1694
+ * **O(V! · V)** worst-case (exponential).
1695
+ *
1605
1696
  * @example
1606
1697
  * ```ts
1607
1698
  * import { createGraph, genPostorders } from '@statelyai/graph';
@@ -1665,6 +1756,8 @@ function* genPostorders(graph, opts) {
1665
1756
  * Only meaningful for connected undirected graphs (or the component reachable
1666
1757
  * from an arbitrary start node in directed graphs).
1667
1758
  *
1759
+ * **O(E log E)** using either edge sorting (Kruskal) or a min-heap (Prim).
1760
+ *
1668
1761
  * @example
1669
1762
  * ```ts
1670
1763
  * import { createGraph, getMinimumSpanningTree } from '@statelyai/graph';
@@ -1687,7 +1780,7 @@ function* genPostorders(graph, opts) {
1687
1780
  */
1688
1781
  function getMinimumSpanningTree(graph, opts) {
1689
1782
  const algorithm = opts?.algorithm ?? "prim";
1690
- const getWeight = opts?.getWeight ?? (() => 1);
1783
+ const getWeight = opts?.getWeight ?? ((e) => e.weight ?? 1);
1691
1784
  const mstEdges = algorithm === "kruskal" ? kruskalMST(graph, getWeight) : primMST(graph, getWeight);
1692
1785
  return createGraph({
1693
1786
  id: graph.id,
@@ -1705,7 +1798,8 @@ function getMinimumSpanningTree(graph, opts) {
1705
1798
  sourceId: e.sourceId,
1706
1799
  targetId: e.targetId,
1707
1800
  label: e.label,
1708
- data: e.data
1801
+ data: e.data,
1802
+ ...e.weight !== void 0 && { weight: e.weight }
1709
1803
  }))
1710
1804
  });
1711
1805
  }
@@ -1714,7 +1808,7 @@ function primMST(graph, getWeight) {
1714
1808
  const idx = getIndex(graph);
1715
1809
  const inMST = /* @__PURE__ */ new Set();
1716
1810
  const mstEdges = [];
1717
- const candidates = [];
1811
+ const candidates = new MinPriorityQueue((a, b) => a.weight - b.weight);
1718
1812
  function addEdgesOf(nodeId) {
1719
1813
  for (const eid of idx.outEdges.get(nodeId) ?? []) {
1720
1814
  const ai = idx.edgeById.get(eid);
@@ -1738,10 +1832,8 @@ function primMST(graph, getWeight) {
1738
1832
  const startId = graph.nodes[0].id;
1739
1833
  inMST.add(startId);
1740
1834
  addEdgesOf(startId);
1741
- while (candidates.length > 0 && inMST.size < graph.nodes.length) {
1742
- let minIdx = 0;
1743
- for (let i = 1; i < candidates.length; i++) if (candidates[i].weight < candidates[minIdx].weight) minIdx = i;
1744
- const { edge } = candidates.splice(minIdx, 1)[0];
1835
+ while (candidates.size > 0 && inMST.size < graph.nodes.length) {
1836
+ const { edge } = candidates.pop();
1745
1837
  const targetId = graph.type === "undirected" && inMST.has(edge.targetId) ? edge.sourceId : edge.targetId;
1746
1838
  if (inMST.has(targetId)) continue;
1747
1839
  inMST.add(targetId);
@@ -1780,7 +1872,9 @@ function kruskalMST(graph, getWeight) {
1780
1872
  /**
1781
1873
  * Returns shortest paths between all pairs of nodes.
1782
1874
  * Algorithm 'dijkstra' (default): runs getShortestPaths per source node.
1783
- * Algorithm 'floyd-warshall': classic O(V^3) dynamic programming.
1875
+ * Algorithm 'floyd-warshall': classic dynamic programming.
1876
+ *
1877
+ * **O(V · (V + E) log V)** (Dijkstra) or **O(V³)** (Floyd-Warshall).
1784
1878
  *
1785
1879
  * @example
1786
1880
  * ```ts
@@ -1815,7 +1909,7 @@ function dijkstraAllPaths(graph, getWeight) {
1815
1909
  }
1816
1910
  function floydWarshallAllPaths(graph, getWeight) {
1817
1911
  const idx = getIndex(graph);
1818
- const w = getWeight ?? (() => 1);
1912
+ const w = getWeight ?? ((e) => e.weight ?? 1);
1819
1913
  const nodeIds = graph.nodes.map((n$1) => n$1.id);
1820
1914
  const n = nodeIds.length;
1821
1915
  const idxOf = /* @__PURE__ */ new Map();
@@ -1898,6 +1992,100 @@ function fwReconstruct(graph, prev, idxOf, nodeIds, sourceNode, sourceIdx, targe
1898
1992
  return results;
1899
1993
  }
1900
1994
  /**
1995
+ * Returns a shortest path using A* search with an admissible heuristic.
1996
+ * More efficient than Dijkstra when a good heuristic is available.
1997
+ *
1998
+ * **O((V + E) log V)** time with a good heuristic; degrades to Dijkstra
1999
+ * with `heuristic: () => 0`.
2000
+ *
2001
+ * @example
2002
+ * ```ts
2003
+ * import { createGraph, getAStarPath } from '@statelyai/graph';
2004
+ *
2005
+ * const graph = createGraph({
2006
+ * nodes: [
2007
+ * { id: 'a', x: 0, y: 0 },
2008
+ * { id: 'b', x: 1, y: 0 },
2009
+ * { id: 'c', x: 1, y: 1 },
2010
+ * ],
2011
+ * edges: [
2012
+ * { id: 'ab', sourceId: 'a', targetId: 'b', weight: 1 },
2013
+ * { id: 'bc', sourceId: 'b', targetId: 'c', weight: 1 },
2014
+ * { id: 'ac', sourceId: 'a', targetId: 'c', weight: 3 },
2015
+ * ],
2016
+ * });
2017
+ *
2018
+ * const path = getAStarPath(graph, {
2019
+ * from: 'a',
2020
+ * to: 'c',
2021
+ * heuristic: (nodeId) => {
2022
+ * const node = graph.nodes.find(n => n.id === nodeId)!;
2023
+ * const target = graph.nodes.find(n => n.id === 'c')!;
2024
+ * return Math.abs(node.x! - target.x!) + Math.abs(node.y! - target.y!);
2025
+ * },
2026
+ * });
2027
+ * // path: a -> b -> c (weight 2, cheaper than direct a -> c)
2028
+ * ```
2029
+ */
2030
+ function getAStarPath(graph, opts) {
2031
+ const idx = getIndex(graph);
2032
+ const { from: sourceId, to: targetId, heuristic } = opts;
2033
+ const getWeight = opts.getWeight ?? ((e) => e.weight ?? 1);
2034
+ const sourceNi = idx.nodeById.get(sourceId);
2035
+ if (sourceNi === void 0) return void 0;
2036
+ if (!idx.nodeById.has(targetId)) return void 0;
2037
+ if (sourceId === targetId) return {
2038
+ source: graph.nodes[sourceNi],
2039
+ steps: []
2040
+ };
2041
+ const gScore = /* @__PURE__ */ new Map();
2042
+ const cameFrom = /* @__PURE__ */ new Map();
2043
+ const closedSet = /* @__PURE__ */ new Set();
2044
+ const openSet = new MinPriorityQueue((a, b) => a.f - b.f);
2045
+ gScore.set(sourceId, 0);
2046
+ openSet.push({
2047
+ id: sourceId,
2048
+ f: heuristic(sourceId)
2049
+ });
2050
+ while (openSet.size > 0) {
2051
+ const { id: currentId } = openSet.pop();
2052
+ if (closedSet.has(currentId)) continue;
2053
+ if (currentId === targetId) {
2054
+ const steps = [];
2055
+ let cur = targetId;
2056
+ while (cur !== sourceId) {
2057
+ const prev = cameFrom.get(cur);
2058
+ const ni = idx.nodeById.get(cur);
2059
+ steps.unshift({
2060
+ edge: prev.edge,
2061
+ node: graph.nodes[ni]
2062
+ });
2063
+ cur = prev.from;
2064
+ }
2065
+ return {
2066
+ source: graph.nodes[sourceNi],
2067
+ steps
2068
+ };
2069
+ }
2070
+ closedSet.add(currentId);
2071
+ for (const { neighborId, edge } of getNeighborEdges(graph, currentId)) {
2072
+ if (closedSet.has(neighborId)) continue;
2073
+ const tentativeG = (gScore.get(currentId) ?? Infinity) + getWeight(edge);
2074
+ if (tentativeG < (gScore.get(neighborId) ?? Infinity)) {
2075
+ cameFrom.set(neighborId, {
2076
+ from: currentId,
2077
+ edge
2078
+ });
2079
+ gScore.set(neighborId, tentativeG);
2080
+ openSet.push({
2081
+ id: neighborId,
2082
+ f: tentativeG + heuristic(neighborId)
2083
+ });
2084
+ }
2085
+ }
2086
+ }
2087
+ }
2088
+ /**
1901
2089
  * Joins two paths end-to-end. The last node of the head path must equal
1902
2090
  * the source of the tail path (the overlap node).
1903
2091
  *
@@ -1933,4 +2121,4 @@ function joinPaths(headPath, tailPath) {
1933
2121
  }
1934
2122
 
1935
2123
  //#endregion
1936
- export { addNode as A, hasNode as B, isAcyclic as C, GraphInstance as D, joinPaths as E, deleteEntities as F, updateEntities as H, deleteNode as I, getEdge as L, createGraphFromTransition as M, createVisualGraph as N, addEdge as O, deleteEdge as P, getNode as R, hasPath as S, isTree as T, updateNode as U, updateEdge as V, getShortestPaths as _, genPreorders as a, getStronglyConnectedComponents as b, getAllPairsShortestPaths as c, getMinimumSpanningTree as d, getPostorder as f, getShortestPath as g, getPreorders as h, genPostorders as i, createGraph as j, addEntities as k, getConnectedComponents as l, getPreorder as m, dfs as n, genShortestPaths as o, getPostorders as p, genCycles as r, genSimplePaths as s, bfs as t, getCycles as u, getSimplePath as v, isConnected as w, getTopologicalSort as x, getSimplePaths as y, hasEdge as z };
2124
+ export { addEntities as A, hasEdge as B, hasPath as C, joinPaths as D, isTree as E, deleteEdge as F, updateEdge as H, deleteEntities as I, deleteNode as L, createGraph as M, createGraphFromTransition as N, GraphInstance as O, createVisualGraph as P, getEdge as R, getTopologicalSort as S, isConnected as T, updateEntities as U, hasNode as V, updateNode as W, getShortestPath as _, genPreorders as a, getSimplePaths as b, getAStarPath as c, getCycles as d, getMinimumSpanningTree as f, getPreorders as g, getPreorder as h, genPostorders as i, addNode as j, addEdge as k, getAllPairsShortestPaths as l, getPostorders as m, dfs as n, genShortestPaths as o, getPostorder as p, genCycles as r, genSimplePaths as s, bfs as t, getConnectedComponents as u, getShortestPaths as v, isAcyclic as w, getStronglyConnectedComponents as x, getSimplePath as y, getNode as z };