@statelyai/graph 2.0.0 → 2.1.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 (73) hide show
  1. package/README.md +67 -19
  2. package/dist/{algorithms-CsGNehct.d.mts → algorithms-D1cgly0g.d.mts} +145 -6
  3. package/dist/{algorithms-DF1pSQGv.mjs → algorithms-DBpH74hR.mjs} +673 -891
  4. package/dist/algorithms.d.mts +2 -2
  5. package/dist/algorithms.mjs +2 -2
  6. package/dist/config-Dt5u1gSf.mjs +793 -0
  7. package/dist/{converter-DyCJJfTe.mjs → converter-DB6Rg6Vd.mjs} +2 -2
  8. package/dist/formats/adjacency-list/index.d.mts +1 -1
  9. package/dist/formats/adjacency-list/index.mjs +1 -1
  10. package/dist/formats/converter/index.d.mts +1 -1
  11. package/dist/formats/converter/index.mjs +1 -1
  12. package/dist/formats/cytoscape/index.d.mts +4 -4
  13. package/dist/formats/cytoscape/index.mjs +8 -4
  14. package/dist/formats/d2/index.d.mts +1 -1
  15. package/dist/formats/d2/index.mjs +1 -1
  16. package/dist/formats/d3/index.d.mts +4 -4
  17. package/dist/formats/d3/index.mjs +8 -4
  18. package/dist/formats/dot/index.d.mts +1 -1
  19. package/dist/formats/dot/index.mjs +1 -1
  20. package/dist/formats/edge-list/index.d.mts +1 -1
  21. package/dist/formats/edge-list/index.mjs +1 -1
  22. package/dist/formats/elk/index.d.mts +1 -1
  23. package/dist/formats/elk/index.mjs +43 -11
  24. package/dist/formats/gexf/index.d.mts +1 -1
  25. package/dist/formats/gexf/index.mjs +22 -2
  26. package/dist/formats/gml/index.d.mts +4 -4
  27. package/dist/formats/gml/index.mjs +8 -4
  28. package/dist/formats/graphml/index.d.mts +1 -1
  29. package/dist/formats/graphml/index.mjs +24 -2
  30. package/dist/formats/jgf/index.d.mts +4 -4
  31. package/dist/formats/jgf/index.mjs +8 -4
  32. package/dist/formats/mermaid/index.d.mts +1 -1
  33. package/dist/formats/mermaid/index.mjs +1 -1
  34. package/dist/formats/tgf/index.d.mts +4 -4
  35. package/dist/formats/tgf/index.mjs +4 -4
  36. package/dist/formats/xyflow/index.d.mts +12 -6
  37. package/dist/formats/xyflow/index.mjs +11 -6
  38. package/dist/{index-D51lJnt2.d.mts → index-BlbSWUvH.d.mts} +1 -1
  39. package/dist/{index-DWmo1mIp.d.mts → index-CNvqxPLJ.d.mts} +82 -14
  40. package/dist/index.d.mts +6 -6
  41. package/dist/index.mjs +152 -17
  42. package/dist/layout/cytoscape.d.mts +66 -0
  43. package/dist/layout/cytoscape.mjs +114 -0
  44. package/dist/layout/d3-force.d.mts +52 -0
  45. package/dist/layout/d3-force.mjs +127 -0
  46. package/dist/layout/d3-hierarchy.d.mts +39 -0
  47. package/dist/layout/d3-hierarchy.mjs +135 -0
  48. package/dist/layout/dagre.d.mts +32 -0
  49. package/dist/layout/dagre.mjs +99 -0
  50. package/dist/layout/elk.d.mts +47 -0
  51. package/dist/layout/elk.mjs +73 -0
  52. package/dist/layout/forceatlas2.d.mts +48 -0
  53. package/dist/layout/forceatlas2.mjs +100 -0
  54. package/dist/layout/graphviz.d.mts +50 -0
  55. package/dist/layout/graphviz.mjs +179 -0
  56. package/dist/layout/index.d.mts +185 -0
  57. package/dist/layout/index.mjs +181 -0
  58. package/dist/layout/webcola.d.mts +40 -0
  59. package/dist/layout/webcola.mjs +104 -0
  60. package/dist/{queries-BfXeTXRf.d.mts → queries-B6quF529.d.mts} +1 -1
  61. package/dist/{queries-KirMDR7e.mjs → queries-BMM0XAv_.mjs} +23 -17
  62. package/dist/queries.d.mts +1 -1
  63. package/dist/queries.mjs +1 -1
  64. package/dist/schemas.d.mts +19 -1
  65. package/dist/schemas.mjs +10 -1
  66. package/dist/{types-DNYdIU21.d.mts → types-BAEQTwK_.d.mts} +46 -3
  67. package/package.json +47 -5
  68. package/schemas/edge.schema.json +27 -0
  69. package/schemas/graph.schema.json +27 -0
  70. /package/dist/{adjacency-list-GeL1Cu-L.mjs → adjacency-list-DQ32Mmhx.mjs} +0 -0
  71. /package/dist/{edge-list-BcZ0h6zz.mjs → edge-list-CA9UTvn2.mjs} +0 -0
  72. /package/dist/{mode-D8OnHFBk.mjs → mode-gu_mhKKs.mjs} +0 -0
  73. /package/dist/{validate-TtH-x3JV.mjs → validate-BsfSOv0S.mjs} +0 -0
