@statelyai/graph 0.4.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.
Files changed (46) hide show
  1. package/README.md +63 -144
  2. package/dist/{algorithms-CnTmuX9t.mjs → algorithms-oVD9PYil.mjs} +219 -31
  3. package/dist/algorithms.d.mts +91 -3
  4. package/dist/algorithms.mjs +2 -2
  5. package/dist/{converter-C5DlzzHs.mjs → converter-B5CUD0r9.mjs} +2 -2
  6. package/dist/formats/adjacency-list/index.d.mts +1 -1
  7. package/dist/formats/adjacency-list/index.mjs +1 -1
  8. package/dist/formats/converter/index.d.mts +2 -2
  9. package/dist/formats/converter/index.mjs +1 -1
  10. package/dist/formats/cytoscape/index.d.mts +1 -1
  11. package/dist/formats/cytoscape/index.mjs +1 -1
  12. package/dist/formats/d3/index.d.mts +1 -1
  13. package/dist/formats/d3/index.mjs +1 -1
  14. package/dist/formats/dot/index.d.mts +1 -1
  15. package/dist/formats/dot/index.mjs +1 -1
  16. package/dist/formats/edge-list/index.d.mts +1 -1
  17. package/dist/formats/edge-list/index.mjs +1 -1
  18. package/dist/formats/elk/index.d.mts +61 -0
  19. package/dist/formats/elk/index.mjs +176 -0
  20. package/dist/formats/gexf/index.d.mts +1 -1
  21. package/dist/formats/gexf/index.mjs +1 -1
  22. package/dist/formats/gml/index.d.mts +1 -1
  23. package/dist/formats/gml/index.mjs +1 -1
  24. package/dist/formats/graphml/index.d.mts +1 -1
  25. package/dist/formats/graphml/index.mjs +148 -56
  26. package/dist/formats/jgf/index.d.mts +1 -1
  27. package/dist/formats/jgf/index.mjs +1 -1
  28. package/dist/formats/mermaid/index.d.mts +51 -33
  29. package/dist/formats/mermaid/index.mjs +315 -31
  30. package/dist/formats/tgf/index.d.mts +1 -1
  31. package/dist/formats/tgf/index.mjs +1 -1
  32. package/dist/formats/xyflow/index.d.mts +1 -1
  33. package/dist/index.d.mts +100 -3
  34. package/dist/index.mjs +366 -10
  35. package/dist/queries.d.mts +1 -1
  36. package/dist/queries.mjs +1 -1
  37. package/dist/schemas.d.mts +39 -4
  38. package/dist/schemas.mjs +27 -5
  39. package/dist/{types-Bq_fmLwW.d.mts → types-DF-HNw50.d.mts} +65 -13
  40. package/package.json +7 -1
  41. package/schemas/edge.schema.json +32 -1
  42. package/schemas/graph.schema.json +114 -4
  43. package/schemas/node.schema.json +45 -2
  44. /package/dist/{adjacency-list-Bv4tfiM3.mjs → adjacency-list-fldj-QAL.mjs} +0 -0
  45. /package/dist/{edge-list-R1SUbHwe.mjs → edge-list-Br05wXMg.mjs} +0 -0
  46. /package/dist/{indexing-DitHphT7.mjs → indexing-DyfgLuzw.mjs} +0 -0
package/README.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # @statelyai/graph
2
2
 
3
- A TypeScript graph library built on plain JSON objects. Supports directed/undirected graphs, hierarchical nodes, graph algorithms, visual properties, and serialization to DOT, GraphML, and more.
3
+ A TypeScript graph library built on plain JSON objects. Supports directed/undirected graphs, hierarchical nodes, graph algorithms, visual properties, and serialization to DOT, GraphML, Mermaid, and more.
4
4
 
