@statelyai/graph 0.12.0 → 1.0.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 +26 -16
- package/dist/{adjacency-list-Ca0VjKIf.mjs → adjacency-list-VsUaH9SJ.mjs} +2 -2
- package/dist/{algorithms-BHHg7lGq.mjs → algorithms-Ba7o7niK.mjs} +35 -31
- package/dist/{algorithms-BlM-qoJb.d.mts → algorithms-fTqmvhzP.d.mts} +1 -1
- package/dist/algorithms.d.mts +1 -1
- package/dist/algorithms.mjs +1 -1
- package/dist/{converter-Dspillnn.mjs → converter-udLITX36.mjs} +2 -2
- package/dist/{edge-list-gKe8-iRa.mjs → edge-list-DP4otyPU.mjs} +1 -1
- package/dist/format-support.d.mts +6 -0
- package/dist/format-support.mjs +68 -33
- package/dist/formats/adjacency-list/index.d.mts +1 -1
- package/dist/formats/adjacency-list/index.mjs +1 -1
- package/dist/formats/converter/index.d.mts +1 -60
- package/dist/formats/converter/index.mjs +1 -1
- package/dist/formats/cytoscape/index.d.mts +1 -1
- package/dist/formats/cytoscape/index.mjs +19 -3
- package/dist/formats/d2/index.d.mts +109 -0
- package/dist/formats/d2/index.mjs +1086 -0
- package/dist/formats/d3/index.d.mts +8 -1
- package/dist/formats/d3/index.mjs +35 -7
- package/dist/formats/dot/index.d.mts +1 -1
- package/dist/formats/dot/index.mjs +3 -3
- package/dist/formats/edge-list/index.d.mts +1 -1
- package/dist/formats/edge-list/index.mjs +1 -1
- package/dist/formats/elk/index.d.mts +1 -1
- package/dist/formats/elk/index.mjs +86 -23
- package/dist/formats/gexf/index.d.mts +1 -1
- package/dist/formats/gexf/index.mjs +102 -4
- package/dist/formats/gml/index.d.mts +1 -1
- package/dist/formats/gml/index.mjs +43 -16
- package/dist/formats/graphml/index.d.mts +1 -1
- package/dist/formats/graphml/index.mjs +11 -4
- package/dist/formats/jgf/index.d.mts +1 -1
- package/dist/formats/jgf/index.mjs +19 -3
- package/dist/formats/mermaid/index.d.mts +2 -1
- package/dist/formats/mermaid/index.mjs +48 -18
- package/dist/formats/tgf/index.d.mts +1 -1
- package/dist/formats/tgf/index.mjs +2 -2
- package/dist/formats/xyflow/index.d.mts +2 -1
- package/dist/formats/xyflow/index.mjs +97 -40
- package/dist/index-CHoriXZD.d.mts +638 -0
- package/dist/index-D9Kj6Fe3.d.mts +61 -0
- package/dist/index.d.mts +6 -631
- package/dist/index.mjs +8 -7
- package/dist/{indexing-CJc-ul8e.mjs → indexing-DR8M1vBy.mjs} +18 -4
- package/dist/mode-D8OnHFBk.mjs +15 -0
- package/dist/queries-BlkA1HAN.d.mts +516 -0
- package/dist/queries.d.mts +1 -514
- package/dist/queries.mjs +17 -15
- package/dist/schemas.d.mts +29 -17
- package/dist/schemas.mjs +139 -6
- package/dist/{types-CnZ01raw.d.mts → types-3-FS9NV2.d.mts} +30 -7
- package/package.json +2 -1
- package/schemas/edge.schema.json +11 -0
- package/schemas/graph.schema.json +25 -3
- package/schemas/node.schema.json +7 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @statelyai/graph
|
|
2
2
|
|
|
3
|
-
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.
|
|
4
4
|
|
|
5
5
|
Made from our experience at [stately.ai](https://stately.ai), where we build visual tools for complex systems.
|
|
6
6
|
|
|
@@ -25,13 +25,13 @@ Optional peers are only needed for specific adapters:
|
|
|
25
25
|
|
|
26
26
|
## Highlights
|
|
27
27
|
|
|
28
|
-
- Plain JSON graphs with no runtime wrappers required
|
|
28
|
+
- Plain JSON graphs with no runtime wrappers required; omitted `data` defaults to `null`
|
|
29
29
|
- Standalone functions with a consistent `get*`/`gen*`/`is*`/`add*` naming model
|
|
30
30
|
- Directed, undirected, hierarchical, and visual graph support
|
|
31
31
|
- Ports for node-editor and dataflow-style graphs
|
|
32
32
|
- Algorithms for traversal, paths, centrality, communities, connectivity, isomorphism, ordering, MST, and walks
|
|
33
33
|
- Diff/patch utilities for graph state changes
|
|
34
|
-
- Multi-format conversion via package subpaths
|
|
34
|
+
- Multi-format conversion via package subpaths, with fidelity claims tested against fixtures
|
|
35
35
|
- Small, fast test suite with broad format coverage
|
|
36
36
|
|
|
37
37
|
## Quick Start
|
|
@@ -149,17 +149,17 @@ getEdgesByPort(graph, 'render', 'input'); // [e1]
|
|
|
149
149
|
|
|
150
150
|
<!-- validation helpers exported from src/schemas.ts -->
|
|
151
151
|
|
|
152
|
-
Use the `@statelyai/graph/schemas` subpath when you want runtime validation or JSON Schema generation.
|
|
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
153
|
|
|
154
154
|
```ts
|
|
155
|
-
import { GraphSchema,
|
|
155
|
+
import { GraphSchema, isGraph, validateGraph } from '@statelyai/graph/schemas';
|
|
156
156
|
|
|
157
157
|
const unknownValue: unknown = JSON.parse(input);
|
|
158
158
|
|
|
159
159
|
if (isGraph(unknownValue)) {
|
|
160
160
|
// fully typed Graph
|
|
161
161
|
} else {
|
|
162
|
-
console.error(
|
|
162
|
+
console.error(validateGraph(unknownValue));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
const parsed = GraphSchema.parse(unknownValue);
|
|
@@ -252,7 +252,7 @@ const imported = fromGEXF(gexfXmlString); // GEXF (Gephi)
|
|
|
252
252
|
|
|
253
253
|
<!-- supported format adapters derived from src/formats/* subdirectories -->
|
|
254
254
|
|
|
255
|
-
**Supported formats:** Cytoscape.js JSON, D3.js JSON, JSON Graph Format, GEXF, GraphML, GML, TGF, DOT, Mermaid (flowchart, state, sequence, class, ER, mindmap, block, Ishikawa), ELK, xyflow, adjacency list, and edge list.
|
|
255
|
+
**Supported formats:** Cytoscape.js JSON, D3.js JSON, D2, JSON Graph Format, GEXF, GraphML, GML, TGF, DOT, Mermaid (flowchart, state, sequence, class, ER, mindmap, block, Ishikawa), ELK, xyflow, adjacency list, and edge list.
|
|
256
256
|
|
|
257
257
|
Each bidirectional format also has a converter object:
|
|
258
258
|
|
|
@@ -263,6 +263,11 @@ const cyto = cytoscapeConverter.to(graph);
|
|
|
263
263
|
const back = cytoscapeConverter.from(cyto);
|
|
264
264
|
```
|
|
265
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
|
+
|
|
266
271
|
## Format Support
|
|
267
272
|
|
|
268
273
|
<!-- format support matrix derived from src/formats/support.ts -->
|
|
@@ -270,17 +275,18 @@ const back = cytoscapeConverter.from(cyto);
|
|
|
270
275
|
| Format | Hierarchy | Ports | Visual | Round-trip | Notes |
|
|
271
276
|
| ------------------- | --------- | ------- | ------- | ---------- | -------------------------------------------------------------------------- |
|
|
272
277
|
| `adjacency-list` | none | none | none | partial | Connectivity only; edge metadata is lost. |
|
|
273
|
-
| `cytoscape` | full | full |
|
|
274
|
-
| `d3` |
|
|
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
|
+
| `d2` | full | full | full | full | D2 syntax, hierarchy, ports, styles, and connector modes round-trip. |
|
|
275
281
|
| `dot` | partial | partial | partial | partial | Edge port ids round-trip, but `:port:compass` mapping is still incomplete. |
|
|
276
282
|
| `edge-list` | none | none | none | partial | Endpoints only. |
|
|
277
|
-
| `elk` | full | full | full |
|
|
278
|
-
| `gexf` | full | full |
|
|
279
|
-
| `gml` | full | full |
|
|
283
|
+
| `elk` | full | full | full | full | Metadata round-trips through reserved layout options. |
|
|
284
|
+
| `gexf` | full | full | full | full | Custom attributes preserve metadata beyond the standard viz module. |
|
|
285
|
+
| `gml` | full | full | full | full | Graph, node, and edge metadata round-trip through direct and JSON fields. |
|
|
280
286
|
| `graphml` | full | full | partial | partial | Ports round-trip through `<data>` fields. |
|
|
281
|
-
| `jgf` | full | full |
|
|
287
|
+
| `jgf` | full | full | full | full | Graph, node, and edge metadata round-trip through `metadata` objects. |
|
|
282
288
|
| `tgf` | none | none | none | partial | Minimal ids and labels only. |
|
|
283
|
-
| `xyflow` |
|
|
289
|
+
| `xyflow` | full | full | full | full | Metadata round-trips through reserved data fields. |
|
|
284
290
|
| `mermaid/block` | partial | none | partial | partial | Syntax-driven, not port-aware. |
|
|
285
291
|
| `mermaid/class` | none | none | none | partial | Class syntax is stored conservatively. |
|
|
286
292
|
| `mermaid/er` | none | none | none | partial | Focuses on entities and cardinality. |
|
|
@@ -288,7 +294,7 @@ const back = cytoscapeConverter.from(cyto);
|
|
|
288
294
|
| `mermaid/ishikawa` | full | none | none | partial | Preserves hierarchy, not fishbone layout. |
|
|
289
295
|
| `mermaid/mindmap` | full | none | partial | partial | Icon syntax is not fully re-emitted. |
|
|
290
296
|
| `mermaid/sequence` | partial | none | none | partial | Actor links and menu syntax are incomplete. |
|
|
291
|
-
| `mermaid/state` | full | none | partial |
|
|
297
|
+
| `mermaid/state` | full | none | partial | full | State syntax round-trips through graph and node data. |
|
|
292
298
|
|
|
293
299
|
Some formats have optional peer dependencies: `fast-xml-parser` (GEXF, GraphML) and `dotparser` (DOT). All other formats are dependency-free.
|
|
294
300
|
|
|
@@ -299,6 +305,7 @@ Format-specific docs live alongside the source:
|
|
|
299
305
|
- [Adjacency list](./src/formats/adjacency-list/README.md)
|
|
300
306
|
- [Cytoscape](./src/formats/cytoscape/README.md)
|
|
301
307
|
- [D3](./src/formats/d3/README.md)
|
|
308
|
+
- [D2](./src/formats/d2/README.md)
|
|
302
309
|
- [DOT](./src/formats/dot/README.md)
|
|
303
310
|
- [Edge list](./src/formats/edge-list/README.md)
|
|
304
311
|
- [ELK](./src/formats/elk/README.md)
|
|
@@ -327,13 +334,14 @@ The repo includes runnable examples under [`examples/`](./examples):
|
|
|
327
334
|
```bash
|
|
328
335
|
pnpm install
|
|
329
336
|
pnpm verify
|
|
337
|
+
pnpm bench
|
|
330
338
|
```
|
|
331
339
|
|
|
332
340
|
See [CONTRIBUTING.md](./CONTRIBUTING.md) for contributor conventions, format-module checklist, and release notes guidance.
|
|
333
341
|
|
|
334
342
|
## Why this library?
|
|
335
343
|
|
|
336
|
-
Graph file formats define how to _store_ graphs. Visualization libraries define how to _render_ them. This library is the
|
|
344
|
+
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.
|
|
337
345
|
|
|
338
346
|
```
|
|
339
347
|
GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSON() → render
|
|
@@ -341,6 +349,8 @@ GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSO
|
|
|
341
349
|
|
|
342
350
|
Your `Graph` is a plain object that survives `JSON.stringify`, `structuredClone`, `postMessage`, and `localStorage` without adapters.
|
|
343
351
|
|
|
352
|
+
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.
|
|
353
|
+
|
|
344
354
|
## License
|
|
345
355
|
|
|
346
356
|
MIT
|
|
@@ -20,7 +20,7 @@ function toAdjacencyList(graph) {
|
|
|
20
20
|
for (const node of graph.nodes) adj[node.id] = [];
|
|
21
21
|
for (const edge of graph.edges) {
|
|
22
22
|
adj[edge.sourceId]?.push(edge.targetId);
|
|
23
|
-
if (graph.
|
|
23
|
+
if (graph.mode !== "directed") adj[edge.targetId]?.push(edge.sourceId);
|
|
24
24
|
}
|
|
25
25
|
return adj;
|
|
26
26
|
}
|
|
@@ -70,7 +70,7 @@ function fromAdjacencyList(adj, options) {
|
|
|
70
70
|
}
|
|
71
71
|
return {
|
|
72
72
|
id: options?.id ?? "",
|
|
73
|
-
|
|
73
|
+
mode: directed ? "directed" : "undirected",
|
|
74
74
|
initialNodeId: null,
|
|
75
75
|
nodes,
|
|
76
76
|
edges,
|
|
@@ -1,4 +1,5 @@
|
|
|
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-DR8M1vBy.mjs";
|
|
2
|
+
import { t as getEdgeMode } from "./mode-D8OnHFBk.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/graph.ts
|
|
4
5
|
/**
|
|
@@ -7,7 +8,7 @@ import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdg
|
|
|
7
8
|
* @example
|
|
8
9
|
* ```ts
|
|
9
10
|
* const port = createGraphPort({ name: 'output', direction: 'out' });
|
|
10
|
-
* // { name: 'output', direction: 'out', data:
|
|
11
|
+
* // { name: 'output', direction: 'out', data: null }
|
|
11
12
|
* ```
|
|
12
13
|
*/
|
|
13
14
|
function createGraphPort(config) {
|
|
@@ -15,7 +16,7 @@ function createGraphPort(config) {
|
|
|
15
16
|
const port = {
|
|
16
17
|
name: config.name,
|
|
17
18
|
direction: config.direction ?? "inout",
|
|
18
|
-
data: config.data
|
|
19
|
+
data: config.data ?? null
|
|
19
20
|
};
|
|
20
21
|
if (config.label !== void 0) port.label = config.label;
|
|
21
22
|
if (config.x !== void 0) port.x = config.x;
|
|
@@ -50,7 +51,7 @@ function createGraphNode(config) {
|
|
|
50
51
|
...config.parentId !== void 0 && { parentId: config.parentId ?? null },
|
|
51
52
|
...config.initialNodeId !== void 0 && { initialNodeId: config.initialNodeId ?? null },
|
|
52
53
|
label: config.label ?? null,
|
|
53
|
-
data: config.data
|
|
54
|
+
data: config.data ?? null
|
|
54
55
|
};
|
|
55
56
|
if (config.ports !== void 0 && config.ports.length > 0) {
|
|
56
57
|
validatePortNames(config.ports);
|
|
@@ -71,7 +72,7 @@ function createGraphNode(config) {
|
|
|
71
72
|
* @example
|
|
72
73
|
* ```ts
|
|
73
74
|
* const edge = createGraphEdge({ id: 'e1', sourceId: 'a', targetId: 'b' });
|
|
74
|
-
* // { type: 'edge', id: 'e1', sourceId: 'a', targetId: 'b', label: null, data:
|
|
75
|
+
* // { type: 'edge', id: 'e1', sourceId: 'a', targetId: 'b', label: null, data: null }
|
|
75
76
|
* ```
|
|
76
77
|
*/
|
|
77
78
|
function createGraphEdge(config) {
|
|
@@ -84,10 +85,11 @@ function createGraphEdge(config) {
|
|
|
84
85
|
sourceId: config.sourceId,
|
|
85
86
|
targetId: config.targetId,
|
|
86
87
|
label: config.label ?? null,
|
|
87
|
-
data: config.data
|
|
88
|
+
data: config.data ?? null
|
|
88
89
|
};
|
|
89
90
|
if (config.sourcePort !== void 0) edge.sourcePort = config.sourcePort;
|
|
90
91
|
if (config.targetPort !== void 0) edge.targetPort = config.targetPort;
|
|
92
|
+
if (config.mode !== void 0) edge.mode = config.mode;
|
|
91
93
|
if (config.weight !== void 0) edge.weight = config.weight;
|
|
92
94
|
if (config.x !== void 0) edge.x = config.x;
|
|
93
95
|
if (config.y !== void 0) edge.y = config.y;
|
|
@@ -111,11 +113,11 @@ function createGraphEdge(config) {
|
|
|
111
113
|
function createGraph(config) {
|
|
112
114
|
const graph = {
|
|
113
115
|
id: config?.id ?? "",
|
|
114
|
-
|
|
116
|
+
mode: config?.mode ?? "directed",
|
|
115
117
|
initialNodeId: config?.initialNodeId ?? null,
|
|
116
118
|
nodes: (config?.nodes ?? []).map(createGraphNode),
|
|
117
119
|
edges: (config?.edges ?? []).map(createGraphEdge),
|
|
118
|
-
data: config?.data ??
|
|
120
|
+
data: config?.data ?? null
|
|
119
121
|
};
|
|
120
122
|
if (config?.direction !== void 0) graph.direction = config.direction;
|
|
121
123
|
if (config?.style !== void 0) graph.style = config.style;
|
|
@@ -244,7 +246,7 @@ function createGraphFromTransition(transition, options) {
|
|
|
244
246
|
}
|
|
245
247
|
return createGraph({
|
|
246
248
|
id: options.id ?? "",
|
|
247
|
-
|
|
249
|
+
mode: "directed",
|
|
248
250
|
initialNodeId: initialStateId,
|
|
249
251
|
nodes,
|
|
250
252
|
edges
|
|
@@ -582,8 +584,9 @@ var GraphInstance = class GraphInstance {
|
|
|
582
584
|
get id() {
|
|
583
585
|
return this.graph.id;
|
|
584
586
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
+
/** Default directedness for all edges. */
|
|
588
|
+
get mode() {
|
|
589
|
+
return this.graph.mode;
|
|
587
590
|
}
|
|
588
591
|
get nodes() {
|
|
589
592
|
return this.graph.nodes;
|
|
@@ -703,9 +706,11 @@ function getNeighborIds$1(graph, nodeId) {
|
|
|
703
706
|
const ai = idx.edgeById.get(eid);
|
|
704
707
|
if (ai !== void 0) ids.push(graph.edges[ai].targetId);
|
|
705
708
|
}
|
|
706
|
-
|
|
709
|
+
for (const eid of idx.inEdges.get(nodeId) ?? []) {
|
|
707
710
|
const ai = idx.edgeById.get(eid);
|
|
708
|
-
if (ai
|
|
711
|
+
if (ai === void 0) continue;
|
|
712
|
+
const edge = graph.edges[ai];
|
|
713
|
+
if (getEdgeMode(graph, edge) !== "directed") ids.push(edge.sourceId);
|
|
709
714
|
}
|
|
710
715
|
return ids;
|
|
711
716
|
}
|
|
@@ -736,11 +741,11 @@ function getNeighborEdges(graph, nodeId) {
|
|
|
736
741
|
});
|
|
737
742
|
}
|
|
738
743
|
}
|
|
739
|
-
|
|
744
|
+
for (const eid of idx.inEdges.get(nodeId) ?? []) {
|
|
740
745
|
const ai = idx.edgeById.get(eid);
|
|
741
746
|
if (ai !== void 0) {
|
|
742
747
|
const edge = graph.edges[ai];
|
|
743
|
-
result.push({
|
|
748
|
+
if (getEdgeMode(graph, edge) !== "directed") result.push({
|
|
744
749
|
neighborId: edge.sourceId,
|
|
745
750
|
edge
|
|
746
751
|
});
|
|
@@ -844,7 +849,6 @@ function bellmanFord(graph, sourceId, getWeight) {
|
|
|
844
849
|
const dist = /* @__PURE__ */ new Map();
|
|
845
850
|
const prev = /* @__PURE__ */ new Map();
|
|
846
851
|
const effectiveWeight = getWeight ?? ((edge) => edge.weight ?? 1);
|
|
847
|
-
const isUndirected = graph.type === "undirected";
|
|
848
852
|
for (const node of graph.nodes) {
|
|
849
853
|
dist.set(node.id, Infinity);
|
|
850
854
|
prev.set(node.id, []);
|
|
@@ -857,7 +861,7 @@ function bellmanFord(graph, sourceId, getWeight) {
|
|
|
857
861
|
toId: edge.targetId,
|
|
858
862
|
edge
|
|
859
863
|
});
|
|
860
|
-
if (
|
|
864
|
+
if (getEdgeMode(graph, edge) !== "directed") directedEdges.push({
|
|
861
865
|
fromId: edge.targetId,
|
|
862
866
|
toId: edge.sourceId,
|
|
863
867
|
edge
|
|
@@ -1021,7 +1025,7 @@ function getCycles(graph) {
|
|
|
1021
1025
|
return [...genCycles(graph)];
|
|
1022
1026
|
}
|
|
1023
1027
|
function* genCycles(graph) {
|
|
1024
|
-
if (graph.
|
|
1028
|
+
if (graph.mode !== "directed") yield* genCyclesUndirected(graph);
|
|
1025
1029
|
else yield* genCyclesDirected(graph);
|
|
1026
1030
|
}
|
|
1027
1031
|
function* genCyclesDirected(graph) {
|
|
@@ -1160,7 +1164,7 @@ function floydWarshallAllPaths(graph, getWeight) {
|
|
|
1160
1164
|
from: source,
|
|
1161
1165
|
edge
|
|
1162
1166
|
});
|
|
1163
|
-
if (graph
|
|
1167
|
+
if (getEdgeMode(graph, edge) !== "directed") {
|
|
1164
1168
|
if (edgeWeight < dist[target][source]) {
|
|
1165
1169
|
dist[target][source] = edgeWeight;
|
|
1166
1170
|
prev[target][source] = [{
|
|
@@ -1318,7 +1322,7 @@ function* dfs(graph, startId) {
|
|
|
1318
1322
|
}
|
|
1319
1323
|
}
|
|
1320
1324
|
function isAcyclic(graph) {
|
|
1321
|
-
if (graph.
|
|
1325
|
+
if (graph.mode !== "directed") return isAcyclicUndirected(graph);
|
|
1322
1326
|
const WHITE = 0;
|
|
1323
1327
|
const GRAY = 1;
|
|
1324
1328
|
const BLACK = 2;
|
|
@@ -1576,7 +1580,7 @@ function getMinimumSpanningTree(graph, opts) {
|
|
|
1576
1580
|
const mstEdges = algorithm === "kruskal" ? kruskalMST(graph, getWeight) : primMST(graph, getWeight);
|
|
1577
1581
|
return createGraph({
|
|
1578
1582
|
id: graph.id,
|
|
1579
|
-
|
|
1583
|
+
mode: graph.mode,
|
|
1580
1584
|
initialNodeId: graph.initialNodeId ?? void 0,
|
|
1581
1585
|
nodes: graph.nodes.map((node) => ({
|
|
1582
1586
|
id: node.id,
|
|
@@ -1611,7 +1615,7 @@ function primMST(graph, getWeight) {
|
|
|
1611
1615
|
edge
|
|
1612
1616
|
});
|
|
1613
1617
|
}
|
|
1614
|
-
if (graph.
|
|
1618
|
+
if (graph.mode !== "directed") for (const eid of idx.inEdges.get(nodeId) ?? []) {
|
|
1615
1619
|
const ai = idx.edgeById.get(eid);
|
|
1616
1620
|
if (ai === void 0) continue;
|
|
1617
1621
|
const edge = graph.edges[ai];
|
|
@@ -1626,7 +1630,7 @@ function primMST(graph, getWeight) {
|
|
|
1626
1630
|
addEdgesOf(startId);
|
|
1627
1631
|
while (candidates.size > 0 && inMST.size < graph.nodes.length) {
|
|
1628
1632
|
const { edge } = candidates.pop();
|
|
1629
|
-
const targetId = graph.
|
|
1633
|
+
const targetId = graph.mode !== "directed" && inMST.has(edge.targetId) ? edge.sourceId : edge.targetId;
|
|
1630
1634
|
if (inMST.has(targetId)) continue;
|
|
1631
1635
|
inMST.add(targetId);
|
|
1632
1636
|
mstEdges.push(edge);
|
|
@@ -1675,7 +1679,7 @@ function getNeighborIds(graph, nodeId) {
|
|
|
1675
1679
|
const edgeIndex = idx.edgeById.get(edgeId);
|
|
1676
1680
|
if (edgeIndex !== void 0) neighbors.push(graph.edges[edgeIndex].targetId);
|
|
1677
1681
|
}
|
|
1678
|
-
if (graph.
|
|
1682
|
+
if (graph.mode !== "directed") for (const edgeId of idx.inEdges.get(nodeId) ?? []) {
|
|
1679
1683
|
const edgeIndex = idx.edgeById.get(edgeId);
|
|
1680
1684
|
if (edgeIndex !== void 0) neighbors.push(graph.edges[edgeIndex].sourceId);
|
|
1681
1685
|
}
|
|
@@ -1688,7 +1692,7 @@ function getIncomingIds(graph, nodeId) {
|
|
|
1688
1692
|
const edgeIndex = idx.edgeById.get(edgeId);
|
|
1689
1693
|
if (edgeIndex !== void 0) incoming.push(graph.edges[edgeIndex].sourceId);
|
|
1690
1694
|
}
|
|
1691
|
-
if (graph.
|
|
1695
|
+
if (graph.mode !== "directed") for (const edgeId of idx.outEdges.get(nodeId) ?? []) {
|
|
1692
1696
|
const edgeIndex = idx.edgeById.get(edgeId);
|
|
1693
1697
|
if (edgeIndex !== void 0) incoming.push(graph.edges[edgeIndex].targetId);
|
|
1694
1698
|
}
|
|
@@ -1741,7 +1745,7 @@ function getDegreeCentrality(graph) {
|
|
|
1741
1745
|
for (const node of graph.nodes) {
|
|
1742
1746
|
const outDegree = idx.outEdges.get(node.id)?.length ?? 0;
|
|
1743
1747
|
const inDegree = idx.inEdges.get(node.id)?.length ?? 0;
|
|
1744
|
-
const degree = graph.
|
|
1748
|
+
const degree = graph.mode !== "directed" ? new Set([...idx.outEdges.get(node.id) ?? [], ...idx.inEdges.get(node.id) ?? []]).size : outDegree + inDegree;
|
|
1745
1749
|
scores[node.id] = degree * scale;
|
|
1746
1750
|
}
|
|
1747
1751
|
return scores;
|
|
@@ -1841,9 +1845,9 @@ function getBetweennessCentrality(graph) {
|
|
|
1841
1845
|
}
|
|
1842
1846
|
const order = graph.nodes.length;
|
|
1843
1847
|
if (order <= 2) return scores;
|
|
1844
|
-
const scale = graph.
|
|
1848
|
+
const scale = graph.mode !== "directed" ? 1 / ((order - 1) * (order - 2) / 2) : 1 / ((order - 1) * (order - 2));
|
|
1845
1849
|
for (const nodeId of Object.keys(scores)) {
|
|
1846
|
-
if (graph.
|
|
1850
|
+
if (graph.mode !== "directed") scores[nodeId] /= 2;
|
|
1847
1851
|
scores[nodeId] *= scale;
|
|
1848
1852
|
}
|
|
1849
1853
|
return scores;
|
|
@@ -2314,11 +2318,11 @@ function getDegreeSignature(graph, nodeId) {
|
|
|
2314
2318
|
const idx = getIndex(graph);
|
|
2315
2319
|
const outDegree = idx.outEdges.get(nodeId)?.length ?? 0;
|
|
2316
2320
|
const inDegree = idx.inEdges.get(nodeId)?.length ?? 0;
|
|
2317
|
-
if (graph.
|
|
2321
|
+
if (graph.mode !== "directed") return `u:${new Set([...idx.outEdges.get(nodeId) ?? [], ...idx.inEdges.get(nodeId) ?? []]).size}`;
|
|
2318
2322
|
return `d:${inDegree}:${outDegree}`;
|
|
2319
2323
|
}
|
|
2320
2324
|
function getEdgesBetween(graph, sourceId, targetId) {
|
|
2321
|
-
if (graph.
|
|
2325
|
+
if (graph.mode !== "directed") return graph.edges.filter((edge) => edge.sourceId === sourceId && edge.targetId === targetId || edge.sourceId === targetId && edge.targetId === sourceId);
|
|
2322
2326
|
return graph.edges.filter((edge) => edge.sourceId === sourceId && edge.targetId === targetId);
|
|
2323
2327
|
}
|
|
2324
2328
|
function edgesAreCompatible(edgesA, edgesB, edgeMatch) {
|
|
@@ -2339,7 +2343,7 @@ function edgesAreCompatible(edgesA, edgesB, edgeMatch) {
|
|
|
2339
2343
|
* node and edge payloads.
|
|
2340
2344
|
*/
|
|
2341
2345
|
function isIsomorphic(graphA, graphB, options) {
|
|
2342
|
-
if (graphA.
|
|
2346
|
+
if (graphA.mode !== graphB.mode) return false;
|
|
2343
2347
|
if (graphA.nodes.length !== graphB.nodes.length) return false;
|
|
2344
2348
|
if (graphA.edges.length !== graphB.edges.length) return false;
|
|
2345
2349
|
const nodeMatch = options?.nodeMatch;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as TraversalOptions, O as SinglePathOptions, S as MSTOptions, T as PathOptions, _ as GraphNode, n as AllPairsShortestPathsOptions, p as GraphEdge, t as AStarOptions, u as Graph, y as GraphPath } from "./types-3-FS9NV2.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/algorithms/traversal.d.ts
|
|
4
4
|
declare function bfs<N>(graph: Graph<N>, startId: string): Generator<GraphNode<N>>;
|
package/dist/algorithms.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as genCycles, B as getStronglyConnectedComponents, C as getMinimumSpanningTree, D as getPostorders, E as getPostorder, F as getCycles, G as getTopologicalSort, H as bfs, I as getShortestPath, J as isConnected, K as hasPath, L as getShortestPaths, M as genSimplePaths, N as getAStarPath, O as getPreorder, P as getAllPairsShortestPaths, R as getSimplePath, S as getPageRank, T as genPreorders, U as dfs, V as joinPaths, W as getConnectedComponents, Y as isTree, _ as getDegreeCentrality, a as getBridges, b as getInDegreeCentrality, c as genGirvanNewmanCommunities, d as getLabelPropagationCommunities, f as getModularity, g as getClosenessCentrality, h as getBetweennessCentrality, i as getBiconnectedComponents, j as genShortestPaths, k as getPreorders, l as getGirvanNewmanCommunities, m as IterativeCentralityOptions, n as isIsomorphic, o as GirvanNewmanOptions, p as HITSResult, q as isAcyclic, r as getArticulationPoints, s as LabelPropagationOptions, t as IsomorphismOptions, u as getGreedyModularityCommunities, v as getEigenvectorCentrality, w as genPostorders, x as getOutDegreeCentrality, y as getHITS, z as getSimplePaths } from "./algorithms-
|
|
1
|
+
import { A as genCycles, B as getStronglyConnectedComponents, C as getMinimumSpanningTree, D as getPostorders, E as getPostorder, F as getCycles, G as getTopologicalSort, H as bfs, I as getShortestPath, J as isConnected, K as hasPath, L as getShortestPaths, M as genSimplePaths, N as getAStarPath, O as getPreorder, P as getAllPairsShortestPaths, R as getSimplePath, S as getPageRank, T as genPreorders, U as dfs, V as joinPaths, W as getConnectedComponents, Y as isTree, _ as getDegreeCentrality, a as getBridges, b as getInDegreeCentrality, c as genGirvanNewmanCommunities, d as getLabelPropagationCommunities, f as getModularity, g as getClosenessCentrality, h as getBetweennessCentrality, i as getBiconnectedComponents, j as genShortestPaths, k as getPreorders, l as getGirvanNewmanCommunities, m as IterativeCentralityOptions, n as isIsomorphic, o as GirvanNewmanOptions, p as HITSResult, q as isAcyclic, r as getArticulationPoints, s as LabelPropagationOptions, t as IsomorphismOptions, u as getGreedyModularityCommunities, v as getEigenvectorCentrality, w as genPostorders, x as getOutDegreeCentrality, y as getHITS, z as getSimplePaths } from "./algorithms-fTqmvhzP.mjs";
|
|
2
2
|
export { GirvanNewmanOptions, HITSResult, IsomorphismOptions, IterativeCentralityOptions, LabelPropagationOptions, 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 };
|
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-Ba7o7niK.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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-
|
|
2
|
-
import { n as toEdgeList, t as fromEdgeList } from "./edge-list-
|
|
1
|
+
import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-VsUaH9SJ.mjs";
|
|
2
|
+
import { n as toEdgeList, t as fromEdgeList } from "./edge-list-DP4otyPU.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/formats/converter/index.ts
|
|
5
5
|
/**
|
|
@@ -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",
|
|
@@ -23,12 +29,12 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
23
29
|
undirected: "full",
|
|
24
30
|
hierarchy: "full",
|
|
25
31
|
ports: "full",
|
|
26
|
-
visual: "
|
|
27
|
-
style: "
|
|
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,35 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
36
42
|
features: {
|
|
37
43
|
directed: "full",
|
|
38
44
|
undirected: "full",
|
|
39
|
-
hierarchy: "
|
|
45
|
+
hierarchy: "full",
|
|
40
46
|
ports: "full",
|
|
41
|
-
visual: "
|
|
42
|
-
style: "
|
|
47
|
+
visual: "full",
|
|
48
|
+
style: "full",
|
|
43
49
|
weight: "full",
|
|
44
|
-
roundTrip: "
|
|
50
|
+
roundTrip: "full"
|
|
51
|
+
},
|
|
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."]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "d2",
|
|
56
|
+
importPath: "@statelyai/graph/d2",
|
|
57
|
+
features: {
|
|
58
|
+
directed: "full",
|
|
59
|
+
undirected: "full",
|
|
60
|
+
hierarchy: "full",
|
|
61
|
+
ports: "full",
|
|
62
|
+
visual: "full",
|
|
63
|
+
style: "full",
|
|
64
|
+
weight: "none",
|
|
65
|
+
roundTrip: "full"
|
|
45
66
|
},
|
|
46
|
-
notes: [
|
|
67
|
+
notes: [
|
|
68
|
+
"Containers map to hierarchy; dot vs block declaration form is preserved per node.",
|
|
69
|
+
"sql_table/class fields map to ports; node.field connections round-trip as sourcePort/targetPort.",
|
|
70
|
+
"Per-edge connectors (->, <-, --, <->) map to edge.mode; the authored glyph is preserved in _d2.arrow.",
|
|
71
|
+
"vars/classes/imports are preserved on graph data; comments attach to the following entity (best-effort).",
|
|
72
|
+
"d2 has no native edge weight."
|
|
73
|
+
]
|
|
47
74
|
},
|
|
48
75
|
{
|
|
49
76
|
id: "dot",
|
|
@@ -84,11 +111,11 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
84
111
|
hierarchy: "full",
|
|
85
112
|
ports: "full",
|
|
86
113
|
visual: "full",
|
|
87
|
-
style: "
|
|
88
|
-
weight: "
|
|
89
|
-
roundTrip: "
|
|
114
|
+
style: "full",
|
|
115
|
+
weight: "full",
|
|
116
|
+
roundTrip: "full"
|
|
90
117
|
},
|
|
91
|
-
notes: ["
|
|
118
|
+
notes: ["ELK-native layout fields are preserved directly; graph, node, port, and edge metadata round-trip through reserved layout options."]
|
|
92
119
|
},
|
|
93
120
|
{
|
|
94
121
|
id: "gexf",
|
|
@@ -98,12 +125,16 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
98
125
|
undirected: "full",
|
|
99
126
|
hierarchy: "full",
|
|
100
127
|
ports: "full",
|
|
101
|
-
visual: "
|
|
102
|
-
style: "
|
|
128
|
+
visual: "full",
|
|
129
|
+
style: "full",
|
|
103
130
|
weight: "full",
|
|
104
|
-
roundTrip: "
|
|
131
|
+
roundTrip: "full"
|
|
105
132
|
},
|
|
106
|
-
notes: [
|
|
133
|
+
notes: [
|
|
134
|
+
"Custom attributes preserve graph, node, and edge metadata beyond the standard viz module.",
|
|
135
|
+
"Ports round-trip via custom node/edge attributes.",
|
|
136
|
+
"Per-edge directedness round-trips via the `type` edge attribute; bidirectional maps to directed."
|
|
137
|
+
]
|
|
107
138
|
},
|
|
108
139
|
{
|
|
109
140
|
id: "gml",
|
|
@@ -113,12 +144,12 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
113
144
|
undirected: "full",
|
|
114
145
|
hierarchy: "full",
|
|
115
146
|
ports: "full",
|
|
116
|
-
visual: "
|
|
117
|
-
style: "
|
|
147
|
+
visual: "full",
|
|
148
|
+
style: "full",
|
|
118
149
|
weight: "full",
|
|
119
|
-
roundTrip: "
|
|
150
|
+
roundTrip: "full"
|
|
120
151
|
},
|
|
121
|
-
notes: ["GML
|
|
152
|
+
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
153
|
},
|
|
123
154
|
{
|
|
124
155
|
id: "graphml",
|
|
@@ -133,7 +164,11 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
133
164
|
weight: "full",
|
|
134
165
|
roundTrip: "partial"
|
|
135
166
|
},
|
|
136
|
-
notes: [
|
|
167
|
+
notes: [
|
|
168
|
+
"GraphML attribute fidelity is good, but not every extension is represented.",
|
|
169
|
+
"Ports round-trip through node and edge `<data>` fields.",
|
|
170
|
+
"Per-edge directedness round-trips via the `directed` edge attribute; bidirectional maps to directed."
|
|
171
|
+
]
|
|
137
172
|
},
|
|
138
173
|
{
|
|
139
174
|
id: "jgf",
|
|
@@ -143,12 +178,12 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
143
178
|
undirected: "full",
|
|
144
179
|
hierarchy: "full",
|
|
145
180
|
ports: "full",
|
|
146
|
-
visual: "
|
|
147
|
-
style: "
|
|
181
|
+
visual: "full",
|
|
182
|
+
style: "full",
|
|
148
183
|
weight: "full",
|
|
149
|
-
roundTrip: "
|
|
184
|
+
roundTrip: "full"
|
|
150
185
|
},
|
|
151
|
-
notes: ["JGF preserves
|
|
186
|
+
notes: ["JGF preserves graph, node, and edge metadata via `metadata` objects.", "Ports round-trip through node and edge metadata."]
|
|
152
187
|
},
|
|
153
188
|
{
|
|
154
189
|
id: "tgf",
|
|
@@ -171,14 +206,14 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
171
206
|
features: {
|
|
172
207
|
directed: "full",
|
|
173
208
|
undirected: "partial",
|
|
174
|
-
hierarchy: "
|
|
209
|
+
hierarchy: "full",
|
|
175
210
|
ports: "full",
|
|
176
211
|
visual: "full",
|
|
177
|
-
style: "
|
|
178
|
-
weight: "
|
|
179
|
-
roundTrip: "
|
|
212
|
+
style: "full",
|
|
213
|
+
weight: "full",
|
|
214
|
+
roundTrip: "full"
|
|
180
215
|
},
|
|
181
|
-
notes: ["
|
|
216
|
+
notes: ["xyflow-native fields are preserved directly; graph, node, edge, style, weight, and port metadata round-trip through reserved data fields."]
|
|
182
217
|
},
|
|
183
218
|
{
|
|
184
219
|
id: "mermaid/block",
|
|
@@ -296,9 +331,9 @@ const FORMAT_SUPPORT_MATRIX = [
|
|
|
296
331
|
visual: "partial",
|
|
297
332
|
style: "partial",
|
|
298
333
|
weight: "none",
|
|
299
|
-
roundTrip: "
|
|
334
|
+
roundTrip: "full"
|
|
300
335
|
},
|
|
301
|
-
notes: ["State notes
|
|
336
|
+
notes: ["State-specific syntax such as notes, classes, descriptions, directions, hierarchy, and parallel regions round-trips through node and graph data."]
|
|
302
337
|
}
|
|
303
338
|
];
|
|
304
339
|
function getFormatSupportEntry(id) {
|