package/dist/index.mjs CHANGED
@@ -1,9 +1,98 @@
1
- import { C as getSinks, D as isLeaf, E as isCompound, N as invalidateIndex, S as getSiblings, T as getSuccessors, _ as getPorts, a as getDescendants, b as getRelativeDistanceMap, c as getEdgesOf, d as getLCA, f as getNeighbors, g as getPort, h as getParent, i as getDepth, l as getInDegree, m as getOutEdges, n as getChildren, o as getEdgesBetween, p as getOutDegree, r as getDegree, s as getEdgesByPort, t as getAncestors, u as getInEdges, v as getPredecessors, w as getSources, x as getRoots, y as getRelativeDistance } from "./queries-KirMDR7e.mjs";
2
- import { $ as joinPaths, A as dfs, B as toEdgeConfig, C as genPostorders, D as getPreorder, E as getPostorders, F as isConnected, G as getAStarPath, H as genCycles, I as isTree, J as getShortestPath, K as getAllPairsShortestPaths, L as flatten, M as getTopologicalSort, N as hasPath, O as getPreorders, P as isAcyclic, Q as getStronglyConnectedComponents, R as getSubgraph, S as getMinimumSpanningTree, T as getPostorder, U as genShortestPaths, V as toNodeConfig, W as genSimplePaths, X as getSimplePath, Y as getShortestPaths, Z as getSimplePaths, _ as getEigenvectorCentrality, _t as updateEdge, a as isIsomorphic, at as createGraphEdge, b as getOutDegreeCentrality, c as getBridges, ct as createGraphPort, d as getGreedyModularityCommunities, dt as deleteEntities, et as GraphInstance, f as getLabelPropagationCommunities, ft as deleteNode, g as getDegreeCentrality, gt as hasNode, h as getClosenessCentrality, ht as hasEdge, i as getLouvainCommunities, it as createGraph, j as getConnectedComponents, k as bfs, l as genGirvanNewmanCommunities, lt as createVisualGraph, m as getBetweennessCentrality, mt as getNode, n as getDominatorTree, nt as addEntities, o as getArticulationPoints, ot as createGraphFromTransition, p as getModularity, pt as getEdge, q as getCycles, r as getMaxFlow, rt as addNode, s as getBiconnectedComponents, st as createGraphNode, t as getTransitiveReduction, tt as addEdge, u as getGirvanNewmanCommunities, ut as deleteEdge, v as getHITS, vt as updateEntities, w as genPreorders, x as getPageRank, y as getInDegreeCentrality, yt as updateNode, z as reverseGraph } from "./algorithms-DF1pSQGv.mjs";
3
- import { n as isEdgeDirected, t as getEdgeMode } from "./mode-D8OnHFBk.mjs";
4
- import { t as getGraphIssues } from "./validate-TtH-x3JV.mjs";
5
- import { n as createFormatConverter } from "./converter-DyCJJfTe.mjs";
1
+ import { C as getSinks, D as isLeaf, E as isCompound, N as invalidateIndex, S as getSiblings, T as getSuccessors, _ as getPorts, a as getDescendants, b as getRelativeDistanceMap, c as getEdgesOf, d as getLCA, f as getNeighbors, g as getPort, h as getParent, i as getDepth, l as getInDegree, m as getOutEdges, n as getChildren, o as getEdgesBetween, p as getOutDegree, r as getDegree, s as getEdgesByPort, t as getAncestors, u as getInEdges, v as getPredecessors, w as getSources, x as getRoots, y as getRelativeDistance } from "./queries-BMM0XAv_.mjs";
2
+ import { S as updateNode, _ as getNode, a as addEntities, b as updateEdge, c as createGraphEdge, d as createGraphPort, f as createVisualGraph, g as getEdge, h as deleteNode, i as addEdge, l as createGraphFromTransition, m as deleteEntities, n as toNodeConfig, o as addNode, p as deleteEdge, r as GraphInstance, s as createGraph, t as toEdgeConfig, u as createGraphNode, v as hasEdge, x as updateEntities, y as hasNode } from "./config-Dt5u1gSf.mjs";
3
+ import { n as isEdgeDirected, t as getEdgeMode } from "./mode-gu_mhKKs.mjs";
4
+ import { t as getGraphIssues } from "./validate-BsfSOv0S.mjs";
5
+ import { $ as getAStarPath, A as genPreorders, B as getTopologicalSort, C as getHITS, D as getPageRank, E as getOutDegreeCentrality, F as bfs, G as flatten, H as isAcyclic, I as dfs, J as getSubgraph, K as getFlattenedGraph, L as genBFS, M as getPostorders, N as getPreorder, O as getMinimumSpanningTree, P as getPreorders, Q as genSimplePaths, R as genDFS, S as getEigenvectorCentrality, T as getKatzCentrality, U as isConnected, V as hasPath, W as isTree, X as genCycles, Y as reverseGraph, Z as genShortestPaths, _ as getCoreNumbers, a as getLouvainCommunities, at as getSimplePath, b as getClosenessCentrality, c as getBiconnectedComponents, ct as joinPaths, d as getGirvanNewmanCommunities, et as getAllPairsShortestPaths, f as getGreedyModularityCommunities, g as isBipartite, h as getMaximumBipartiteMatching, i as getMinCut, it as getShortestPaths, j as getPostorder, k as genPostorders, l as getBridges, lt as mulberry32$1, m as getModularity, n as getDominatorTree, nt as getJoinedPath, o as isIsomorphic, ot as getSimplePaths, p as getLabelPropagationCommunities, q as getReversedGraph, r as getMaxFlow, rt as getShortestPath, s as getArticulationPoints, st as getStronglyConnectedComponents, t as getTransitiveReduction, tt as getCycles, u as genGirvanNewmanCommunities, v as getKCore, w as getInDegreeCentrality, x as getDegreeCentrality, y as getBetweennessCentrality, z as getConnectedComponents } from "./algorithms-DBpH74hR.mjs";
6
+ import { n as createFormatConverter } from "./converter-DB6Rg6Vd.mjs";
6
7
 
