@statelyai/graph 0.11.1 → 0.13.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 +120 -46
- package/dist/{algorithms-BHHg7lGq.mjs → algorithms-BNDQcHU3.mjs} +7 -7
- package/dist/algorithms.mjs +1 -1
- package/dist/format-support.d.mts +6 -0
- package/dist/format-support.mjs +51 -45
- package/dist/formats/cytoscape/index.mjs +24 -2
- package/dist/formats/d3/index.d.mts +7 -0
- package/dist/formats/d3/index.mjs +42 -8
- package/dist/formats/dot/index.mjs +18 -6
- package/dist/formats/elk/index.mjs +86 -23
- package/dist/formats/gexf/index.mjs +138 -17
- package/dist/formats/gml/index.mjs +44 -11
- package/dist/formats/jgf/index.mjs +24 -2
- package/dist/formats/mermaid/index.d.mts +1 -0
- package/dist/formats/mermaid/index.mjs +39 -9
- package/dist/formats/xyflow/index.d.mts +1 -0
- package/dist/formats/xyflow/index.mjs +97 -40
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{indexing-CJc-ul8e.mjs → indexing-DUl3kTqm.mjs} +18 -4
- package/dist/queries.mjs +1 -1
- package/dist/schemas.d.mts +22 -7
- package/dist/schemas.mjs +158 -2
- package/package.json +1 -1
- package/schemas/graph.schema.json +1 -0
- package/schemas/node.schema.json +1 -0
package/README.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: '@statelyai/graph'
|
|
3
|
-
---
|
|
4
|
-
|
|
5
1
|
# @statelyai/graph
|
|
6
2
|
|
|
7
|
-
A TypeScript graph library
|
|
3
|
+
A TypeScript graph library for JSON-serializable graph IR. Use it to validate, analyze, transform, and round-trip directed, undirected, hierarchical, port-aware, and visual graphs across tools.
|
|
8
4
|
|
|
9
5
|
Made from our experience at [stately.ai](https://stately.ai), where we build visual tools for complex systems.
|
|
10
6
|
|
|
@@ -18,24 +14,24 @@ Optional peers are only needed for specific adapters:
|
|
|
18
14
|
|
|
19
15
|
<!-- optional peer dependencies derived from package.json#peerDependencies -->
|
|
20
16
|
|
|
21
|
-
| Package
|
|
22
|
-
|
|
|
17
|
+
| Package | Needed for |
|
|
18
|
+
| ----------------- | --------------------------------------------------- |
|
|
23
19
|
| `fast-xml-parser` | `@statelyai/graph/gexf`, `@statelyai/graph/graphml` |
|
|
24
|
-
| `dotparser`
|
|
25
|
-
| `cytoscape`
|
|
26
|
-
| `d3-force`
|
|
27
|
-
| `elkjs`
|
|
28
|
-
| `zod`
|
|
20
|
+
| `dotparser` | `@statelyai/graph/dot` parsing |
|
|
21
|
+
| `cytoscape` | Cytoscape integration tests and consumer typing |
|
|
22
|
+
| `d3-force` | D3 force integration tests and consumer typing |
|
|
23
|
+
| `elkjs` | `@statelyai/graph/elk` |
|
|
24
|
+
| `zod` | `@statelyai/graph/schemas` |
|
|
29
25
|
|
|
30
26
|
## Highlights
|
|
31
27
|
|
|
32
|
-
- Plain JSON graphs with no runtime wrappers required
|
|
28
|
+
- Plain JSON graphs with no runtime wrappers required; omitted `data` defaults to `null`
|
|
33
29
|
- Standalone functions with a consistent `get*`/`gen*`/`is*`/`add*` naming model
|
|
34
30
|
- Directed, undirected, hierarchical, and visual graph support
|
|
35
31
|
- Ports for node-editor and dataflow-style graphs
|
|
36
32
|
- Algorithms for traversal, paths, centrality, communities, connectivity, isomorphism, ordering, MST, and walks
|
|
37
33
|
- Diff/patch utilities for graph state changes
|
|
38
|
-
- Multi-format conversion via package subpaths
|
|
34
|
+
- Multi-format conversion via package subpaths, with fidelity claims tested against fixtures
|
|
39
35
|
- Small, fast test suite with broad format coverage
|
|
40
36
|
|
|
41
37
|
## Quick Start
|
|
@@ -43,7 +39,12 @@ Optional peers are only needed for specific adapters:
|
|
|
43
39
|
Graphs are plain JSON-serializable objects. All operations are standalone functions — no classes, no DOM, no rendering engine.
|
|
44
40
|
|
|
45
41
|
```ts
|
|
46
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
createGraph,
|
|
44
|
+
addNode,
|
|
45
|
+
addEdge,
|
|
46
|
+
getShortestPath,
|
|
47
|
+
} from '@statelyai/graph';
|
|
47
48
|
|
|
48
49
|
const graph = createGraph({
|
|
49
50
|
nodes: [
|
|
@@ -70,12 +71,17 @@ const path = getShortestPath(graph, { from: 'a', to: 'c' });
|
|
|
70
71
|
Look up, add, delete, and update nodes and edges. Query neighbors, predecessors, successors, degree, and more.
|
|
71
72
|
|
|
72
73
|
```ts
|
|
73
|
-
import {
|
|
74
|
+
import {
|
|
75
|
+
getNode,
|
|
76
|
+
deleteNode,
|
|
77
|
+
getNeighbors,
|
|
78
|
+
getSources,
|
|
79
|
+
} from '@statelyai/graph';
|
|
74
80
|
|
|
75
|
-
const node = getNode(graph, 'a');
|
|
76
|
-
deleteNode(graph, 'd');
|
|
81
|
+
const node = getNode(graph, 'a'); // lookup by id
|
|
82
|
+
deleteNode(graph, 'd'); // removes node + connected edges
|
|
77
83
|
const neighbors = getNeighbors(graph, 'a'); // adjacent nodes
|
|
78
|
-
const roots = getSources(graph);
|
|
84
|
+
const roots = getSources(graph); // nodes with no incoming edges
|
|
79
85
|
```
|
|
80
86
|
|
|
81
87
|
Batch operations (`addEntities`, `deleteEntities`, `updateEntities`) let you apply multiple changes at once.
|
|
@@ -96,14 +102,14 @@ const graph = createGraph({
|
|
|
96
102
|
{ id: 'c' },
|
|
97
103
|
],
|
|
98
104
|
edges: [
|
|
99
|
-
{ id: 'e1', sourceId: 'a', targetId: 'b' },
|
|
105
|
+
{ id: 'e1', sourceId: 'a', targetId: 'b' }, // resolves to a -> b1
|
|
100
106
|
{ id: 'e2', sourceId: 'b1', targetId: 'b2' },
|
|
101
|
-
{ id: 'e3', sourceId: 'b', targetId: 'c' },
|
|
107
|
+
{ id: 'e3', sourceId: 'b', targetId: 'c' }, // expands from all leaves of b
|
|
102
108
|
],
|
|
103
109
|
});
|
|
104
110
|
|
|
105
111
|
const children = getChildren(graph, 'b'); // [b1, b2]
|
|
106
|
-
const flat = flatten(graph);
|
|
112
|
+
const flat = flatten(graph); // only leaf nodes, edges resolved
|
|
107
113
|
```
|
|
108
114
|
|
|
109
115
|
## Ports
|
|
@@ -139,6 +145,26 @@ getPorts(graph, 'fetch'); // [{ name: 'result', ... }]
|
|
|
139
145
|
getEdgesByPort(graph, 'render', 'input'); // [e1]
|
|
140
146
|
```
|
|
141
147
|
|
|
148
|
+
## Schema Validation
|
|
149
|
+
|
|
150
|
+
<!-- validation helpers exported from src/schemas.ts -->
|
|
151
|
+
|
|
152
|
+
Use the `@statelyai/graph/schemas` subpath when you want runtime validation or JSON Schema generation. `validateGraph()` combines shape checks with graph invariants such as duplicate ids, dangling edges, missing parents, missing initial nodes, duplicate ports, invalid port references, and parent cycles.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { GraphSchema, isGraph, validateGraph } from '@statelyai/graph/schemas';
|
|
156
|
+
|
|
157
|
+
const unknownValue: unknown = JSON.parse(input);
|
|
158
|
+
|
|
159
|
+
if (isGraph(unknownValue)) {
|
|
160
|
+
// fully typed Graph
|
|
161
|
+
} else {
|
|
162
|
+
console.error(validateGraph(unknownValue));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const parsed = GraphSchema.parse(unknownValue);
|
|
166
|
+
```
|
|
167
|
+
|
|
142
168
|
## Algorithms
|
|
143
169
|
|
|
144
170
|
<!-- algorithm functions exported from src/algorithms.ts -->
|
|
@@ -147,27 +173,40 @@ Includes traversal (BFS, DFS, preorder/postorder), pathfinding (shortest path, s
|
|
|
147
173
|
|
|
148
174
|
```ts
|
|
149
175
|
import {
|
|
150
|
-
bfs,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
bfs,
|
|
177
|
+
dfs,
|
|
178
|
+
hasPath,
|
|
179
|
+
isAcyclic,
|
|
180
|
+
getShortestPath,
|
|
181
|
+
getCycles,
|
|
182
|
+
getTopologicalSort,
|
|
183
|
+
getConnectedComponents,
|
|
184
|
+
getMinimumSpanningTree,
|
|
185
|
+
getPageRank,
|
|
186
|
+
getLabelPropagationCommunities,
|
|
187
|
+
genGirvanNewmanCommunities,
|
|
188
|
+
getBridges,
|
|
189
|
+
isIsomorphic,
|
|
155
190
|
} from '@statelyai/graph';
|
|
156
191
|
|
|
157
|
-
for (const node of bfs(graph, 'a')) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
192
|
+
for (const node of bfs(graph, 'a')) {
|
|
193
|
+
/* breadth-first */
|
|
194
|
+
}
|
|
195
|
+
for (const node of dfs(graph, 'a')) {
|
|
196
|
+
/* depth-first */
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
hasPath(graph, 'a', 'c'); // reachability
|
|
200
|
+
isAcyclic(graph); // cycle check
|
|
201
|
+
getShortestPath(graph, { from: 'a', to: 'c' }); // single shortest path
|
|
202
|
+
getTopologicalSort(graph); // topological order (or null)
|
|
203
|
+
getConnectedComponents(graph); // connected components
|
|
204
|
+
getMinimumSpanningTree(graph, { weight: (e) => e.data?.weight ?? 1 }); // MST
|
|
205
|
+
getPageRank(graph); // link analysis scores
|
|
206
|
+
getLabelPropagationCommunities(graph); // community detection
|
|
207
|
+
[...genGirvanNewmanCommunities(graph)]; // lazy community splits
|
|
208
|
+
getBridges(graph); // bridge edges
|
|
209
|
+
isIsomorphic(graph, otherGraph); // structural equivalence
|
|
171
210
|
```
|
|
172
211
|
|
|
173
212
|
## Diff & Walks
|
|
@@ -205,10 +244,10 @@ import { fromGEXF } from '@statelyai/graph/gexf';
|
|
|
205
244
|
import { toCytoscapeJSON } from '@statelyai/graph/cytoscape';
|
|
206
245
|
import { toD3Graph } from '@statelyai/graph/d3';
|
|
207
246
|
|
|
208
|
-
const dot = toDOT(graph);
|
|
209
|
-
const cytoData = toCytoscapeJSON(graph);
|
|
210
|
-
const d3Data = toD3Graph(graph);
|
|
211
|
-
const imported = fromGEXF(gexfXmlString);
|
|
247
|
+
const dot = toDOT(graph); // Graphviz DOT
|
|
248
|
+
const cytoData = toCytoscapeJSON(graph); // Cytoscape.js JSON
|
|
249
|
+
const d3Data = toD3Graph(graph); // D3.js { nodes, links }
|
|
250
|
+
const imported = fromGEXF(gexfXmlString); // GEXF (Gephi)
|
|
212
251
|
```
|
|
213
252
|
|
|
214
253
|
<!-- supported format adapters derived from src/formats/* subdirectories -->
|
|
@@ -224,6 +263,38 @@ const cyto = cytoscapeConverter.to(graph);
|
|
|
224
263
|
const back = cytoscapeConverter.from(cyto);
|
|
225
264
|
```
|
|
226
265
|
|
|
266
|
+
Round-trip fidelity may use adapter-specific graph, node, and edge `data`
|
|
267
|
+
metadata when the target format does not have a native field for a source
|
|
268
|
+
concept. A `partial` round-trip entry means the adapter still drops meaningful
|
|
269
|
+
source information instead of preserving it as metadata.
|
|
270
|
+
|
|
271
|
+
## Format Support
|
|
272
|
+
|
|
273
|
+
<!-- format support matrix derived from src/formats/support.ts -->
|
|
274
|
+
|
|
275
|
+
| Format | Hierarchy | Ports | Visual | Round-trip | Notes |
|
|
276
|
+
| ------------------- | --------- | ------- | ------- | ---------- | -------------------------------------------------------------------------- |
|
|
277
|
+
| `adjacency-list` | none | none | none | partial | Connectivity only; edge metadata is lost. |
|
|
278
|
+
| `cytoscape` | full | full | full | full | Graph, node, and edge metadata round-trip through element data. |
|
|
279
|
+
| `d3` | full | full | full | full | Graph, node, and edge metadata round-trip through the loose JSON shape. |
|
|
280
|
+
| `dot` | partial | partial | partial | partial | Edge port ids round-trip, but `:port:compass` mapping is still incomplete. |
|
|
281
|
+
| `edge-list` | none | none | none | partial | Endpoints only. |
|
|
282
|
+
| `elk` | full | full | full | full | Metadata round-trips through reserved layout options. |
|
|
283
|
+
| `gexf` | full | full | full | full | Custom attributes preserve metadata beyond the standard viz module. |
|
|
284
|
+
| `gml` | full | full | full | full | Graph, node, and edge metadata round-trip through direct and JSON fields. |
|
|
285
|
+
| `graphml` | full | full | partial | partial | Ports round-trip through `<data>` fields. |
|
|
286
|
+
| `jgf` | full | full | full | full | Graph, node, and edge metadata round-trip through `metadata` objects. |
|
|
287
|
+
| `tgf` | none | none | none | partial | Minimal ids and labels only. |
|
|
288
|
+
| `xyflow` | full | full | full | full | Metadata round-trips through reserved data fields. |
|
|
289
|
+
| `mermaid/block` | partial | none | partial | partial | Syntax-driven, not port-aware. |
|
|
290
|
+
| `mermaid/class` | none | none | none | partial | Class syntax is stored conservatively. |
|
|
291
|
+
| `mermaid/er` | none | none | none | partial | Focuses on entities and cardinality. |
|
|
292
|
+
| `mermaid/flowchart` | partial | none | partial | partial | `linkStyle` indices are fragile. |
|
|
293
|
+
| `mermaid/ishikawa` | full | none | none | partial | Preserves hierarchy, not fishbone layout. |
|
|
294
|
+
| `mermaid/mindmap` | full | none | partial | partial | Icon syntax is not fully re-emitted. |
|
|
295
|
+
| `mermaid/sequence` | partial | none | none | partial | Actor links and menu syntax are incomplete. |
|
|
296
|
+
| `mermaid/state` | full | none | partial | full | State syntax round-trips through graph and node data. |
|
|
297
|
+
|
|
227
298
|
Some formats have optional peer dependencies: `fast-xml-parser` (GEXF, GraphML) and `dotparser` (DOT). All other formats are dependency-free.
|
|
228
299
|
|
|
229
300
|
Format-specific docs live alongside the source:
|
|
@@ -261,13 +332,14 @@ The repo includes runnable examples under [`examples/`](./examples):
|
|
|
261
332
|
```bash
|
|
262
333
|
pnpm install
|
|
263
334
|
pnpm verify
|
|
335
|
+
pnpm bench
|
|
264
336
|
```
|
|
265
337
|
|
|
266
338
|
See [CONTRIBUTING.md](./CONTRIBUTING.md) for contributor conventions, format-module checklist, and release notes guidance.
|
|
267
339
|
|
|
268
340
|
## Why this library?
|
|
269
341
|
|
|
270
|
-
Graph file formats define how to _store_ graphs. Visualization libraries define how to _render_ them. This library is the
|
|
342
|
+
Graph file formats define how to _store_ graphs. Visualization libraries define how to _render_ them. This library is the trusted interchange and analysis layer in between: plain JSON objects in, validation, algorithms, transforms, diffing, and format-preserving conversion out.
|
|
271
343
|
|
|
272
344
|
```
|
|
273
345
|
GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSON() → render
|
|
@@ -275,6 +347,8 @@ GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSO
|
|
|
275
347
|
|
|
276
348
|
Your `Graph` is a plain object that survives `JSON.stringify`, `structuredClone`, `postMessage`, and `localStorage` without adapters.
|
|
277
349
|
|
|
350
|
+
A canonical graph is a deterministic projection of a graph for comparison, hashing, snapshots, or caches. A future pure helper would return a new graph with stable node/edge ordering and normalized optional fields. A hash would be a digest of that canonical JSON. A summary would be a small structural report, for example node count, edge count, roots, sinks, component count, compound depth, port count, and whether the graph is acyclic. A pure `sortGraph()` would return a sorted copy and never mutate the input.
|
|
351
|
+
|
|
278
352
|
## License
|
|
279
353
|
|
|
280
354
|
MIT
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdge, o as invalidateIndex, r as indexAddNode, t as getIndex } from "./indexing-
|
|
1
|
+
import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdge, o as invalidateIndex, r as indexAddNode, t as getIndex } from "./indexing-DUl3kTqm.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/graph.ts
|
|
4
4
|
/**
|
|
@@ -7,7 +7,7 @@ import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdg
|
|
|
7
7
|
* @example
|
|
8
8
|
* ```ts
|
|
9
9
|
* const port = createGraphPort({ name: 'output', direction: 'out' });
|
|
10
|
-
* // { name: 'output', direction: 'out', data:
|
|
10
|
+
* // { name: 'output', direction: 'out', data: null }
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
13
|
function createGraphPort(config) {
|
|
@@ -15,7 +15,7 @@ function createGraphPort(config) {
|
|
|
15
15
|
const port = {
|
|
16
16
|
name: config.name,
|
|
17
17
|
direction: config.direction ?? "inout",
|
|
18
|
-
data: config.data
|
|
18
|
+
data: config.data ?? null
|
|
19
19
|
};
|
|
20
20
|
if (config.label !== void 0) port.label = config.label;
|
|
21
21
|
if (config.x !== void 0) port.x = config.x;
|
|
@@ -50,7 +50,7 @@ function createGraphNode(config) {
|
|
|
50
50
|
...config.parentId !== void 0 && { parentId: config.parentId ?? null },
|
|
51
51
|
...config.initialNodeId !== void 0 && { initialNodeId: config.initialNodeId ?? null },
|
|
52
52
|
label: config.label ?? null,
|
|
53
|
-
data: config.data
|
|
53
|
+
data: config.data ?? null
|
|
54
54
|
};
|
|
55
55
|
if (config.ports !== void 0 && config.ports.length > 0) {
|
|
56
56
|
validatePortNames(config.ports);
|
|
@@ -71,7 +71,7 @@ function createGraphNode(config) {
|
|
|
71
71
|
* @example
|
|
72
72
|
* ```ts
|
|
73
73
|
* const edge = createGraphEdge({ id: 'e1', sourceId: 'a', targetId: 'b' });
|
|
74
|
-
* // { type: 'edge', id: 'e1', sourceId: 'a', targetId: 'b', label: null, data:
|
|
74
|
+
* // { type: 'edge', id: 'e1', sourceId: 'a', targetId: 'b', label: null, data: null }
|
|
75
75
|
* ```
|
|
76
76
|
*/
|
|
77
77
|
function createGraphEdge(config) {
|
|
@@ -84,7 +84,7 @@ function createGraphEdge(config) {
|
|
|
84
84
|
sourceId: config.sourceId,
|
|
85
85
|
targetId: config.targetId,
|
|
86
86
|
label: config.label ?? null,
|
|
87
|
-
data: config.data
|
|
87
|
+
data: config.data ?? null
|
|
88
88
|
};
|
|
89
89
|
if (config.sourcePort !== void 0) edge.sourcePort = config.sourcePort;
|
|
90
90
|
if (config.targetPort !== void 0) edge.targetPort = config.targetPort;
|
|
@@ -115,7 +115,7 @@ function createGraph(config) {
|
|
|
115
115
|
initialNodeId: config?.initialNodeId ?? null,
|
|
116
116
|
nodes: (config?.nodes ?? []).map(createGraphNode),
|
|
117
117
|
edges: (config?.edges ?? []).map(createGraphEdge),
|
|
118
|
-
data: config?.data ??
|
|
118
|
+
data: config?.data ?? null
|
|
119
119
|
};
|
|
120
120
|
if (config?.direction !== void 0) graph.direction = config.direction;
|
|
121
121
|
if (config?.style !== void 0) graph.style = config.style;
|
package/dist/algorithms.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as isAcyclic, B as getShortestPaths, C as getPreorder, D as getConnectedComponents, E as dfs, F as genSimplePaths, H as getSimplePaths, I as getAStarPath, L as getAllPairsShortestPaths, M as isTree, N as genCycles, O as getTopologicalSort, P as genShortestPaths, R as getCycles, S as getPostorders, T as bfs, U as getStronglyConnectedComponents, V as getSimplePath, W as joinPaths, _ as getPageRank, a as genGirvanNewmanCommunities, b as genPreorders, c as getLabelPropagationCommunities, d as getClosenessCentrality, f as getDegreeCentrality, g as getOutDegreeCentrality, h as getInDegreeCentrality, i as getBridges, j as isConnected, k as hasPath, l as getModularity, m as getHITS, n as getArticulationPoints, o as getGirvanNewmanCommunities, p as getEigenvectorCentrality, r as getBiconnectedComponents, s as getGreedyModularityCommunities, t as isIsomorphic, u as getBetweennessCentrality, v as getMinimumSpanningTree, w as getPreorders, x as getPostorder, y as genPostorders, z as getShortestPath } from "./algorithms-
|
|
1
|
+
import { A as isAcyclic, B as getShortestPaths, C as getPreorder, D as getConnectedComponents, E as dfs, F as genSimplePaths, H as getSimplePaths, I as getAStarPath, L as getAllPairsShortestPaths, M as isTree, N as genCycles, O as getTopologicalSort, P as genShortestPaths, R as getCycles, S as getPostorders, T as bfs, U as getStronglyConnectedComponents, V as getSimplePath, W as joinPaths, _ as getPageRank, a as genGirvanNewmanCommunities, b as genPreorders, c as getLabelPropagationCommunities, d as getClosenessCentrality, f as getDegreeCentrality, g as getOutDegreeCentrality, h as getInDegreeCentrality, i as getBridges, j as isConnected, k as hasPath, l as getModularity, m as getHITS, n as getArticulationPoints, o as getGirvanNewmanCommunities, p as getEigenvectorCentrality, r as getBiconnectedComponents, s as getGreedyModularityCommunities, t as isIsomorphic, u as getBetweennessCentrality, v as getMinimumSpanningTree, w as getPreorders, x as getPostorder, y as genPostorders, z as getShortestPath } from "./algorithms-BNDQcHU3.mjs";
|
|
2
2
|
|
|
3
3
|
export { bfs, dfs, genCycles, genGirvanNewmanCommunities, genPostorders, genPreorders, genShortestPaths, genSimplePaths, getAStarPath, getAllPairsShortestPaths, getArticulationPoints, getBetweennessCentrality, getBiconnectedComponents, getBridges, getClosenessCentrality, getConnectedComponents, getCycles, getDegreeCentrality, getEigenvectorCentrality, getGirvanNewmanCommunities, getGreedyModularityCommunities, getHITS, getInDegreeCentrality, getLabelPropagationCommunities, getMinimumSpanningTree, getModularity, getOutDegreeCentrality, getPageRank, getPostorder, getPostorders, getPreorder, getPreorders, getShortestPath, getShortestPaths, getSimplePath, getSimplePaths, getStronglyConnectedComponents, getTopologicalSort, hasPath, isAcyclic, isConnected, isIsomorphic, isTree, joinPaths };
|
|
@@ -16,6 +16,12 @@ interface FormatSupportEntry {
|
|
|
16
16
|
features: FormatSupportFeatures;
|
|
17
17
|
notes: string[];
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Round-trip support is allowed to use adapter-specific graph, node, and edge
|
|
21
|
+
* `data` metadata when the target format has no native field for a source
|
|
22
|
+
* concept. A `partial` value means the adapter still drops meaningful source
|
|
23
|
+
* information instead of preserving it as metadata.
|
|
24
|
+
*/
|
|
19
25
|
declare const FORMAT_SUPPORT_MATRIX: FormatSupportEntry[];
|
|
20
26
|
declare function getFormatSupportEntry(id: string): FormatSupportEntry | undefined;
|
|
21
27
|
//#endregion
|
package/dist/format-support.mjs
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
//#region src/formats/support.ts
|
|
2
|
+
/**
|
|
3
|
+
* Round-trip support is allowed to use adapter-specific graph, node, and edge
|
|
4
|
+
* `data` metadata when the target format has no native field for a source
|
|
5
|
+
* concept. A `partial` value means the adapter still drops meaningful source
|
|
6
|
+
* information instead of preserving it as metadata.
|
|
7
|
+
*/
|
|
2
8
|
const FORMAT_SUPPORT_MATRIX = [
|
|
3
9
|
{
|
|
4
10
|
id: "adjacency-list",
|
|
@@ -21,14 +27,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
21
27
|
features: {
|
|
22
28
|
directed: "full",
|
|
23
29
|
undirected: "full",
|
|
24
|
-
hierarchy: "
|
|
25
|
-
ports: "
|
|
26
|
-
visual: "
|
|
27
|
-
style: "
|
|
30
|
+
hierarchy: "full",
|
|
31
|
+
ports: "full",
|
|
32
|
+
visual: "full",
|
|
33
|
+
style: "full",
|
|
28
34
|
weight: "full",
|
|
29
|
-
roundTrip: "
|
|
35
|
+
roundTrip: "full"
|
|
30
36
|
},
|
|
31
|
-
notes: ["Uses Cytoscape JSON element data with
|
|
37
|
+
notes: ["Uses Cytoscape JSON element data with graph, node, and edge metadata stored in element data.", "Ports round-trip through element data as `ports`, `sourcePort`, and `targetPort`."]
|
|
32
38
|
},
|
|
33
39
|
{
|
|
34
40
|
id: "d3",
|
|
@@ -36,14 +42,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
36
42
|
features: {
|
|
37
43
|
directed: "full",
|
|
38
44
|
undirected: "full",
|
|
39
|
-
hierarchy: "
|
|
40
|
-
ports: "
|
|
41
|
-
visual: "
|
|
42
|
-
style: "
|
|
45
|
+
hierarchy: "full",
|
|
46
|
+
ports: "full",
|
|
47
|
+
visual: "full",
|
|
48
|
+
style: "full",
|
|
43
49
|
weight: "full",
|
|
44
|
-
roundTrip: "
|
|
50
|
+
roundTrip: "full"
|
|
45
51
|
},
|
|
46
|
-
notes: ["Targets force-graph structures,
|
|
52
|
+
notes: ["Targets force-graph structures, but graph, node, and edge metadata can be preserved on the loose JSON shape.", "Ports round-trip through node/link objects."]
|
|
47
53
|
},
|
|
48
54
|
{
|
|
49
55
|
id: "dot",
|
|
@@ -58,7 +64,7 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
58
64
|
weight: "none",
|
|
59
65
|
roundTrip: "partial"
|
|
60
66
|
},
|
|
61
|
-
notes: ["
|
|
67
|
+
notes: ["Edge port ids round-trip, but compass points and node port definitions are not mapped.", "HTML labels and layout hints beyond `rankdir` are lossy."]
|
|
62
68
|
},
|
|
63
69
|
{
|
|
64
70
|
id: "edge-list",
|
|
@@ -84,11 +90,11 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
84
90
|
hierarchy: "full",
|
|
85
91
|
ports: "full",
|
|
86
92
|
visual: "full",
|
|
87
|
-
style: "
|
|
88
|
-
weight: "
|
|
89
|
-
roundTrip: "
|
|
93
|
+
style: "full",
|
|
94
|
+
weight: "full",
|
|
95
|
+
roundTrip: "full"
|
|
90
96
|
},
|
|
91
|
-
notes: ["
|
|
97
|
+
notes: ["ELK-native layout fields are preserved directly; graph, node, port, and edge metadata round-trip through reserved layout options."]
|
|
92
98
|
},
|
|
93
99
|
{
|
|
94
100
|
id: "gexf",
|
|
@@ -96,14 +102,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
96
102
|
features: {
|
|
97
103
|
directed: "full",
|
|
98
104
|
undirected: "full",
|
|
99
|
-
hierarchy: "
|
|
100
|
-
ports: "
|
|
101
|
-
visual: "
|
|
102
|
-
style: "
|
|
105
|
+
hierarchy: "full",
|
|
106
|
+
ports: "full",
|
|
107
|
+
visual: "full",
|
|
108
|
+
style: "full",
|
|
103
109
|
weight: "full",
|
|
104
|
-
roundTrip: "
|
|
110
|
+
roundTrip: "full"
|
|
105
111
|
},
|
|
106
|
-
notes: ["
|
|
112
|
+
notes: ["Custom attributes preserve graph, node, and edge metadata beyond the standard viz module.", "Ports round-trip via custom node/edge attributes."]
|
|
107
113
|
},
|
|
108
114
|
{
|
|
109
115
|
id: "gml",
|
|
@@ -111,14 +117,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
111
117
|
features: {
|
|
112
118
|
directed: "full",
|
|
113
119
|
undirected: "full",
|
|
114
|
-
hierarchy: "
|
|
115
|
-
ports: "
|
|
116
|
-
visual: "
|
|
117
|
-
style: "
|
|
120
|
+
hierarchy: "full",
|
|
121
|
+
ports: "full",
|
|
122
|
+
visual: "full",
|
|
123
|
+
style: "full",
|
|
118
124
|
weight: "full",
|
|
119
|
-
roundTrip: "
|
|
125
|
+
roundTrip: "full"
|
|
120
126
|
},
|
|
121
|
-
notes: ["GML
|
|
127
|
+
notes: ["GML stores graph, node, and edge metadata directly or as JSON-stringified fields.", "Ports round-trip through JSON-stringified node metadata and edge fields."]
|
|
122
128
|
},
|
|
123
129
|
{
|
|
124
130
|
id: "graphml",
|
|
@@ -126,14 +132,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
126
132
|
features: {
|
|
127
133
|
directed: "full",
|
|
128
134
|
undirected: "full",
|
|
129
|
-
hierarchy: "
|
|
130
|
-
ports: "
|
|
135
|
+
hierarchy: "full",
|
|
136
|
+
ports: "full",
|
|
131
137
|
visual: "partial",
|
|
132
138
|
style: "partial",
|
|
133
139
|
weight: "full",
|
|
134
140
|
roundTrip: "partial"
|
|
135
141
|
},
|
|
136
|
-
notes: ["GraphML attribute fidelity is good, but not every extension is represented."]
|
|
142
|
+
notes: ["GraphML attribute fidelity is good, but not every extension is represented.", "Ports round-trip through node and edge `<data>` fields."]
|
|
137
143
|
},
|
|
138
144
|
{
|
|
139
145
|
id: "jgf",
|
|
@@ -141,14 +147,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
141
147
|
features: {
|
|
142
148
|
directed: "full",
|
|
143
149
|
undirected: "full",
|
|
144
|
-
hierarchy: "
|
|
145
|
-
ports: "
|
|
146
|
-
visual: "
|
|
147
|
-
style: "
|
|
150
|
+
hierarchy: "full",
|
|
151
|
+
ports: "full",
|
|
152
|
+
visual: "full",
|
|
153
|
+
style: "full",
|
|
148
154
|
weight: "full",
|
|
149
|
-
roundTrip: "
|
|
155
|
+
roundTrip: "full"
|
|
150
156
|
},
|
|
151
|
-
notes: ["JGF preserves
|
|
157
|
+
notes: ["JGF preserves graph, node, and edge metadata via `metadata` objects.", "Ports round-trip through node and edge metadata."]
|
|
152
158
|
},
|
|
153
159
|
{
|
|
154
160
|
id: "tgf",
|
|
@@ -171,14 +177,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
171
177
|
features: {
|
|
172
178
|
directed: "full",
|
|
173
179
|
undirected: "partial",
|
|
174
|
-
hierarchy: "
|
|
180
|
+
hierarchy: "full",
|
|
175
181
|
ports: "full",
|
|
176
182
|
visual: "full",
|
|
177
|
-
style: "
|
|
178
|
-
weight: "
|
|
179
|
-
roundTrip: "
|
|
183
|
+
style: "full",
|
|
184
|
+
weight: "full",
|
|
185
|
+
roundTrip: "full"
|
|
180
186
|
},
|
|
181
|
-
notes: ["
|
|
187
|
+
notes: ["xyflow-native fields are preserved directly; graph, node, edge, style, weight, and port metadata round-trip through reserved data fields."]
|
|
182
188
|
},
|
|
183
189
|
{
|
|
184
190
|
id: "mermaid/block",
|
|
@@ -296,9 +302,9 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
296
302
|
visual: "partial",
|
|
297
303
|
style: "partial",
|
|
298
304
|
weight: "none",
|
|
299
|
-
roundTrip: "
|
|
305
|
+
roundTrip: "full"
|
|
300
306
|
},
|
|
301
|
-
notes: ["State notes
|
|
307
|
+
notes: ["State-specific syntax such as notes, classes, descriptions, directions, hierarchy, and parallel regions round-trips through node and graph data."]
|
|
302
308
|
}
|
|
303
309
|
];
|
|
304
310
|
function getFormatSupportEntry(id) {
|
|
@@ -25,6 +25,7 @@ function toCytoscapeJSON(graph) {
|
|
|
25
25
|
if (graph.initialNodeId) graphData.initialNodeId = graph.initialNodeId;
|
|
26
26
|
if (graph.data !== void 0) graphData.graphData = graph.data;
|
|
27
27
|
if (graph.direction) graphData.direction = graph.direction;
|
|
28
|
+
if (graph.style !== void 0) graphData.style = graph.style;
|
|
28
29
|
return {
|
|
29
30
|
...Object.keys(graphData).length > 0 && { data: graphData },
|
|
30
31
|
elements: {
|
|
@@ -38,6 +39,8 @@ function toCytoscapeJSON(graph) {
|
|
|
38
39
|
if (n.height !== void 0) data.height = n.height;
|
|
39
40
|
if (n.shape) data.shape = n.shape;
|
|
40
41
|
if (n.color) data.color = n.color;
|
|
42
|
+
if (n.style !== void 0) data.style = n.style;
|
|
43
|
+
if (n.ports !== void 0) data.ports = n.ports;
|
|
41
44
|
const node = { data };
|
|
42
45
|
if (n.x !== void 0 && n.y !== void 0) node.position = {
|
|
43
46
|
x: n.x,
|
|
@@ -53,7 +56,15 @@ function toCytoscapeJSON(graph) {
|
|
|
53
56
|
};
|
|
54
57
|
if (e.label) data.label = e.label;
|
|
55
58
|
if (e.data !== void 0) data.edgeData = e.data;
|
|
59
|
+
if (e.weight !== void 0) data.weight = e.weight;
|
|
60
|
+
if (e.x !== void 0) data.x = e.x;
|
|
61
|
+
if (e.y !== void 0) data.y = e.y;
|
|
62
|
+
if (e.width !== void 0) data.width = e.width;
|
|
63
|
+
if (e.height !== void 0) data.height = e.height;
|
|
56
64
|
if (e.color) data.color = e.color;
|
|
65
|
+
if (e.style !== void 0) data.style = e.style;
|
|
66
|
+
if (e.sourcePort !== void 0) data.sourcePort = e.sourcePort;
|
|
67
|
+
if (e.targetPort !== void 0) data.targetPort = e.targetPort;
|
|
57
68
|
return { data };
|
|
58
69
|
})
|
|
59
70
|
}
|
|
@@ -85,6 +96,7 @@ function fromCytoscapeJSON(cyto) {
|
|
|
85
96
|
initialNodeId: cyto.data?.initialNodeId ?? null,
|
|
86
97
|
data: cyto.data?.graphData,
|
|
87
98
|
...cyto.data?.direction && { direction: cyto.data.direction },
|
|
99
|
+
...cyto.data?.style !== void 0 && { style: cyto.data.style },
|
|
88
100
|
nodes: cyto.elements.nodes.map((n) => ({
|
|
89
101
|
type: "node",
|
|
90
102
|
id: n.data.id,
|
|
@@ -99,7 +111,9 @@ function fromCytoscapeJSON(cyto) {
|
|
|
99
111
|
...n.data.width !== void 0 && { width: n.data.width },
|
|
100
112
|
...n.data.height !== void 0 && { height: n.data.height },
|
|
101
113
|
...n.data.shape && { shape: n.data.shape },
|
|
102
|
-
...n.data.color && { color: n.data.color }
|
|
114
|
+
...n.data.color && { color: n.data.color },
|
|
115
|
+
...n.data.style !== void 0 && { style: n.data.style },
|
|
116
|
+
...n.data.ports !== void 0 && { ports: n.data.ports }
|
|
103
117
|
})),
|
|
104
118
|
edges: cyto.elements.edges.map((e, i) => ({
|
|
105
119
|
type: "edge",
|
|
@@ -108,7 +122,15 @@ function fromCytoscapeJSON(cyto) {
|
|
|
108
122
|
targetId: e.data.target,
|
|
109
123
|
label: e.data.label ?? "",
|
|
110
124
|
data: e.data.edgeData,
|
|
111
|
-
...e.data.
|
|
125
|
+
...e.data.weight !== void 0 && { weight: e.data.weight },
|
|
126
|
+
...e.data.x !== void 0 && { x: e.data.x },
|
|
127
|
+
...e.data.y !== void 0 && { y: e.data.y },
|
|
128
|
+
...e.data.width !== void 0 && { width: e.data.width },
|
|
129
|
+
...e.data.height !== void 0 && { height: e.data.height },
|
|
130
|
+
...e.data.color && { color: e.data.color },
|
|
131
|
+
...e.data.style !== void 0 && { style: e.data.style },
|
|
132
|
+
...e.data.sourcePort !== void 0 && { sourcePort: e.data.sourcePort },
|
|
133
|
+
...e.data.targetPort !== void 0 && { targetPort: e.data.targetPort }
|
|
112
134
|
}))
|
|
113
135
|
};
|
|
114
136
|
}
|
|
@@ -11,8 +11,15 @@ interface D3Link {
|
|
|
11
11
|
[key: string]: any;
|
|
12
12
|
}
|
|
13
13
|
interface D3Graph {
|
|
14
|
+
id?: string;
|
|
15
|
+
type?: Graph['type'];
|
|
16
|
+
initialNodeId?: string | null;
|
|
17
|
+
data?: any;
|
|
18
|
+
direction?: Graph['direction'];
|
|
19
|
+
style?: Graph['style'];
|
|
14
20
|
nodes: D3Node[];
|
|
15
21
|
links: D3Link[];
|
|
22
|
+
[key: string]: any;
|
|
16
23
|
}
|
|
17
24
|
/**
|
|
18
25
|
* Converts a graph to D3.js force-directed format.
|