5
5
  Made from our experience at [stately.ai](https://stately.ai), where we build visual tools for complex systems.
6
6
 
7
-
8
7
  ## Install
9
8
 
10
9
  ```bash
@@ -13,6 +12,8 @@ npm install @statelyai/graph
13
12
 
14
13
  ## Quick Start
15
14
 
15
+ Graphs are plain JSON-serializable objects. All operations are standalone functions — no classes, no DOM, no rendering engine.
16
+
16
17
  ```ts
17
18
  import { createGraph, addNode, addEdge, getShortestPath } from '@statelyai/graph';
18
19
 
@@ -36,12 +37,27 @@ addEdge(graph, { id: 'e3', sourceId: 'a', targetId: 'd' });
36
37
  const path = getShortestPath(graph, { from: 'a', to: 'c' });
37
38
  ```
38
39
 
39
- ### Hierarchical Graphs
40
+ ## Graph Manipulation
40
41
 
41
- Nodes support parent-child relationships. Use `flatten()` to decompose compound nodes into a flat graph.
42
+ Look up, add, delete, and update nodes and edges. Query neighbors, predecessors, successors, degree, and more.
42
43
 
43
44
  ```ts
44
- import { createGraph, flatten } from '@statelyai/graph';
45
+ import { getNode, deleteNode, getNeighbors, getSources } from '@statelyai/graph';
46
+
47
+ const node = getNode(graph, 'a'); // lookup by id
48
+ deleteNode(graph, 'd'); // removes node + connected edges
49
+ const neighbors = getNeighbors(graph, 'a'); // adjacent nodes
50
+ const roots = getSources(graph); // nodes with no incoming edges
51
+ ```
52
+
53
+ Batch operations (`addEntities`, `deleteEntities`, `updateEntities`) let you apply multiple changes at once.
54
+
55
+ ## Hierarchy
56
+
57
+ Nodes support parent-child relationships for compound/nested graphs. Query children, ancestors, descendants, depth, and least common ancestor. Use `flatten()` to decompose into a flat leaf-node graph.
58
+
59
+ ```ts
60
+ import { createGraph, getChildren, getLCA, flatten } from '@statelyai/graph';
45
61
 
46
62
  const graph = createGraph({
47
63
  nodes: [
@@ -58,10 +74,33 @@ const graph = createGraph({
58
74
  ],
59
75
  });
60
76
 
61
- const flat = flatten(graph); // only leaf nodes, edges resolved
77
+ const children = getChildren(graph, 'b'); // [b1, b2]
78
+ const flat = flatten(graph); // only leaf nodes, edges resolved
62
79
  ```
63
80
 
64
- ### Visual Graphs
81
+ ## Algorithms
82
+
83
+ Includes traversal (BFS, DFS), pathfinding (shortest path, simple paths, all-pairs shortest paths), cycle detection, connected/strongly-connected components, topological sort, minimum spanning tree, and more. Many algorithms have lazy generator variants (`gen*`) for early exit.
84
+
85
+ ```ts
86
+ import {
87
+ bfs, dfs, hasPath, isAcyclic,
88
+ getShortestPath, getCycles, getTopologicalSort,
89
+ getConnectedComponents, getMinimumSpanningTree,
90
+ } from '@statelyai/graph';
91
+
92
+ for (const node of bfs(graph, 'a')) { /* breadth-first */ }
93
+ for (const node of dfs(graph, 'a')) { /* depth-first */ }
94
+
95
+ hasPath(graph, 'a', 'c'); // reachability
96
+ isAcyclic(graph); // cycle check
97
+ getShortestPath(graph, { from: 'a', to: 'c' }); // single shortest path
98
+ getTopologicalSort(graph); // topological order (or null)
99
+ getConnectedComponents(graph); // connected components
100
+ getMinimumSpanningTree(graph, { weight: e => e.data?.weight ?? 1 }); // MST
101
+ ```
102
+
103
+ ## Visual Graphs
65
104
 
66
105
  `createVisualGraph()` guarantees `x`, `y`, `width`, `height` on all nodes and edges (default `0`).
67
106
 
@@ -72,170 +111,50 @@ const diagram = createVisualGraph({
72
111
  direction: 'right',
73
112
  nodes: [
74
113
  { id: 'a', x: 0, y: 0, width: 120, height: 60, shape: 'rectangle' },
75
- { id: 'b', x: 200, y: 0, width: 120, height: 60, shape: 'ellipse', color: '#3b82f6' },
114
+ { id: 'b', x: 200, y: 0, width: 120, height: 60, shape: 'ellipse' },
76
115
  ],
77
116
  edges: [{ id: 'e1', sourceId: 'a', targetId: 'b', width: 100, height: 100 }],
78
117
  });
79
118
  ```
80
119
 
81
- ### Format Conversion
120
+ ## Format Conversion
121
+
122
+ Import and export graphs to many formats. Converters are available as subpath imports.
82
123
 
83
124
  ```ts
84
- import { toCytoscapeJSON } from '@statelyai/graph/cytoscape';
85
- import { fromJGF } from '@statelyai/graph/jgf';
86
- import { toD3Graph } from '@statelyai/graph/d3';
87
125
  import { toDOT } from '@statelyai/graph/dot';
88
- import { toGraphML } from '@statelyai/graph/graphml';
89
126
  import { fromGEXF } from '@statelyai/graph/gexf';
127
+ import { toCytoscapeJSON } from '@statelyai/graph/cytoscape';
128
+ import { toD3Graph } from '@statelyai/graph/d3';
90
129
 
91
- // Export to web visualization libraries
92
- const cytoData = toCytoscapeJSON(graph); // Cytoscape.js JSON (compound graphs preserved)
93
- const d3Data = toD3Graph(graph); // D3.js { nodes, links }
94
-
95
- // Export to text formats
96
- const dot = toDOT(graph); // Graphviz DOT
97
- const xml = toGraphML(graph); // GraphML XML
98
-
99
- // Import from any format
100
- const g1 = fromJGF(jsonGraphData); // JSON Graph Format
101
- const g2 = fromGEXF(gexfXmlString); // GEXF (Gephi)
130
+ const dot = toDOT(graph); // Graphviz DOT
131
+ const cytoData = toCytoscapeJSON(graph); // Cytoscape.js JSON
132
+ const d3Data = toD3Graph(graph); // D3.js { nodes, links }
133
+ const imported = fromGEXF(gexfXmlString); // GEXF (Gephi)
102
134
  ```
103
135
 
104
- Each bidirectional format also has a converter object for a unified interface:
136
+ **Supported formats:** Cytoscape.js JSON, D3.js JSON, JSON Graph Format, GEXF, GraphML, GML, TGF, DOT, Mermaid (flowchart, state, sequence, class, ER, mindmap, block), adjacency list, and edge list.
137
+
138
+ Each bidirectional format also has a converter object:
105
139
 
106
140
  ```ts
107
- import { createFormatConverter } from '@statelyai/graph';
108
141
  import { cytoscapeConverter } from '@statelyai/graph/cytoscape';
109
142
 
110
- // Use a built-in converter
111
143
  const cyto = cytoscapeConverter.to(graph);
112
144
  const back = cytoscapeConverter.from(cyto);
113
-
114
- // Create your own
115
- const myConverter = createFormatConverter(myToFn, myFromFn);
116
145
  ```
117
146
 
118
- ## Why this library?
147
+ Some formats have optional peer dependencies: `fast-xml-parser` (GEXF, GraphML) and `dotparser` (DOT). All other formats are dependency-free.
119
148
 
120
- Graph file formats (GEXF, GraphML) define how to _store_ graphs. Visualization libraries (Cytoscape.js, D3) define how to _render_ them. Neither gives you a good way to _work with_ them in between.
149
+ ## Why this library?
121
150
 
122
- This library is the computational layer: plain JSON objects in, algorithms and mutations, plain JSON objects out. No classes, no DOM, no rendering engine; just data and functions.
151
+ Graph file formats define how to _store_ graphs. Visualization libraries define how to _render_ them. This library is the computational layer in between: plain JSON objects in, algorithms and mutations, plain JSON objects out.
123
152
 
124
153
  ```
125
154
  GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSON() → render
126
155
  ```
127
156
 
128
- Your `Graph` is a plain object that survives `JSON.stringify`, `structuredClone`, `postMessage`, and `localStorage` without adapters. Format converters are the I/O ports: read from any supported format, do your work, export to whatever your renderer or database expects.
129
-
130
- ## API
131
-
132
- ### Graph Creation
133
-
134
- | Function | Description |
135
- |----------|-------------|
136
- | `createGraph(config?)` | Create a graph |
137
- | `createVisualGraph(config?)` | Create a graph with required position/size on nodes and edges |
138
-
139
- ### Lookups & Mutations
140
-
141
- | Function | Description |
142
- |----------|-------------|
143
- | `getNode(graph, id)` | Node by id, or `undefined` |
144
- | `getEdge(graph, id)` | Edge by id, or `undefined` |
145
- | `hasNode(graph, id)` | Node exists? |
146
- | `hasEdge(graph, id)` | Edge exists? |
147
- | `addNode(graph, config)` | Add a node |
148
- | `addEdge(graph, config)` | Add an edge |
149
- | `deleteNode(graph, id, opts?)` | Delete node + connected edges |
150
- | `deleteEdge(graph, id)` | Delete an edge |
151
- | `updateNode(graph, id, patch)` | Update node fields |
152
- | `updateEdge(graph, id, patch)` | Update edge fields |
153
- | `addEntities(graph, entities)` | Batch add |
154
- | `deleteEntities(graph, ids)` | Batch delete |
155
- | `updateEntities(graph, updates)` | Batch update |
156
-
157
- ### Queries
158
-
159
- | Function | Description |
160
- |----------|-------------|
161
- | `getNeighbors(graph, nodeId)` | Adjacent nodes |
162
- | `getSuccessors(graph, nodeId)` | Outgoing neighbors |
163
- | `getPredecessors(graph, nodeId)` | Incoming neighbors |
164
- | `getDegree(graph, nodeId)` | Connected edge count |
165
- | `getInDegree` / `getOutDegree` | Directed edge counts |
166
- | `getEdgesOf(graph, nodeId)` | All connected edges |
167
- | `getInEdges` / `getOutEdges` | Directed edges |
168
- | `getEdgeBetween(graph, src, tgt)` | Edge between two nodes |
169
- | `getSources(graph)` | Nodes with inDegree 0 |
170
- | `getSinks(graph)` | Nodes with outDegree 0 |
171
-
172
- ### Hierarchy
173
-
174
- | Function | Description |
175
- |----------|-------------|
176
- | `getChildren(graph, nodeId)` | Direct children |
177
- | `getParent(graph, nodeId)` | Parent node |
178
- | `getAncestors(graph, nodeId)` | All ancestors |
179
- | `getDescendants(graph, nodeId)` | All descendants |
180
- | `getSiblings(graph, nodeId)` | Same-parent nodes |
181
- | `getRoots(graph)` | Top-level nodes |
182
- | `getDepth(graph, nodeId)` | Hierarchy depth (root = 0) |
183
- | `getLCA(graph, ...nodeIds)` | Least Common Ancestor |
184
- | `isCompound(graph, nodeId)` | Has children? |
185
- | `isLeaf(graph, nodeId)` | No children? |
186
-
187
- ### Algorithms
188
-
189
- | Function | Description |
190
- |----------|-------------|
191
- | `bfs(graph, startId)` | Breadth-first traversal (generator) |
192
- | `dfs(graph, startId)` | Depth-first traversal (generator) |
193
- | `hasPath(graph, src, tgt)` | Reachability check |
194
- | `isAcyclic(graph)` | No cycles? |
195
- | `isConnected(graph)` | Single connected component? |
196
- | `isTree(graph)` | Connected + acyclic? |
197
- | `getConnectedComponents(graph)` | Connected components |
198
- | `getStronglyConnectedComponents(graph)` | SCCs (directed) |
199
- | `getTopologicalSort(graph)` | Topological order, or `null` if cyclic |
200
- | `getShortestPath(graph, opts)` | Single shortest path |
201
- | `getShortestPaths(graph, opts)` | All shortest paths from source |
202
- | `getSimplePath(graph, opts)` | Single simple path |
203
- | `getSimplePaths(graph, opts)` | All simple paths |
204
- | `getCycles(graph)` | All cycles |
205
- | `getPreorder` / `getPostorder` | DFS orderings |
206
- | `getMinimumSpanningTree(graph, opts)` | MST (Prim's or Kruskal's) |
207
- | `getAllPairsShortestPaths(graph, opts)` | Floyd-Warshall or Dijkstra |
208
-
209
- Generator variants: `genShortestPaths`, `genSimplePaths`, `genCycles`, `genPreorders`, `genPostorders`.
210
-
211
- ### Transforms
212
-
213
- | Function | Description |
214
- |----------|-------------|
215
- | `flatten(graph)` | Decompose hierarchy into flat leaf-node graph |
216
-
217
- ### Formats
218
-
219
- Import format converters from subpaths (for example, `@statelyai/graph/dot` or `@statelyai/graph/mermaid`).
220
-
221
- | Format | Export | Import | Compound? | Notes |
222
- |--------|--------|--------|-----------|-------|
223
- | **Cytoscape.js JSON** | `toCytoscapeJSON` | `fromCytoscapeJSON` | Yes | `parent` maps to `parentId` |
224
- | **D3.js JSON** | `toD3Graph` | `fromD3Graph` | No | `{ nodes, links }` for force layouts |
225
- | **JSON Graph Format** | `toJGF` | `fromJGF` | Yes | Formal spec, metadata-extensible |
226
- | **GEXF** | `toGEXF` | `fromGEXF` | Yes | Gephi native, `pid` hierarchy, viz module |
227
- | **GraphML** | `toGraphML` | `fromGraphML` | Yes | XML standard, requires `fast-xml-parser` |
228
- | **GML** | `toGML` | `fromGML` | Yes | Nested node blocks for hierarchy |
229
- | **TGF** | `toTGF` | `fromTGF` | No | Minimal (id + label only) |
230
- | **DOT** | `toDOT` | `fromDOT` | Yes (subgraphs) | Graphviz DOT (`dotparser` peer dep) |
231
- | **Mermaid** | `toMermaid*` | `fromMermaid*` | Varies by diagram | Sequence, flowchart, state, class, ER, mindmap, block |
232
- | **Adjacency list** | `toAdjacencyList` | `fromAdjacencyList` | No | `Record<string, string[]>` |
233
- | **Edge list** | `toEdgeList` | `fromEdgeList` | No | `[source, target][]` |
234
-
235
- Optional peer deps by format:
236
- - `@statelyai/graph/gexf` and `@statelyai/graph/graphml` use `fast-xml-parser`
237
- - `@statelyai/graph/dot` uses `dotparser`
238
- - Other formats are dependency-free
157
+ Your `Graph` is a plain object that survives `JSON.stringify`, `structuredClone`, `postMessage`, and `localStorage` without adapters.
239
158
 
240
159
  ## License
241
160