8
+ //#region src/generators.ts
9
+ function assertNonNegativeInteger(caller, name, value) {
10
+ if (!Number.isInteger(value) || value < 0) throw new Error(`${caller}: ${name} ${value} is invalid — pass a non-negative integer`);
11
+ }
12
+ /**
13
+ * Create the complete graph K_n: every pair of distinct nodes connected by
14
+ * one undirected edge. Nodes are `n0..n{n-1}` (`options.idPrefix` overrides
15
+ * the `n` prefix); edges are `e0..`.
16
+ */
17
+ function createCompleteGraph(n, options) {
18
+ assertNonNegativeInteger("createCompleteGraph", "node count", n);
19
+ const prefix = options?.idPrefix ?? "n";
20
+ const nodes = [];
21
+ for (let i = 0; i < n; i++) nodes.push({ id: `${prefix}${i}` });
22
+ const edges = [];
23
+ let edgeId = 0;
24
+ for (let i = 0; i < n; i++) for (let j = i + 1; j < n; j++) edges.push({
25
+ id: `e${edgeId++}`,
26
+ sourceId: `${prefix}${i}`,
27
+ targetId: `${prefix}${j}`
28
+ });
29
+ return createGraph({
30
+ mode: "undirected",
31
+ nodes,
32
+ edges
33
+ });
34
+ }
35
+ /**
36
+ * Create a `rows × cols` grid graph: node `(r, c)` is connected to its right
37
+ * and down neighbors by undirected edges. Nodes are `n{r}_{c}`
38
+ * (`options.idPrefix` overrides the `n` prefix); edges are `e0..`.
39
+ */
40
+ function createGridGraph(rows, cols, options) {
41
+ assertNonNegativeInteger("createGridGraph", "row count", rows);
42
+ assertNonNegativeInteger("createGridGraph", "column count", cols);
43
+ const prefix = options?.idPrefix ?? "n";
44
+ const nodeId = (r, c) => `${prefix}${r}_${c}`;
45
+ const nodes = [];
46
+ const edges = [];
47
+ let edgeId = 0;
48
+ for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) {
49
+ nodes.push({ id: nodeId(r, c) });
50
+ if (c + 1 < cols) edges.push({
51
+ id: `e${edgeId++}`,
52
+ sourceId: nodeId(r, c),
53
+ targetId: nodeId(r, c + 1)
54
+ });
55
+ if (r + 1 < rows) edges.push({
56
+ id: `e${edgeId++}`,
57
+ sourceId: nodeId(r, c),
58
+ targetId: nodeId(r + 1, c)
59
+ });
60
+ }
61
+ return createGraph({
62
+ mode: "undirected",
63
+ nodes,
64
+ edges
65
+ });
66
+ }
67
+ /**
68
+ * Create an Erdős–Rényi G(n, p) random graph: each of the n·(n-1)/2 node
69
+ * pairs gets an undirected edge with probability `probability`. With
70
+ * `options.seed` the result is deterministic per seed (mulberry32);
71
+ * otherwise `Math.random` is used. Nodes are `n0..n{n-1}`
72
+ * (`options.idPrefix` overrides the `n` prefix); edges are `e0..`.
73
+ */
74
+ function createRandomGraph(n, probability, options) {
75
+ assertNonNegativeInteger("createRandomGraph", "node count", n);
76
+ if (!(probability >= 0 && probability <= 1)) throw new Error(`createRandomGraph: probability ${probability} is invalid — pass a number between 0 and 1`);
77
+ const prefix = options?.idPrefix ?? "n";
78
+ const rng = options?.seed !== void 0 ? mulberry32$1(options.seed) : Math.random;
79
+ const nodes = [];
80
+ for (let i = 0; i < n; i++) nodes.push({ id: `${prefix}${i}` });
81
+ const edges = [];
82
+ let edgeId = 0;
83
+ for (let i = 0; i < n; i++) for (let j = i + 1; j < n; j++) if (rng() < probability) edges.push({
84
+ id: `e${edgeId++}`,
85
+ sourceId: `${prefix}${i}`,
86
+ targetId: `${prefix}${j}`
87
+ });
88
+ return createGraph({
89
+ mode: "undirected",
90
+ nodes,
91
+ edges
92
+ });
93
+ }
94
+
95
+ //#endregion
7
96
  //#region src/equivalence.ts
8
97
  /** Shallow-compare two values, returning true if they differ. */
