@statelyai/graph 0.3.1 → 0.4.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 (40) hide show
  1. package/README.md +12 -11
  2. package/dist/{algorithms-NWSB2RWj.mjs → algorithms-CnTmuX9t.mjs} +11 -6
  3. package/dist/algorithms.d.mts +1 -1
  4. package/dist/algorithms.mjs +1 -1
  5. package/dist/{converter-CchokMDg.mjs → converter-C5DlzzHs.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 +1 -1
  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 +4 -4
  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/gexf/index.d.mts +1 -1
  19. package/dist/formats/gexf/index.mjs +5 -5
  20. package/dist/formats/gml/index.d.mts +1 -1
  21. package/dist/formats/gml/index.mjs +3 -3
  22. package/dist/formats/graphml/index.d.mts +1 -1
  23. package/dist/formats/graphml/index.mjs +2 -2
  24. package/dist/formats/jgf/index.d.mts +1 -1
  25. package/dist/formats/jgf/index.mjs +4 -4
  26. package/dist/formats/mermaid/index.d.mts +1 -1
  27. package/dist/formats/mermaid/index.mjs +5 -5
  28. package/dist/formats/tgf/index.d.mts +1 -1
  29. package/dist/formats/tgf/index.mjs +1 -1
  30. package/dist/formats/xyflow/index.d.mts +73 -0
  31. package/dist/formats/xyflow/index.mjs +133 -0
  32. package/dist/index.d.mts +1 -1
  33. package/dist/index.mjs +5 -5
  34. package/dist/{indexing-eNDrXdDA.mjs → indexing-DitHphT7.mjs} +6 -5
  35. package/dist/queries.d.mts +2 -2
  36. package/dist/queries.mjs +9 -9
  37. package/dist/{types-BDXC1O5b.d.mts → types-Bq_fmLwW.d.mts} +14 -3
  38. package/package.json +3 -1
  39. /package/dist/{adjacency-list-ITO40kmn.mjs → adjacency-list-Bv4tfiM3.mjs} +0 -0
  40. /package/dist/{edge-list-CgX6bBIF.mjs → edge-list-R1SUbHwe.mjs} +0 -0
package/README.md CHANGED
@@ -4,17 +4,6 @@ A TypeScript graph library built on plain JSON objects. Supports directed/undire
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
- ## Why this library?
8
-
9
- 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.
10
-
11
- 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.
12
-
13
- ```
14
- GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSON() → render
15
- ```
16
-
17
- 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.
18
7
 
19
8
  ## Install
20
9
 
@@ -126,6 +115,18 @@ const back = cytoscapeConverter.from(cyto);
126
115
  const myConverter = createFormatConverter(myToFn, myFromFn);
127
116
  ```
128
117
 
118
+ ## Why this library?
119
+
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.
121
+
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.
123
+
124
+ ```
125
+ GEXF file → fromGEXF() → Graph → run algorithms, mutate → toCytoscapeJSON() → render
126
+ ```
127
+
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
+
129
130
  ## API
130
131
 
131
132
  ### Graph Creation
@@ -1,12 +1,14 @@
1
- import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdge, o as invalidateIndex, r as indexAddNode, t as getIndex } from "./indexing-eNDrXdDA.mjs";
1
+ import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdge, o as invalidateIndex, r as indexAddNode, t as getIndex } from "./indexing-DitHphT7.mjs";
2
2
 
3
3
  //#region src/graph.ts
4
4
  function resolveNode(config) {
5
+ if (!config.id) throw new Error("Node id must be a non-empty string");
6
+ if (config.parentId === "") throw new Error("Node parentId must be a non-empty string");
5
7
  const node = {
6
8
  type: "node",
7
9
  id: config.id,
8
- parentId: config.parentId ?? null,
9
- initialNodeId: config.initialNodeId ?? null,
10
+ ...config.parentId !== void 0 && { parentId: config.parentId ?? null },
11
+ ...config.initialNodeId !== void 0 && { initialNodeId: config.initialNodeId ?? null },
10
12
  label: config.label ?? "",
11
13
  data: config.data
12
14
  };
@@ -20,6 +22,9 @@ function resolveNode(config) {
20
22
  return node;
21
23
  }
22
24
  function resolveEdge(config) {
25
+ if (!config.id) throw new Error("Edge id must be a non-empty string");
26
+ if (!config.sourceId) throw new Error("Edge sourceId must be a non-empty string");
27
+ if (!config.targetId) throw new Error("Edge targetId must be a non-empty string");
23
28
  const edge = {
24
29
  type: "edge",
25
30
  id: config.id,
@@ -251,10 +256,10 @@ function hasEdge(graph, id) {
251
256
  * ```
252
257
  */
253
258
  function addNode(graph, config) {
259
+ const node = resolveNode(config);
254
260
  const idx = getIndex(graph);
255
261
  if (idx.nodeById.has(config.id)) throw new Error(`Node "${config.id}" already exists`);
256
- if (config.parentId != null && !idx.nodeById.has(config.parentId)) throw new Error(`Parent node "${config.parentId}" does not exist`);
257
- const node = resolveNode(config);
262
+ if (config.parentId && !idx.nodeById.has(config.parentId)) throw new Error(`Parent node "${config.parentId}" does not exist`);
258
263
  indexAddNode(idx, node, graph.nodes.push(node) - 1);
259
264
  return node;
260
265
  }
@@ -270,11 +275,11 @@ function addNode(graph, config) {
270
275
  * ```
271
276
  */
272
277
  function addEdge(graph, config) {
278
+ const edge = resolveEdge(config);
273
279
  const idx = getIndex(graph);
274
280
  if (idx.edgeById.has(config.id)) throw new Error(`Edge "${config.id}" already exists`);
275
281
  if (!idx.nodeById.has(config.sourceId)) throw new Error(`Source node "${config.sourceId}" does not exist`);
276
282
  if (!idx.nodeById.has(config.targetId)) throw new Error(`Target node "${config.targetId}" does not exist`);
277
- const edge = resolveEdge(config);
278
283
  indexAddEdge(idx, edge, graph.edges.push(edge) - 1);
279
284
  return edge;
280
285
  }
@@ -1,4 +1,4 @@
1
- import { C as TraversalOptions, _ as MSTOptions, b as PathOptions, c as Graph, h as GraphPath, p as GraphNode, t as AllPairsShortestPathsOptions, x as SinglePathOptions } from "./types-BDXC1O5b.mjs";
1
+ import { C as TraversalOptions, _ as MSTOptions, b as PathOptions, c as Graph, h as GraphPath, p as GraphNode, t as AllPairsShortestPathsOptions, x as SinglePathOptions } from "./types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/algorithms.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { C as isAcyclic, E as joinPaths, S as hasPath, T as isTree, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths } from "./algorithms-NWSB2RWj.mjs";
1
+ import { C as isAcyclic, E as joinPaths, S as hasPath, T as isTree, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths } from "./algorithms-CnTmuX9t.mjs";
2
2
 
3
3
  export { bfs, dfs, genCycles, genPostorders, genPreorders, genShortestPaths, genSimplePaths, getAllPairsShortestPaths, getConnectedComponents, getCycles, getMinimumSpanningTree, getPostorder, getPostorders, getPreorder, getPreorders, getShortestPath, getShortestPaths, getSimplePath, getSimplePaths, getStronglyConnectedComponents, getTopologicalSort, hasPath, isAcyclic, isConnected, isTree, joinPaths };
@@ -1,5 +1,5 @@
1
- import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-ITO40kmn.mjs";
2
- import { n as toEdgeList, t as fromEdgeList } from "./edge-list-CgX6bBIF.mjs";
1
+ import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-Bv4tfiM3.mjs";
2
+ import { n as toEdgeList, t as fromEdgeList } from "./edge-list-R1SUbHwe.mjs";
3
3
 
4
4
  //#region src/formats/converter/index.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { c as Graph } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/adjacency-list/index.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { n as toAdjacencyList, t as fromAdjacencyList } from "../../adjacency-list-ITO40kmn.mjs";
1
+ import { n as toAdjacencyList, t as fromAdjacencyList } from "../../adjacency-list-Bv4tfiM3.mjs";
2
2
 
3
3
  export { fromAdjacencyList, toAdjacencyList };
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/converter/index.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { n as createFormatConverter, r as edgeListConverter, t as adjacencyListConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter, r as edgeListConverter, t as adjacencyListConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  export { adjacencyListConverter, createFormatConverter, edgeListConverter };
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/cytoscape/index.d.ts
4
4
  interface CytoscapeNode {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  //#region src/formats/cytoscape/index.ts
4
4
  /**
@@ -22,7 +22,7 @@ function toCytoscapeJSON(graph) {
22
22
  const graphData = {};
23
23
  if (graph.id) graphData.id = graph.id;
24
24
  graphData.type = graph.type;
25
- if (graph.initialNodeId !== null) graphData.initialNodeId = graph.initialNodeId;
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
28
  return {
@@ -30,9 +30,9 @@ function toCytoscapeJSON(graph) {
30
30
  elements: {
31
31
  nodes: graph.nodes.map((n) => {
32
32
  const data = { id: n.id };
33
- if (n.parentId !== null) data.parent = n.parentId;
33
+ if (n.parentId) data.parent = n.parentId;
34
34
  if (n.label) data.label = n.label;
35
- if (n.initialNodeId !== null) data.initialNodeId = n.initialNodeId;
35
+ if (n.initialNodeId) data.initialNodeId = n.initialNodeId;
36
36
  if (n.data !== void 0) data.nodeData = n.data;
37
37
  if (n.width !== void 0) data.width = n.width;
38
38
  if (n.height !== void 0) data.height = n.height;
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/d3/index.d.ts
4
4
  interface D3Node {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  //#region src/formats/d3/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/dot/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
  import parse from "dotparser";
3
3
 
4
4
  //#region src/formats/dot/index.ts
@@ -1,4 +1,4 @@
1
- import { c as Graph } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/edge-list/index.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { n as toEdgeList, t as fromEdgeList } from "../../edge-list-CgX6bBIF.mjs";
1
+ import { n as toEdgeList, t as fromEdgeList } from "../../edge-list-R1SUbHwe.mjs";
2
2
 
3
3
  export { fromEdgeList, toEdgeList };
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/gexf/index.d.ts
4
4
  declare function toGEXF(graph: Graph): string;
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
3
3
 
4
4
  //#region src/formats/gexf/index.ts
@@ -32,11 +32,11 @@ function toGEXF(graph) {
32
32
  }];
33
33
  const nodes = graph.nodes.map((n) => {
34
34
  const attvalues = [];
35
- if (n.parentId !== null) attvalues.push({
35
+ if (n.parentId) attvalues.push({
36
36
  "@_for": "a_parentId",
37
37
  "@_value": n.parentId
38
38
  });
39
- if (n.initialNodeId !== null) attvalues.push({
39
+ if (n.initialNodeId) attvalues.push({
40
40
  "@_for": "a_initialNodeId",
41
41
  "@_value": n.initialNodeId
42
42
  });
@@ -52,7 +52,7 @@ function toGEXF(graph) {
52
52
  "@_id": n.id,
53
53
  "@_label": n.label || n.id
54
54
  };
55
- if (n.parentId !== null) node["@_pid"] = n.parentId;
55
+ if (n.parentId) node["@_pid"] = n.parentId;
56
56
  if (attvalues.length > 0) node.attvalues = { attvalue: attvalues };
57
57
  if (n.color) {
58
58
  const hex$1 = n.color.replace("#", "");
@@ -91,7 +91,7 @@ function toGEXF(graph) {
91
91
  return edge;
92
92
  });
93
93
  const graphData = [];
94
- if (graph.initialNodeId !== null) graphData.push({
94
+ if (graph.initialNodeId) graphData.push({
95
95
  "@_for": "a_initialNodeId",
96
96
  "@_value": graph.initialNodeId
97
97
  });
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/gml/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  //#region src/formats/gml/index.ts
4
4
  /**
@@ -30,7 +30,7 @@ function toGML(graph) {
30
30
  if (graph.id) lines.push(` id ${gmlString(graph.id)}`);
31
31
  const childrenMap = /* @__PURE__ */ new Map();
32
32
  for (const node of graph.nodes) {
33
- const pid = node.parentId;
33
+ const pid = node.parentId ?? null;
34
34
  if (!childrenMap.has(pid)) childrenMap.set(pid, []);
35
35
  childrenMap.get(pid).push(node);
36
36
  }
@@ -38,7 +38,7 @@ function toGML(graph) {
38
38
  lines.push(`${indent}node [`);
39
39
  lines.push(`${indent} id ${gmlString(node.id)}`);
40
40
  if (node.label) lines.push(`${indent} label ${gmlString(node.label)}`);
41
- if (node.initialNodeId !== null) lines.push(`${indent} initialNodeId ${gmlString(node.initialNodeId)}`);
41
+ if (node.initialNodeId) lines.push(`${indent} initialNodeId ${gmlString(node.initialNodeId)}`);
42
42
  if (node.data !== void 0) lines.push(`${indent} data ${gmlString(JSON.stringify(node.data))}`);
43
43
  if (node.shape) lines.push(`${indent} shape ${gmlString(node.shape)}`);
44
44
  if (node.color) lines.push(`${indent} color ${gmlString(node.color)}`);
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/graphml/index.d.ts
4
4
  declare function toGraphML(graph: Graph): string;
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
3
3
 
4
4
  //#region src/formats/graphml/index.ts
@@ -72,7 +72,7 @@ function toGraphML(graph) {
72
72
  "@_key": "label",
73
73
  "#text": n.label
74
74
  });
75
- if (n.parentId !== null) data.push({
75
+ if (n.parentId) data.push({
76
76
  "@_key": "parentId",
77
77
  "#text": n.parentId
78
78
  });
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/jgf/index.d.ts
4
4
  interface JGFNode {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  //#region src/formats/jgf/index.ts
4
4
  /**
@@ -20,7 +20,7 @@ import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
20
20
  */
21
21
  function toJGF(graph) {
22
22
  const metadata = {};
23
- if (graph.initialNodeId !== null) metadata.initialNodeId = graph.initialNodeId;
23
+ if (graph.initialNodeId) metadata.initialNodeId = graph.initialNodeId;
24
24
  if (graph.data !== void 0) metadata.data = graph.data;
25
25
  if (graph.direction) metadata.direction = graph.direction;
26
26
  return { graph: {
@@ -29,8 +29,8 @@ function toJGF(graph) {
29
29
  ...Object.keys(metadata).length > 0 && { metadata },
30
30
  nodes: graph.nodes.map((n) => {
31
31
  const meta = {};
32
- if (n.parentId !== null) meta.parentId = n.parentId;
33
- if (n.initialNodeId !== null) meta.initialNodeId = n.initialNodeId;
32
+ if (n.parentId) meta.parentId = n.parentId;
33
+ if (n.initialNodeId) meta.initialNodeId = n.initialNodeId;
34
34
  if (n.data !== void 0) meta.data = n.data;
35
35
  if (n.x !== void 0) meta.x = n.x;
36
36
  if (n.y !== void 0) meta.y = n.y;
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/mermaid/sequence.d.ts
4
4
  interface SequenceNodeData {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  //#region src/formats/mermaid/shared.ts
4
4
  const MERMAID_TO_DIRECTION = {
@@ -1097,7 +1097,7 @@ function toMermaidFlowchart(graph) {
1097
1097
  }
1098
1098
  const childrenMap = /* @__PURE__ */ new Map();
1099
1099
  for (const node of graph.nodes) {
1100
- const pid = node.parentId;
1100
+ const pid = node.parentId ?? null;
1101
1101
  if (!childrenMap.has(pid)) childrenMap.set(pid, []);
1102
1102
  childrenMap.get(pid).push(node);
1103
1103
  }
@@ -1304,7 +1304,7 @@ function toMermaidState(graph) {
1304
1304
  const lines = ["stateDiagram-v2"];
1305
1305
  const childrenMap = /* @__PURE__ */ new Map();
1306
1306
  for (const node of graph.nodes) {
1307
- const pid = node.parentId;
1307
+ const pid = node.parentId ?? null;
1308
1308
  if (!childrenMap.has(pid)) childrenMap.set(pid, []);
1309
1309
  childrenMap.get(pid).push(node);
1310
1310
  }
@@ -1995,7 +1995,7 @@ function toMermaidMindmap(graph) {
1995
1995
  const lines = ["mindmap"];
1996
1996
  const childrenMap = /* @__PURE__ */ new Map();
1997
1997
  for (const node of graph.nodes) {
1998
- const pid = node.parentId;
1998
+ const pid = node.parentId ?? null;
1999
1999
  if (!childrenMap.has(pid)) childrenMap.set(pid, []);
2000
2000
  childrenMap.get(pid).push(node);
2001
2001
  }
@@ -2198,7 +2198,7 @@ function toMermaidBlock(graph) {
2198
2198
  if (graph.data?.columns) lines.push(` columns ${graph.data.columns}`);
2199
2199
  const childrenMap = /* @__PURE__ */ new Map();
2200
2200
  for (const node of graph.nodes) {
2201
- const pid = node.parentId;
2201
+ const pid = node.parentId ?? null;
2202
2202
  if (!childrenMap.has(pid)) childrenMap.set(pid, []);
2203
2203
  childrenMap.get(pid).push(node);
2204
2204
  }
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/formats/tgf/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
+ import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
2
2
 
3
3
  //#region src/formats/tgf/index.ts
4
4
  /**
@@ -0,0 +1,73 @@
1
+ import { D as VisualGraphFormatConverter, T as VisualGraph } from "../../types-Bq_fmLwW.mjs";
2
+ import { EdgeBase, NodeBase } from "@xyflow/system";
3
+
4
+ //#region src/formats/xyflow/index.d.ts
5
+ /** xyflow Node — re-exported from `@xyflow/system`. */
6
+ type XYFlowNode<TNodeData extends Record<string, unknown> = Record<string, unknown>> = NodeBase<TNodeData>;
7
+ /** xyflow Edge — re-exported from `@xyflow/system`. */
8
+ type XYFlowEdge<TEdgeData extends Record<string, unknown> = Record<string, unknown>> = EdgeBase<TEdgeData>;
9
+ interface XYFlow<TNodeData extends Record<string, unknown> = Record<string, unknown>, TEdgeData extends Record<string, unknown> = Record<string, unknown>> {
10
+ nodes: XYFlowNode<TNodeData>[];
11
+ edges: XYFlowEdge<TEdgeData>[];
12
+ }
13
+ /**
14
+ * Converts a visual graph to xyflow (React Flow / Svelte Flow) format.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { createVisualGraph } from '@statelyai/graph';
19
+ * import { toXYFlow } from '@statelyai/graph/formats/xyflow';
20
+ *
21
+ * const graph = createVisualGraph({
22
+ * nodes: [
23
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
24
+ * { id: 'b', x: 200, y: 100, width: 100, height: 50 },
25
+ * ],
26
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
27
+ * });
28
+ *
29
+ * const flow = toXYFlow(graph);
30
+ * // { nodes: [...], edges: [...] }
31
+ * ```
32
+ */
33
+ declare function toXYFlow(graph: VisualGraph): XYFlow;
34
+ /**
35
+ * Parses an xyflow (React Flow / Svelte Flow) object into a visual graph.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { fromXYFlow } from '@statelyai/graph/formats/xyflow';
40
+ *
41
+ * const graph = fromXYFlow({
42
+ * nodes: [
43
+ * { id: 'a', position: { x: 0, y: 0 }, data: {} },
44
+ * { id: 'b', position: { x: 200, y: 100 }, data: {} },
45
+ * ],
46
+ * edges: [{ id: 'e0', source: 'a', target: 'b' }],
47
+ * });
48
+ * ```
49
+ */
50
+ declare function fromXYFlow(flow: XYFlow): VisualGraph;
51
+ /**
52
+ * Bidirectional converter for xyflow (React Flow / Svelte Flow) format.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { createVisualGraph } from '@statelyai/graph';
57
+ * import { xyflowConverter } from '@statelyai/graph/formats/xyflow';
58
+ *
59
+ * const graph = createVisualGraph({
60
+ * nodes: [
61
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
62
+ * { id: 'b', x: 200, y: 100, width: 100, height: 50 },
63
+ * ],
64
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
65
+ * });
66
+ *
67
+ * const flow = xyflowConverter.to(graph);
68
+ * const roundTripped = xyflowConverter.from(flow);
69
+ * ```
70
+ */
71
+ declare const xyflowConverter: VisualGraphFormatConverter<XYFlow>;
72
+ //#endregion
73
+ export { XYFlow, XYFlowEdge, XYFlowNode, fromXYFlow, toXYFlow, xyflowConverter };
@@ -0,0 +1,133 @@
1
+ //#region src/formats/xyflow/index.ts
2
+ /**
3
+ * Converts a visual graph to xyflow (React Flow / Svelte Flow) format.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { createVisualGraph } from '@statelyai/graph';
8
+ * import { toXYFlow } from '@statelyai/graph/formats/xyflow';
9
+ *
10
+ * const graph = createVisualGraph({
11
+ * nodes: [
12
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
13
+ * { id: 'b', x: 200, y: 100, width: 100, height: 50 },
14
+ * ],
15
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
16
+ * });
17
+ *
18
+ * const flow = toXYFlow(graph);
19
+ * // { nodes: [...], edges: [...] }
20
+ * ```
21
+ */
22
+ function toXYFlow(graph) {
23
+ return {
24
+ nodes: graph.nodes.map((n) => {
25
+ const node = {
26
+ id: n.id,
27
+ position: {
28
+ x: n.x,
29
+ y: n.y
30
+ },
31
+ data: n.data ?? {}
32
+ };
33
+ if (n.parentId) node.parentId = n.parentId;
34
+ if (n.shape) node.type = n.shape;
35
+ if (n.width) node.width = n.width;
36
+ if (n.height) node.height = n.height;
37
+ return node;
38
+ }),
39
+ edges: graph.edges.map((e) => {
40
+ const edge = {
41
+ id: e.id,
42
+ source: e.sourceId,
43
+ target: e.targetId
44
+ };
45
+ if (e.data !== void 0) edge.data = e.data;
46
+ if (e.label) edge.data = {
47
+ ...edge.data,
48
+ label: e.label
49
+ };
50
+ return edge;
51
+ })
52
+ };
53
+ }
54
+ /**
55
+ * Parses an xyflow (React Flow / Svelte Flow) object into a visual graph.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { fromXYFlow } from '@statelyai/graph/formats/xyflow';
60
+ *
61
+ * const graph = fromXYFlow({
62
+ * nodes: [
63
+ * { id: 'a', position: { x: 0, y: 0 }, data: {} },
64
+ * { id: 'b', position: { x: 200, y: 100 }, data: {} },
65
+ * ],
66
+ * edges: [{ id: 'e0', source: 'a', target: 'b' }],
67
+ * });
68
+ * ```
69
+ */
70
+ function fromXYFlow(flow) {
71
+ if (!flow || typeof flow !== "object") throw new Error("XYFlow: expected an object");
72
+ if (!Array.isArray(flow.nodes)) throw new Error("XYFlow: \"nodes\" must be an array");
73
+ if (!Array.isArray(flow.edges)) throw new Error("XYFlow: \"edges\" must be an array");
74
+ return {
75
+ id: "",
76
+ type: "directed",
77
+ initialNodeId: null,
78
+ data: void 0,
79
+ direction: "down",
80
+ nodes: flow.nodes.map((n) => ({
81
+ type: "node",
82
+ id: n.id,
83
+ parentId: n.parentId ?? null,
84
+ initialNodeId: null,
85
+ label: "",
86
+ data: n.data,
87
+ x: n.position.x,
88
+ y: n.position.y,
89
+ width: n.measured?.width ?? n.width ?? n.initialWidth ?? 0,
90
+ height: n.measured?.height ?? n.height ?? n.initialHeight ?? 0,
91
+ ...n.type && { shape: n.type }
92
+ })),
93
+ edges: flow.edges.map((e, i) => ({
94
+ type: "edge",
95
+ id: e.id ?? `e${i}`,
96
+ sourceId: e.source,
97
+ targetId: e.target,
98
+ label: e.data?.label?.toString() ?? "",
99
+ data: e.data,
100
+ x: 0,
101
+ y: 0,
102
+ width: 0,
103
+ height: 0
104
+ }))
105
+ };
106
+ }
107
+ /**
108
+ * Bidirectional converter for xyflow (React Flow / Svelte Flow) format.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * import { createVisualGraph } from '@statelyai/graph';
113
+ * import { xyflowConverter } from '@statelyai/graph/formats/xyflow';
114
+ *
115
+ * const graph = createVisualGraph({
116
+ * nodes: [
117
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
118
+ * { id: 'b', x: 200, y: 100, width: 100, height: 50 },
119
+ * ],
120
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
121
+ * });
122
+ *
123
+ * const flow = xyflowConverter.to(graph);
124
+ * const roundTripped = xyflowConverter.from(flow);
125
+ * ```
126
+ */
127
+ const xyflowConverter = {
128
+ to: toXYFlow,
129
+ from: fromXYFlow
130
+ };
131
+
132
+ //#endregion
133
+ export { fromXYFlow, toXYFlow, xyflowConverter };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as TraversalOptions, D as VisualNode, E as VisualGraphConfig, S as TransitionOptions, T as VisualGraph, _ as MSTOptions, a as EntitiesConfig, b as PathOptions, c as Graph, d as GraphEdge, f as GraphFormatConverter, g as GraphStep, h as GraphPath, i as EdgeConfig, l as GraphConfig, m as GraphPatch, n as DeleteNodeOptions, o as EntitiesUpdate, p as GraphNode, r as EdgeChange, s as EntityRect, t as AllPairsShortestPathsOptions, u as GraphDiff, v as NodeChange, w as VisualEdge, x as SinglePathOptions, y as NodeConfig } from "./types-BDXC1O5b.mjs";
1
+ import { C as TraversalOptions, E as VisualGraphConfig, O as VisualNode, S as TransitionOptions, T as VisualGraph, _ as MSTOptions, a as EntitiesConfig, b as PathOptions, c as Graph, d as GraphEdge, f as GraphFormatConverter, g as GraphStep, h as GraphPath, i as EdgeConfig, l as GraphConfig, m as GraphPatch, n as DeleteNodeOptions, o as EntitiesUpdate, p as GraphNode, r as EdgeChange, s as EntityRect, t as AllPairsShortestPathsOptions, u as GraphDiff, v as NodeChange, w as VisualEdge, x as SinglePathOptions, y as NodeConfig } from "./types-Bq_fmLwW.mjs";
2
2
  import { bfs, dfs, genCycles, genPostorders, genPreorders, genShortestPaths, genSimplePaths, getAllPairsShortestPaths, getConnectedComponents, getCycles, getMinimumSpanningTree, getPostorder, getPostorders, getPreorder, getPreorders, getShortestPath, getShortestPaths, getSimplePath, getSimplePaths, getStronglyConnectedComponents, getTopologicalSort, hasPath, isAcyclic, isConnected, isTree, joinPaths } from "./algorithms.mjs";
3
3
  import { createFormatConverter } from "./formats/converter/index.mjs";
4
4
  import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
- import { o as invalidateIndex, t as getIndex } from "./indexing-eNDrXdDA.mjs";
2
- import { A as addNode, B as hasNode, C as isAcyclic, D as GraphInstance, E as joinPaths, F as deleteEntities, H as updateEntities, I as deleteNode, L as getEdge, M as createGraphFromTransition, N as createVisualGraph, O as addEdge, P as deleteEdge, R as getNode, S as hasPath, T as isTree, U as updateNode, V as updateEdge, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, j as createGraph, k as addEntities, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths, z as hasEdge } from "./algorithms-NWSB2RWj.mjs";
1
+ import { o as invalidateIndex, t as getIndex } from "./indexing-DitHphT7.mjs";
2
+ import { A as addNode, B as hasNode, C as isAcyclic, D as GraphInstance, E as joinPaths, F as deleteEntities, H as updateEntities, I as deleteNode, L as getEdge, M as createGraphFromTransition, N as createVisualGraph, O as addEdge, P as deleteEdge, R as getNode, S as hasPath, T as isTree, U as updateNode, V as updateEdge, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, j as createGraph, k as addEntities, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths, z as hasEdge } from "./algorithms-CnTmuX9t.mjs";
3
3
  import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
4
- import { n as createFormatConverter } from "./converter-CchokMDg.mjs";
4
+ import { n as createFormatConverter } from "./converter-C5DlzzHs.mjs";
5
5
 
6
6
  //#region src/diff.ts
7
7
  function nodeToConfig(node) {
8
8
  const config = { id: node.id };
9
- if (node.parentId !== null) config.parentId = node.parentId;
10
- if (node.initialNodeId !== null) config.initialNodeId = node.initialNodeId;
9
+ if (node.parentId) config.parentId = node.parentId;
10
+ if (node.initialNodeId) config.initialNodeId = node.initialNodeId;
11
11
  if (node.label !== "") config.label = node.label;
12
12
  if (node.data !== void 0) config.data = node.data;
13
13
  if (node.x !== void 0) config.x = node.x;
@@ -53,7 +53,7 @@ function buildIndex(graph) {
53
53
  nodeById.set(n.id, i);
54
54
  outEdges.set(n.id, []);
55
55
  inEdges.set(n.id, []);
56
- const parent = n.parentId;
56
+ const parent = n.parentId ?? null;
57
57
  if (!childNodes.has(parent)) childNodes.set(parent, []);
58
58
  childNodes.get(parent).push(n.id);
59
59
  }
@@ -77,7 +77,7 @@ function indexAddNode(idx, node, arrayIndex) {
77
77
  idx.nodeById.set(node.id, arrayIndex);
78
78
  idx.outEdges.set(node.id, []);
79
79
  idx.inEdges.set(node.id, []);
80
- const parent = node.parentId;
80
+ const parent = node.parentId ?? null;
81
81
  if (!idx.childNodes.has(parent)) idx.childNodes.set(parent, []);
82
82
  idx.childNodes.get(parent).push(node.id);
83
83
  idx.nodeCount++;
@@ -90,13 +90,14 @@ function indexAddEdge(idx, edge, arrayIndex) {
90
90
  }
91
91
  /** Update childNodes index when a node's parentId changes. */
92
92
  function indexReparentNode(idx, nodeId, oldParentId, newParentId) {
93
- const oldSiblings = idx.childNodes.get(oldParentId);
93
+ const oldSiblings = idx.childNodes.get(oldParentId ?? null);
94
94
  if (oldSiblings) {
95
95
  const pos = oldSiblings.indexOf(nodeId);
96
96
  if (pos !== -1) oldSiblings.splice(pos, 1);
97
97
  }
98
- if (!idx.childNodes.has(newParentId)) idx.childNodes.set(newParentId, []);
99
- idx.childNodes.get(newParentId).push(nodeId);
98
+ const np = newParentId ?? null;
99
+ if (!idx.childNodes.has(np)) idx.childNodes.set(np, []);
100
+ idx.childNodes.get(np).push(nodeId);
100
101
  }
101
102
  /** Update adjacency lists when an edge's sourceId/targetId changes. */
102
103
  function indexUpdateEdgeEndpoints(idx, edgeId, oldSourceId, oldTargetId, newSourceId, newTargetId) {
@@ -1,4 +1,4 @@
1
- import { c as Graph, d as GraphEdge, p as GraphNode } from "./types-BDXC1O5b.mjs";
1
+ import { c as Graph, d as GraphEdge, p as GraphNode } from "./types-Bq_fmLwW.mjs";
2
2
 
3
3
  //#region src/queries.d.ts
4
4
 
@@ -238,7 +238,7 @@ declare function getAncestors<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[
238
238
  */
239
239
  declare function getDescendants<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
240
240
  /**
241
- * Returns all root nodes (nodes with no parent, i.e. `parentId === null`).
241
+ * Returns all root nodes (nodes with no parent).
242
242
  *
243
243
  * @example
244
244
  * ```ts
package/dist/queries.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as getIndex } from "./indexing-eNDrXdDA.mjs";
1
+ import { t as getIndex } from "./indexing-DitHphT7.mjs";
2
2
 
3
3
  //#region src/queries.ts
4
4
  /**
@@ -293,7 +293,7 @@ function getParent(graph, nodeId) {
293
293
  const ni = idx.nodeById.get(nodeId);
294
294
  if (ni === void 0) return void 0;
295
295
  const node = graph.nodes[ni];
296
- if (node.parentId === null) return void 0;
296
+ if (!node.parentId) return void 0;
297
297
  const pi = idx.nodeById.get(node.parentId);
298
298
  return pi !== void 0 ? graph.nodes[pi] : void 0;
299
299
  }
@@ -319,7 +319,7 @@ function getAncestors(graph, nodeId) {
319
319
  let ni = idx.nodeById.get(nodeId);
320
320
  if (ni === void 0) return result;
321
321
  let current = graph.nodes[ni];
322
- while (current && current.parentId !== null) {
322
+ while (current && current.parentId) {
323
323
  const pi = idx.nodeById.get(current.parentId);
324
324
  if (pi === void 0) break;
325
325
  const p = graph.nodes[pi];
@@ -361,7 +361,7 @@ function getDescendants(graph, nodeId) {
361
361
  return result;
362
362
  }
363
363
  /**
364
- * Returns all root nodes (nodes with no parent, i.e. `parentId === null`).
364
+ * Returns all root nodes (nodes with no parent).
365
365
  *
366
366
  * @example
367
367
  * ```ts
@@ -440,7 +440,7 @@ function getDepth(graph, nodeId) {
440
440
  let ni = idx.nodeById.get(nodeId);
441
441
  if (ni === void 0) return -1;
442
442
  let current = graph.nodes[ni];
443
- while (current.parentId !== null) {
443
+ while (current.parentId) {
444
444
  d++;
445
445
  const pi = idx.nodeById.get(current.parentId);
446
446
  if (pi === void 0) break;
@@ -470,7 +470,7 @@ function getSiblings(graph, nodeId) {
470
470
  const ni = idx.nodeById.get(nodeId);
471
471
  if (ni === void 0) return [];
472
472
  const node = graph.nodes[ni];
473
- return (idx.childNodes.get(node.parentId) ?? []).filter((id) => id !== nodeId).map((id) => graph.nodes[idx.nodeById.get(id)]).filter(Boolean);
473
+ return (idx.childNodes.get(node.parentId ?? null) ?? []).filter((id) => id !== nodeId).map((id) => graph.nodes[idx.nodeById.get(id)]).filter(Boolean);
474
474
  }
475
475
  /**
476
476
  * Least Common Ancestor -- deepest proper ancestor of all given nodes.
@@ -500,7 +500,7 @@ function getLCA(graph, ...nodeIds) {
500
500
  let ni$1 = idx.nodeById.get(id);
501
501
  if (ni$1 === void 0) return result;
502
502
  let current = graph.nodes[ni$1];
503
- while (current.parentId !== null) {
503
+ while (current.parentId) {
504
504
  result.push(current.parentId);
505
505
  const pi = idx.nodeById.get(current.parentId);
506
506
  if (pi === void 0) break;
@@ -564,7 +564,7 @@ function getRelativeDistanceMap(graph, parentId) {
564
564
  let sourceId = null;
565
565
  if (parentId !== null) {
566
566
  const pi = idx.nodeById.get(parentId);
567
- if (pi !== void 0) sourceId = graph.nodes[pi].initialNodeId;
567
+ if (pi !== void 0) sourceId = graph.nodes[pi].initialNodeId ?? null;
568
568
  } else sourceId = graph.initialNodeId;
569
569
  if (!sourceId) return {};
570
570
  const siblingSet = new Set(idx.childNodes.get(parentId) ?? []);
@@ -637,7 +637,7 @@ function getRelativeDistance(graph, nodeId) {
637
637
  const ni = getIndex(graph).nodeById.get(nodeId);
638
638
  if (ni === void 0) return void 0;
639
639
  const node = graph.nodes[ni];
640
- return getRelativeDistanceMap(graph, node.parentId)[nodeId];
640
+ return getRelativeDistanceMap(graph, node.parentId ?? null)[nodeId];
641
641
  }
642
642
  /**
643
643
  * Nodes with no incoming edges (inDegree 0).
@@ -67,8 +67,8 @@ interface Graph<TNodeData = any, TEdgeData = any, TGraphData = any> {
67
67
  interface GraphNode<TNodeData = any> {
68
68
  type: 'node';
69
69
  id: string;
70
- parentId: string | null;
71
- initialNodeId: string | null;
70
+ parentId?: string | null;
71
+ initialNodeId?: string | null;
72
72
  label: string;
73
73
  data: TNodeData;
74
74
  x?: number;
@@ -234,6 +234,17 @@ interface GraphFormatConverter<TSerial> {
234
234
  /** Convert from the serialized format to a Graph. */
235
235
  from(input: TSerial): Graph;
236
236
  }
237
+ /**
238
+ * A bidirectional converter between `VisualGraph` and a serialized format.
239
+ *
240
+ * Use this for formats that carry position/size data (e.g. xyflow, cytoscape).
241
+ */
242
+ interface VisualGraphFormatConverter<TSerial> {
243
+ /** Convert a VisualGraph to the serialized format. */
244
+ to(graph: VisualGraph): TSerial;
245
+ /** Convert from the serialized format to a VisualGraph. */
246
+ from(input: TSerial): VisualGraph;
247
+ }
237
248
  interface TransitionOptions<TState, TEvent> {
238
249
  /** Initial state to begin BFS exploration from. */
239
250
  initialState: TState;
@@ -251,4 +262,4 @@ interface TransitionOptions<TState, TEvent> {
251
262
  id?: string;
252
263
  }
253
264
  //#endregion
254
- export { TraversalOptions as C, VisualNode as D, VisualGraphConfig as E, TransitionOptions as S, VisualGraph as T, MSTOptions as _, EntitiesConfig as a, PathOptions as b, Graph as c, GraphEdge as d, GraphFormatConverter as f, GraphStep as g, GraphPath as h, EdgeConfig as i, GraphConfig as l, GraphPatch as m, DeleteNodeOptions as n, EntitiesUpdate as o, GraphNode as p, EdgeChange as r, EntityRect as s, AllPairsShortestPathsOptions as t, GraphDiff as u, NodeChange as v, VisualEdge as w, SinglePathOptions as x, NodeConfig as y };
265
+ export { TraversalOptions as C, VisualGraphFormatConverter as D, VisualGraphConfig as E, VisualNode as O, TransitionOptions as S, VisualGraph as T, MSTOptions as _, EntitiesConfig as a, PathOptions as b, Graph as c, GraphEdge as d, GraphFormatConverter as f, GraphStep as g, GraphPath as h, EdgeConfig as i, GraphConfig as l, GraphPatch as m, DeleteNodeOptions as n, EntitiesUpdate as o, GraphNode as p, EdgeChange as r, EntityRect as s, AllPairsShortestPathsOptions as t, GraphDiff as u, NodeChange as v, VisualEdge as w, SinglePathOptions as x, NodeConfig as y };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@statelyai/graph",
3
3
  "type": "module",
4
- "version": "0.3.1",
4
+ "version": "0.4.0",
5
5
  "description": "A TypeScript-first graph library with plain JSON-serializable objects",
6
6
  "author": "David Khourshid <david@stately.ai>",
7
7
  "license": "MIT",
@@ -36,6 +36,7 @@
36
36
  "./jgf": "./dist/formats/jgf/index.mjs",
37
37
  "./mermaid": "./dist/formats/mermaid/index.mjs",
38
38
  "./tgf": "./dist/formats/tgf/index.mjs",
39
+ "./xyflow": "./dist/formats/xyflow/index.mjs",
39
40
  "./queries": "./dist/queries.mjs",
40
41
  "./schemas": "./dist/schemas.mjs",
41
42
  "./package.json": "./package.json"
@@ -53,6 +54,7 @@
53
54
  "@types/d3-array": "^3.2.2",
54
55
  "@types/d3-force": "^3.0.10",
55
56
  "@types/node": "^25.0.3",
57
+ "@xyflow/system": "^0.0.75",
56
58
  "bumpp": "^10.3.2",
57
59
  "cytoscape": "^3.33.1",
58
60
  "d3-array": "^3.2.4",