@statelyai/graph 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -144
- package/dist/{algorithms-DldwenLt.mjs → algorithms-Dw5jEwDK.mjs} +242 -36
- package/dist/algorithms.d.mts +91 -3
- package/dist/algorithms.mjs +2 -2
- package/dist/formats/adjacency-list/index.d.mts +1 -1
- package/dist/formats/converter/index.d.mts +1 -1
- package/dist/formats/cytoscape/index.d.mts +1 -1
- package/dist/formats/d3/index.d.mts +1 -1
- package/dist/formats/dot/index.d.mts +1 -1
- package/dist/formats/edge-list/index.d.mts +1 -1
- package/dist/formats/elk/index.d.mts +1 -1
- package/dist/formats/gexf/index.d.mts +1 -1
- package/dist/formats/gml/index.d.mts +1 -1
- package/dist/formats/graphml/index.d.mts +1 -1
- package/dist/formats/graphml/index.mjs +147 -55
- package/dist/formats/jgf/index.d.mts +1 -1
- package/dist/formats/mermaid/index.d.mts +6 -1
- package/dist/formats/mermaid/index.mjs +20 -6
- package/dist/formats/tgf/index.d.mts +1 -1
- package/dist/formats/xyflow/index.d.mts +1 -1
- package/dist/index.d.mts +120 -3
- package/dist/index.mjs +364 -8
- package/dist/queries.d.mts +1 -1
- package/dist/queries.mjs +1 -1
- package/dist/schemas.d.mts +7 -5
- package/dist/schemas.mjs +4 -3
- package/dist/{types-FBZCrmnG.d.mts → types-DkKjaQW3.d.mts} +61 -9
- package/package.json +1 -1
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
|
-
|
|
40
|
+
## Graph Manipulation
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
Look up, add, delete, and update nodes and edges. Query neighbors, predecessors, successors, degree, and more.
|
|
42
43
|
|
|
43
44
|
```ts
|
|
44
|
-
import {
|
|
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
|
|
77
|
+
const children = getChildren(graph, 'b'); // [b1, b2]
|
|
78
|
+
const flat = flatten(graph); // only leaf nodes, edges resolved
|
|
62
79
|
```
|
|
63
80
|
|
|
64
|
-
|
|
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'
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
const cytoData = toCytoscapeJSON(graph);
|
|
93
|
-
const d3Data = toD3Graph(graph);
|
|
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
|
-
|
|
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
|
-
|
|
147
|
+
Some formats have optional peer dependencies: `fast-xml-parser` (GEXF, GraphML) and `dotparser` (DOT). All other formats are dependency-free.
|
|
119
148
|
|
|
120
|
-
|
|
149
|
+
## Why this library?
|
|
121
150
|
|
|
122
|
-
This library is the computational layer: plain JSON objects in, algorithms and mutations, plain JSON objects out.
|
|
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.
|
|
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
|
|