9
98
  function differs$1(a, b) {
@@ -27,6 +116,8 @@ const LAYOUT_KEYS = {
27
116
  "y",
28
117
  "width",
29
118
  "height",
119
+ "points",
120
+ "routing",
30
121
  "style",
31
122
  "color"
32
123
  ]
@@ -127,6 +218,8 @@ const EDGE_COMPARE_KEYS = [
127
218
  "mode",
128
219
  "sourcePort",
129
220
  "targetPort",
221
+ "points",
222
+ "routing",
130
223
  "x",
131
224
  "y",
132
225
  "width",
@@ -232,18 +325,18 @@ function isEmptyDiff(diff) {
232
325
  *
233
326
  * @example
234
327
  * ```ts
235
- * import { createGraph, getDiff, invertDiff } from '@statelyai/graph';
328
+ * import { createGraph, getDiff, getInvertedDiff } from '@statelyai/graph';
236
329
  *
237
330
  * const a = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
238
331
  * const b = createGraph({ nodes: [{ id: 'n2' }], edges: [] });
239
332
  *
240
333
  * const diff = getDiff(a, b);
241
- * const inv = invertDiff(diff);
334
+ * const inv = getInvertedDiff(diff);
242
335
  * // inv.nodes.added contains n1 (was removed)
243
336
  * // inv.nodes.removed contains n2 (was added)
244
337
  * ```
245
338
  */
246
- function invertDiff(diff) {
339
+ function getInvertedDiff(diff) {
247
340
  return {
248
341
  nodes: {
249
342
  added: diff.nodes.removed.map((c) => structuredClone(c)),
@@ -266,6 +359,12 @@ function invertDiff(diff) {
266
359
  };
267
360
  }
268
361
  /**
362
+ * @deprecated Use {@link getInvertedDiff}.
363
+ */
364
+ function invertDiff(diff) {
365
+ return getInvertedDiff(diff);
366
+ }
367
+ /**
269
368
  * Compute an ordered patch list from graph `a` to graph `b`.
270
369
  * Order (see {@link toPatches}): add nodes → update edges → delete edges →
271
370
  * delete nodes → add edges → update nodes.
@@ -290,17 +389,17 @@ function getPatches(a, b) {
290
389
  *
291
390
  * @example
292
391
  * ```ts
293
- * import { createGraph, getPatches, applyPatches } from '@statelyai/graph';
392
+ * import { createGraph, getPatches, updateGraphWithPatches } from '@statelyai/graph';
294
393
  *
295
394
  * const a = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
296
395
  * const b = createGraph({ nodes: [{ id: 'n1' }, { id: 'n2' }], edges: [] });
297
396
  *
298
397
  * const patches = getPatches(a, b);
299
- * applyPatches(a, patches);
398
+ * updateGraphWithPatches(a, patches);
300
399
  * // a now contains both n1 and n2
301
400
  * ```
302
401
  */
303
- function applyPatches(graph, patches) {
402
+ function updateGraphWithPatches(graph, patches) {
304
403
  for (const patch of patches) switch (patch.op) {
305
404
  case "addNode":
306
405
  addNode(graph, patch.node);
@@ -323,6 +422,12 @@ function applyPatches(graph, patches) {
323
422
  }
324
423
  }
325
424
  /**
425
+ * @deprecated Use {@link updateGraphWithPatches}.
426
+ */
427
+ function applyPatches(graph, patches) {
428
+ updateGraphWithPatches(graph, patches);
429
+ }
430
+ /**
326
431
  * Flatten a structured diff into an ordered patch list.
327
432
  * Order: add nodes → update edges → delete edges → delete nodes → add edges → update nodes.
328
433
  * This avoids cascading deletes removing edges that are being updated,
@@ -679,7 +784,7 @@ function* genPredefinedWalk(graph, edgeIds, options) {
679
784
  /**
680
785
  * Yield at most `n` steps from the source generator.
681
786
  */
682
- function* takeSteps(gen, n) {
787
+ function* genWalkSteps(gen, n) {
683
788
  let count = 0;
684
789
  for (const step of gen) {
685
790
  yield step;
@@ -687,27 +792,45 @@ function* takeSteps(gen, n) {
687
792
  }
688
793
  }
689
794
  /**
795
+ * @deprecated Use {@link genWalkSteps}.
796
+ */
797
+ function* takeSteps(gen, n) {
798
+ yield* genWalkSteps(gen, n);
799
+ }
800
+ /**
690
801
  * Yield steps until a specific node is reached.
691
802
  */
692
- function* takeUntilNode(gen, nodeId) {
803
+ function* genWalkUntilNode(gen, nodeId) {
693
804
  for (const step of gen) {
694
805
  yield step;
695
806
  if (step.node.id === nodeId) return;
696
807
  }
697
808
  }
698
809
  /**
810
+ * @deprecated Use {@link genWalkUntilNode}.
811
+ */
812
+ function* takeUntilNode(gen, nodeId) {
813
+ yield* genWalkUntilNode(gen, nodeId);
814
+ }
815
+ /**
699
816
  * Yield steps until a specific edge is traversed.
700
817
  */
701
- function* takeUntilEdge(gen, edgeId) {
818
+ function* genWalkUntilEdge(gen, edgeId) {
702
819
  for (const step of gen) {
703
820
  yield step;
704
821
  if (step.edge.id === edgeId) return;
705
822
  }
706
823
  }
707
824
  /**
825
+ * @deprecated Use {@link genWalkUntilEdge}.
826
+ */
827
+ function* takeUntilEdge(gen, edgeId) {
828
+ yield* genWalkUntilEdge(gen, edgeId);
829
+ }
830
+ /**
708
831
  * Yield steps until node coverage reaches the target (0–1).
709
832
  */
710
- function* takeUntilNodeCoverage(gen, graph, coverage, options) {
833
+ function* genWalkUntilNodeCoverage(gen, graph, coverage, options) {
711
834
  const totalNodes = graph.nodes.length;
712
835
  const target = Math.ceil(coverage * totalNodes);
713
836
  const startId = options?.from ?? graph.initialNodeId ?? graph.nodes[0]?.id;
@@ -720,9 +843,15 @@ function* takeUntilNodeCoverage(gen, graph, coverage, options) {
720
843
  }
721
844
  }
722
845
  /**
846
+ * @deprecated Use {@link genWalkUntilNodeCoverage}.
847
+ */
848
+ function* takeUntilNodeCoverage(gen, graph, coverage, options) {
849
+ yield* genWalkUntilNodeCoverage(gen, graph, coverage, options);
850
+ }
851
+ /**
723
852
  * Yield steps until edge coverage reaches the target (0–1).
724
853
  */
725
- function* takeUntilEdgeCoverage(gen, graph, coverage) {
854
+ function* genWalkUntilEdgeCoverage(gen, graph, coverage) {
726
855
  const totalEdges = graph.edges.length;
727
856
  const target = Math.ceil(coverage * totalEdges);
728
857
  const visited = /* @__PURE__ */ new Set();
@@ -734,6 +863,12 @@ function* takeUntilEdgeCoverage(gen, graph, coverage) {
734
863
  }
735
864
  }
736
865
  /**
866
+ * @deprecated Use {@link genWalkUntilEdgeCoverage}.
867
+ */
868
+ function* takeUntilEdgeCoverage(gen, graph, coverage) {
869
+ yield* genWalkUntilEdgeCoverage(gen, graph, coverage);
870
+ }
871
+ /**
737
872
  * Compute coverage statistics for a completed walk.
738
873
  */
739
874
  function getCoverage(graph, steps, options) {
@@ -754,4 +889,4 @@ function getCoverage(graph, steps, options) {
754
889
  }
755
890
 
756
891
  //#endregion
757
- export { GraphInstance, LAYOUT_KEYS, addEdge, addEntities, addNode, applyPatches, areEntitiesEqual, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createGraphPort, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, genCycles, genGirvanNewmanCommunities, genPostorders, genPredefinedWalk, genPreorders, genQuickRandomWalk, genRandomWalk, genShortestPaths, genSimplePaths, genWeightedRandomWalk, getAStarPath, getAllPairsShortestPaths, getAncestors, getArticulationPoints, getBetweennessCentrality, getBiconnectedComponents, getBridges, getChildren, getClosenessCentrality, getConnectedComponents, getCoverage, getCycles, getDegree, getDegreeCentrality, getDepth, getDescendants, getDiff, getDominatorTree, getEdge, getEdgeMode, getEdgesBetween, getEdgesByPort, getEdgesOf, getEigenvectorCentrality, getGirvanNewmanCommunities, getGraphIssues, getGreedyModularityCommunities, getHITS, getInDegree, getInDegreeCentrality, getInEdges, getLCA, getLabelPropagationCommunities, getLouvainCommunities, getMaxFlow, getMinimumSpanningTree, getModularity, getNeighbors, getNode, getOutDegree, getOutDegreeCentrality, getOutEdges, getPageRank, getParent, getPatches, getPort, getPorts, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRelativeDistance, getRelativeDistanceMap, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSubgraph, getSuccessors, getTopologicalSort, getTransitiveReduction, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isCompound, isConnected, isEdgeDirected, isEmptyDiff, isIsomorphic, isLayoutEqual, isLeaf, isNonLayoutEqual, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateNode };
892
+ export { GraphInstance, LAYOUT_KEYS, addEdge, addEntities, addNode, applyPatches, areEntitiesEqual, bfs, createCompleteGraph, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createGraphPort, createGridGraph, createRandomGraph, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, genBFS, genCycles, genDFS, genGirvanNewmanCommunities, genPostorders, genPredefinedWalk, genPreorders, genQuickRandomWalk, genRandomWalk, genShortestPaths, genSimplePaths, genWalkSteps, genWalkUntilEdge, genWalkUntilEdgeCoverage, genWalkUntilNode, genWalkUntilNodeCoverage, genWeightedRandomWalk, getAStarPath, getAllPairsShortestPaths, getAncestors, getArticulationPoints, getBetweennessCentrality, getBiconnectedComponents, getBridges, getChildren, getClosenessCentrality, getConnectedComponents, getCoreNumbers, getCoverage, getCycles, getDegree, getDegreeCentrality, getDepth, getDescendants, getDiff, getDominatorTree, getEdge, getEdgeMode, getEdgesBetween, getEdgesByPort, getEdgesOf, getEigenvectorCentrality, getFlattenedGraph, getGirvanNewmanCommunities, getGraphIssues, getGreedyModularityCommunities, getHITS, getInDegree, getInDegreeCentrality, getInEdges, getInvertedDiff, getJoinedPath, getKCore, getKatzCentrality, getLCA, getLabelPropagationCommunities, getLouvainCommunities, getMaxFlow, getMaximumBipartiteMatching, getMinCut, getMinimumSpanningTree, getModularity, getNeighbors, getNode, getOutDegree, getOutDegreeCentrality, getOutEdges, getPageRank, getParent, getPatches, getPort, getPorts, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRelativeDistance, getRelativeDistanceMap, getReversedGraph, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSubgraph, getSuccessors, getTopologicalSort, getTransitiveReduction, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isBipartite, isCompound, isConnected, isEdgeDirected, isEmptyDiff, isIsomorphic, isLayoutEqual, isLeaf, isNonLayoutEqual, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateGraphWithPatches, updateNode };
@@ -0,0 +1,66 @@
1
+ import { I as VisualGraph, f as Graph } from "../types-BAEQTwK_.mjs";
2
+ import { LayoutOptions } from "./index.mjs";
3
+ import cytoscape from "cytoscape";
4
+
5
+ //#region src/layout/cytoscape.d.ts
6
+
7
+ /**
8
+ * Minimal interface an injected cytoscape factory must satisfy — the
9
+ * `cytoscape()` function itself. Inject your own when you've registered
10
+ * layout extensions via `cytoscape.use(...)` (cola, fcose, dagre, …).
11
+ */
12
+ type CytoscapeLike = (options: cytoscape.CytoscapeOptions) => cytoscape.Core;
13
+ interface CytoscapeLayoutOptions extends LayoutOptions {
14
+ /**
15
+ * Cytoscape layout name. Built-ins: `'grid'`, `'circle'`, `'concentric'`,
16
+ * `'breadthfirst'`, `'cose'` (default), `'random'`. Extension layouts work
17
+ * when registered on an injected {@link CytoscapeLayoutOptions.cy} factory.
18
+ */
19
+ name?: string;
20
+ /**
21
+ * Raw cytoscape layout options, merged into the layout call last (override
22
+ * everything, including `name`). Engine-specific tuning — spacing knobs,
23
+ * `boundingBox`, `roots`, iteration counts — goes here; the options vary
24
+ * too much between cytoscape layouts to map `LayoutOptions.spacing`
25
+ * generically. See https://js.cytoscape.org/#layouts
26
+ */
27
+ layoutOptions?: Record<string, unknown>;
28
+ /**
29
+ * Injected cytoscape factory — e.g. one with extensions registered via
30
+ * `cytoscape.use(...)`. Defaults to the imported `cytoscape`.
31
+ */
32
+ cy?: CytoscapeLike;
33
+ }
34
+ /**
35
+ * Lay out a graph with Cytoscape.js (`cytoscape`, an optional peer
36
+ * dependency) — one call unlocks its whole layout ecosystem. Pure: returns a
37
+ * new {@link VisualGraph} with node positions and sizes. Cytoscape layouts
38
+ * position nodes only; edges keep their fields but gain no route `points`.
39
+ * Compound graphs are supported via cytoscape `parent`; parent nodes get
40
+ * cytoscape's computed compound dimensions. All coordinates are absolute
41
+ * (cytoscape does not produce parent-relative positions).
42
+ *
43
+ * Runs headless with `styleEnabled: true` so resolved node sizes
44
+ * ({@link getNodeSize}) participate in overlap avoidance and spacing.
45
+ *
46
+ * Option mapping is deliberately minimal: `measure` resolves sizes,
47
+ * `isFixed` nodes with an existing `x`/`y` are locked in place (cytoscape
48
+ * layouts skip locked nodes), and everything else — including `direction`
49
+ * and `spacing` — is engine-specific and belongs in
50
+ * {@link CytoscapeLayoutOptions.layoutOptions}. `seed` is ignored: the
51
+ * discrete layouts (grid, circle, concentric, breadthfirst) are
52
+ * deterministic, and cose is not seedable.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { getCytoscapeLayout } from '@statelyai/graph/layout/cytoscape';
57
+ *
58
+ * const laidOut = await getCytoscapeLayout(graph, {
59
+ * name: 'breadthfirst',
60
+ * layoutOptions: { roots: ['start'] },
61
+ * });
62
+ * ```
63
+ */
64
+ declare function getCytoscapeLayout(graph: Graph | VisualGraph, options?: CytoscapeLayoutOptions): Promise<VisualGraph>;
65
+ //#endregion
66
+ export { CytoscapeLayoutOptions, CytoscapeLike, getCytoscapeLayout };
@@ -0,0 +1,114 @@
1
+ import { f as createVisualGraph, n as toNodeConfig, t as toEdgeConfig } from "../config-Dt5u1gSf.mjs";
2
+ import { getNodeSize } from "./index.mjs";
3
+ import cytoscape from "cytoscape";
4
+
5
+ //#region src/layout/cytoscape.ts
6
+ /**
7
+ * Lay out a graph with Cytoscape.js (`cytoscape`, an optional peer
8
+ * dependency) — one call unlocks its whole layout ecosystem. Pure: returns a
9
+ * new {@link VisualGraph} with node positions and sizes. Cytoscape layouts
10
+ * position nodes only; edges keep their fields but gain no route `points`.
11
+ * Compound graphs are supported via cytoscape `parent`; parent nodes get
12
+ * cytoscape's computed compound dimensions. All coordinates are absolute
13
+ * (cytoscape does not produce parent-relative positions).
14
+ *
15
+ * Runs headless with `styleEnabled: true` so resolved node sizes
16
+ * ({@link getNodeSize}) participate in overlap avoidance and spacing.
17
+ *
18
+ * Option mapping is deliberately minimal: `measure` resolves sizes,
19
+ * `isFixed` nodes with an existing `x`/`y` are locked in place (cytoscape
20
+ * layouts skip locked nodes), and everything else — including `direction`
21
+ * and `spacing` — is engine-specific and belongs in
22
+ * {@link CytoscapeLayoutOptions.layoutOptions}. `seed` is ignored: the
23
+ * discrete layouts (grid, circle, concentric, breadthfirst) are
24
+ * deterministic, and cose is not seedable.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { getCytoscapeLayout } from '@statelyai/graph/layout/cytoscape';
29
+ *
30
+ * const laidOut = await getCytoscapeLayout(graph, {
31
+ * name: 'breadthfirst',
32
+ * layoutOptions: { roots: ['start'] },
33
+ * });
34
+ * ```
35
+ */
36
+ async function getCytoscapeLayout(graph, options) {
37
+ const elements = [];
38
+ for (const node of graph.nodes) {
39
+ const size = getNodeSize(node, options);
40
+ const hasPosition = node.x !== void 0 && node.y !== void 0;
41
+ elements.push({
42
+ group: "nodes",
43
+ data: {
44
+ id: node.id,
45
+ ...node.parentId != null && { parent: node.parentId },
46
+ width: size.width,
47
+ height: size.height
48
+ },
49
+ ...hasPosition && { position: {
50
+ x: node.x + size.width / 2,
51
+ y: node.y + size.height / 2
52
+ } },
53
+ ...hasPosition && options?.isFixed?.(node) && { locked: true }
54
+ });
55
+ }
56
+ for (const edge of graph.edges) elements.push({
57
+ group: "edges",
58
+ data: {
59
+ id: edge.id,
60
+ source: edge.sourceId,
61
+ target: edge.targetId
62
+ }
63
+ });
64
+ const cy = (options?.cy ?? cytoscape)({
65
+ headless: true,
66
+ styleEnabled: true,
67
+ style: [{
68
+ selector: "node",
69
+ style: {
70
+ width: "data(width)",
71
+ height: "data(height)",
72
+ shape: "rectangle"
73
+ }
74
+ }],
75
+ elements
76
+ });
77
+ try {
78
+ const layout = cy.layout({
79
+ name: options?.name ?? "cose",
80
+ animate: false,
81
+ ...options?.layoutOptions
82
+ });
83
+ const stopped = layout.promiseOn("layoutstop");
84
+ layout.run();
85
+ await stopped;
86
+ return createVisualGraph({
87
+ id: graph.id,
88
+ mode: graph.mode,
89
+ initialNodeId: graph.initialNodeId ?? void 0,
90
+ direction: options?.direction ?? graph.direction,
91
+ data: graph.data,
92
+ ...graph.style !== void 0 && { style: graph.style },
93
+ nodes: graph.nodes.map((node) => {
94
+ const ele = cy.getElementById(node.id);
95
+ const width = ele.width();
96
+ const height = ele.height();
97
+ const position = ele.position();
98
+ return {
99
+ ...toNodeConfig(node),
100
+ width,
101
+ height,
102
+ x: position.x - width / 2,
103
+ y: position.y - height / 2
104
+ };
105
+ }),
106
+ edges: graph.edges.map((edge) => toEdgeConfig(edge))
107
+ });
108
+ } finally {
109
+ cy.destroy();
110
+ }
111
+ }
112
+
113
+ //#endregion
114
+ export { getCytoscapeLayout };
@@ -0,0 +1,52 @@
1
+ import { I as VisualGraph, f as Graph } from "../types-BAEQTwK_.mjs";
2
+ import { LayoutFrame, LayoutOptions } from "./index.mjs";
3
+
4
+ //#region src/layout/d3-force.d.ts
5
+ interface ForceLayoutOptions extends LayoutOptions {
6
+ /** Target distance between linked nodes. Default: 80. */
7
+ linkDistance?: number;
8
+ /** Many-body charge strength (negative repels). Default: -300. */
9
+ chargeStrength?: number;
10
+ /** Number of simulation ticks. Default: 300 (d3's natural cooling span). */
11
+ iterations?: number;
12
+ }
13
+ /**
14
+ * Iterative force-directed layout via d3-force (optional peer dependency).
15
+ * Each `next()` advances one simulation tick and yields a {@link LayoutFrame}
16
+ * (top-left node positions by id + remaining `alpha`); the caller owns pacing
17
+ * (e.g. one tick per animation frame via {@link applyLayoutFrame}) and
18
+ * cancellation (stop iterating). The generator's return value is the settled
19
+ * {@link VisualGraph}.
20
+ *
21
+ * - Deterministic: `options.seed` drives d3's `randomSource`; the same seed
22
+ * always produces the same layout.
23
+ * - `options.isFixed` pins nodes at their current position (d3 `fx`/`fy`).
24
+ * - Hierarchy is ignored (force layouts are flat); per-edge `mode` is
25
+ * irrelevant (forces are symmetric).
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { genForceLayout } from '@statelyai/graph/layout/d3-force';
30
+ * import { applyLayoutFrame } from '@statelyai/graph/layout';
31
+ *
32
+ * for (const frame of genForceLayout(graph, { seed: 42 })) {
33
+ * applyLayoutFrame(graph, frame);
34
+ * render(graph);
35
+ * }
36
+ * ```
37
+ */
38
+ declare function genForceLayout(graph: Graph | VisualGraph, options?: ForceLayoutOptions): Generator<LayoutFrame, VisualGraph>;
39
+ /**
40
+ * Run {@link genForceLayout} to completion and return the settled
41
+ * {@link VisualGraph}. Convenience for non-animated use.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { getForceLayout } from '@statelyai/graph/layout/d3-force';
46
+ *
47
+ * const laidOut = getForceLayout(graph, { seed: 42 });
48
+ * ```
49
+ */
50
+ declare function getForceLayout(graph: Graph | VisualGraph, options?: ForceLayoutOptions): VisualGraph;
51
+ //#endregion
52
+ export { ForceLayoutOptions, genForceLayout, getForceLayout };
@@ -0,0 +1,127 @@
1
+ import { f as createVisualGraph, n as toNodeConfig, t as toEdgeConfig } from "../config-Dt5u1gSf.mjs";
2
+ import { getNodeSize } from "./index.mjs";
3
+ import { forceCenter, forceLink, forceManyBody, forceSimulation } from "d3-force";
4
+
5
+ //#region src/layout/d3-force.ts
6
+ /** mulberry32 — same seeded PRNG the rest of the library uses. */
7
+ function mulberry32(seed) {
8
+ let s = seed | 0;
9
+ return () => {
10
+ s = s + 1831565813 | 0;
11
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
12
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
13
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
14
+ };
15
+ }
16
+ /**
17
+ * Iterative force-directed layout via d3-force (optional peer dependency).
18
+ * Each `next()` advances one simulation tick and yields a {@link LayoutFrame}
19
+ * (top-left node positions by id + remaining `alpha`); the caller owns pacing
20
+ * (e.g. one tick per animation frame via {@link applyLayoutFrame}) and
21
+ * cancellation (stop iterating). The generator's return value is the settled
22
+ * {@link VisualGraph}.
23
+ *
24
+ * - Deterministic: `options.seed` drives d3's `randomSource`; the same seed
25
+ * always produces the same layout.
26
+ * - `options.isFixed` pins nodes at their current position (d3 `fx`/`fy`).
27
+ * - Hierarchy is ignored (force layouts are flat); per-edge `mode` is
28
+ * irrelevant (forces are symmetric).
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { genForceLayout } from '@statelyai/graph/layout/d3-force';
33
+ * import { applyLayoutFrame } from '@statelyai/graph/layout';
34
+ *
35
+ * for (const frame of genForceLayout(graph, { seed: 42 })) {
36
+ * applyLayoutFrame(graph, frame);
37
+ * render(graph);
38
+ * }
39
+ * ```
40
+ */
41
+ function* genForceLayout(graph, options) {
42
+ const rng = mulberry32(options?.seed ?? 1);
43
+ const sizes = /* @__PURE__ */ new Map();
44
+ const scatterRadius = Math.max(80, 30 * Math.sqrt(graph.nodes.length));
45
+ const simNodes = graph.nodes.map((node) => {
46
+ const size = getNodeSize(node, options);
47
+ sizes.set(node.id, size);
48
+ const simNode = {
49
+ id: node.id,
50
+ ...size
51
+ };
52
+ if (node.x !== void 0 && node.y !== void 0) {
53
+ simNode.x = node.x + size.width / 2;
54
+ simNode.y = node.y + size.height / 2;
55
+ if (options?.isFixed?.(node)) {
56
+ simNode.fx = simNode.x;
57
+ simNode.fy = simNode.y;
58
+ }
59
+ } else {
60
+ const angle = rng() * 2 * Math.PI;
61
+ const radius = scatterRadius * Math.sqrt(rng());
62
+ simNode.x = Math.cos(angle) * radius;
63
+ simNode.y = Math.sin(angle) * radius;
64
+ }
65
+ return simNode;
66
+ });
67
+ const simLinks = graph.edges.filter((edge) => sizes.has(edge.sourceId) && sizes.has(edge.targetId) && edge.sourceId !== edge.targetId).map((edge) => ({
68
+ source: edge.sourceId,
69
+ target: edge.targetId
70
+ }));
71
+ const simulation = forceSimulation(simNodes).randomSource(mulberry32(options?.seed ?? 1)).force("link", forceLink(simLinks).id((d) => d.id).distance(options?.linkDistance ?? 80)).force("charge", forceManyBody().strength(options?.chargeStrength ?? -300)).force("center", forceCenter(0, 0)).stop();
72
+ const iterations = options?.iterations ?? 300;
73
+ for (let i = 0; i < iterations; i++) {
74
+ simulation.tick();
75
+ const positions = {};
76
+ for (const simNode of simNodes) positions[simNode.id] = {
77
+ x: (simNode.x ?? 0) - simNode.width / 2,
78
+ y: (simNode.y ?? 0) - simNode.height / 2
79
+ };
80
+ yield {
81
+ positions,
82
+ alpha: simulation.alpha()
83
+ };
84
+ if (simulation.alpha() < simulation.alphaMin()) break;
85
+ }
86
+ const byId = new Map(simNodes.map((simNode) => [simNode.id, simNode]));
87
+ return createVisualGraph({
88
+ id: graph.id,
89
+ mode: graph.mode,
90
+ initialNodeId: graph.initialNodeId ?? void 0,
91
+ direction: graph.direction,
92
+ data: graph.data,
93
+ ...graph.style !== void 0 && { style: graph.style },
94
+ nodes: graph.nodes.map((node) => {
95
+ const size = sizes.get(node.id);
96
+ const simNode = byId.get(node.id);
97
+ return {
98
+ ...toNodeConfig(node),
99
+ ...size,
100
+ x: (simNode.x ?? 0) - size.width / 2,
101
+ y: (simNode.y ?? 0) - size.height / 2
102
+ };
103
+ }),
104
+ edges: graph.edges.map((edge) => toEdgeConfig(edge))
105
+ });
106
+ }
107
+ /**
108
+ * Run {@link genForceLayout} to completion and return the settled
109
+ * {@link VisualGraph}. Convenience for non-animated use.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * import { getForceLayout } from '@statelyai/graph/layout/d3-force';
114
+ *
115
+ * const laidOut = getForceLayout(graph, { seed: 42 });
116
+ * ```
117
+ */
118
+ function getForceLayout(graph, options) {
119
+ const generator = genForceLayout(graph, options);
120
+ for (;;) {
121
+ const step = generator.next();
122
+ if (step.done) return step.value;
123
+ }
124
+ }
125
+
126
+ //#endregion
127
+ export { genForceLayout, getForceLayout };