@statelyai/graph 0.1.0 → 0.3.1

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 (52) hide show
  1. package/README.md +65 -15
  2. package/dist/{adjacency-list-CXpOCibq.mjs → adjacency-list-ITO40kmn.mjs} +34 -1
  3. package/dist/{algorithms-R35X6ro4.mjs → algorithms-NWSB2RWj.mjs} +753 -19
  4. package/dist/algorithms.d.mts +488 -11
  5. package/dist/algorithms.mjs +2 -2
  6. package/dist/converter-CchokMDg.mjs +67 -0
  7. package/dist/edge-list-CgX6bBIF.mjs +71 -0
  8. package/dist/formats/adjacency-list/index.d.mts +44 -0
  9. package/dist/formats/adjacency-list/index.mjs +3 -0
  10. package/dist/formats/converter/index.d.mts +61 -0
  11. package/dist/formats/converter/index.mjs +3 -0
  12. package/dist/formats/cytoscape/index.d.mts +83 -0
  13. package/dist/formats/cytoscape/index.mjs +135 -0
  14. package/dist/formats/d3/index.d.mts +68 -0
  15. package/dist/formats/d3/index.mjs +111 -0
  16. package/dist/formats/dot/index.d.mts +63 -0
  17. package/dist/formats/dot/index.mjs +288 -0
  18. package/dist/formats/edge-list/index.d.mts +43 -0
  19. package/dist/formats/edge-list/index.mjs +3 -0
  20. package/dist/formats/gexf/index.d.mts +9 -0
  21. package/dist/formats/gexf/index.mjs +249 -0
  22. package/dist/formats/gml/index.d.mts +65 -0
  23. package/dist/formats/gml/index.mjs +291 -0
  24. package/dist/formats/graphml/index.d.mts +9 -0
  25. package/dist/{graphml-CUTNRXqd.mjs → formats/graphml/index.mjs} +18 -4
  26. package/dist/formats/jgf/index.d.mts +79 -0
  27. package/dist/formats/jgf/index.mjs +134 -0
  28. package/dist/formats/mermaid/index.d.mts +381 -0
  29. package/dist/formats/mermaid/index.mjs +2237 -0
  30. package/dist/formats/tgf/index.d.mts +54 -0
  31. package/dist/formats/tgf/index.mjs +111 -0
  32. package/dist/index.d.mts +332 -21
  33. package/dist/index.mjs +117 -13
  34. package/dist/{indexing-BHg1VhqN.mjs → indexing-eNDrXdDA.mjs} +31 -2
  35. package/dist/queries.d.mts +430 -9
  36. package/dist/queries.mjs +472 -9
  37. package/dist/{types-XV3S5Jnh.d.mts → types-BDXC1O5b.d.mts} +37 -2
  38. package/package.json +43 -17
  39. package/dist/adjacency-list-DW-lAUe8.d.mts +0 -10
  40. package/dist/dot-BRtq3e3c.mjs +0 -59
  41. package/dist/dot-HmJeUMsj.d.mts +0 -6
  42. package/dist/edge-list-BRujEnnU.mjs +0 -39
  43. package/dist/edge-list-CJmfoNu2.d.mts +0 -10
  44. package/dist/formats/adjacency-list.d.mts +0 -2
  45. package/dist/formats/adjacency-list.mjs +0 -3
  46. package/dist/formats/dot.d.mts +0 -2
  47. package/dist/formats/dot.mjs +0 -3
  48. package/dist/formats/edge-list.d.mts +0 -2
  49. package/dist/formats/edge-list.mjs +0 -3
  50. package/dist/formats/graphml.d.mts +0 -2
  51. package/dist/formats/graphml.mjs +0 -3
  52. package/dist/graphml-CMjPzSfY.d.mts +0 -7
@@ -0,0 +1,291 @@
1
+ import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
2
+
3
+ //#region src/formats/gml/index.ts
4
+ /**
5
+ * Converts a graph to GML (Graph Modelling Language) string.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createGraph } from '@statelyai/graph';
10
+ * import { toGML } from '@statelyai/graph/formats/gml';
11
+ *
12
+ * const graph = createGraph({
13
+ * nodes: [{ id: 'a' }, { id: 'b' }],
14
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
15
+ * });
16
+ *
17
+ * const gml = toGML(graph);
18
+ * // graph [
19
+ * // directed 1
20
+ * // node [ id "a" ]
21
+ * // node [ id "b" ]
22
+ * // edge [ id "e0" source "a" target "b" ]
23
+ * // ]
24
+ * ```
25
+ */
26
+ function toGML(graph) {
27
+ const lines = [];
28
+ lines.push("graph [");
29
+ lines.push(` directed ${graph.type === "directed" ? 1 : 0}`);
30
+ if (graph.id) lines.push(` id ${gmlString(graph.id)}`);
31
+ const childrenMap = /* @__PURE__ */ new Map();
32
+ for (const node of graph.nodes) {
33
+ const pid = node.parentId;
34
+ if (!childrenMap.has(pid)) childrenMap.set(pid, []);
35
+ childrenMap.get(pid).push(node);
36
+ }
37
+ function writeNode(node, indent) {
38
+ lines.push(`${indent}node [`);
39
+ lines.push(`${indent} id ${gmlString(node.id)}`);
40
+ if (node.label) lines.push(`${indent} label ${gmlString(node.label)}`);
41
+ if (node.initialNodeId !== null) lines.push(`${indent} initialNodeId ${gmlString(node.initialNodeId)}`);
42
+ if (node.data !== void 0) lines.push(`${indent} data ${gmlString(JSON.stringify(node.data))}`);
43
+ if (node.shape) lines.push(`${indent} shape ${gmlString(node.shape)}`);
44
+ if (node.color) lines.push(`${indent} color ${gmlString(node.color)}`);
45
+ if (node.x !== void 0 || node.y !== void 0 || node.width !== void 0 || node.height !== void 0) {
46
+ lines.push(`${indent} graphics [`);
47
+ if (node.x !== void 0) lines.push(`${indent} x ${node.x}`);
48
+ if (node.y !== void 0) lines.push(`${indent} y ${node.y}`);
49
+ if (node.width !== void 0) lines.push(`${indent} w ${node.width}`);
50
+ if (node.height !== void 0) lines.push(`${indent} h ${node.height}`);
51
+ lines.push(`${indent} ]`);
52
+ }
53
+ const children = childrenMap.get(node.id);
54
+ if (children) for (const child of children) writeNode(child, indent + " ");
55
+ lines.push(`${indent}]`);
56
+ }
57
+ const roots = childrenMap.get(null) ?? [];
58
+ for (const node of roots) writeNode(node, " ");
59
+ for (const edge of graph.edges) {
60
+ lines.push(" edge [");
61
+ lines.push(` id ${gmlString(edge.id)}`);
62
+ lines.push(` source ${gmlString(edge.sourceId)}`);
63
+ lines.push(` target ${gmlString(edge.targetId)}`);
64
+ if (edge.label) lines.push(` label ${gmlString(edge.label)}`);
65
+ if (edge.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(edge.data))}`);
66
+ if (edge.color) lines.push(` color ${gmlString(edge.color)}`);
67
+ lines.push(" ]");
68
+ }
69
+ lines.push("]");
70
+ return lines.join("\n");
71
+ }
72
+ function gmlString(s) {
73
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
74
+ }
75
+ /**
76
+ * Parses a GML (Graph Modelling Language) string into a graph.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { fromGML } from '@statelyai/graph/formats/gml';
81
+ *
82
+ * const graph = fromGML(`
83
+ * graph [
84
+ * directed 1
85
+ * node [ id "a" ]
86
+ * node [ id "b" ]
87
+ * edge [ source "a" target "b" ]
88
+ * ]
89
+ * `);
90
+ * ```
91
+ */
92
+ function fromGML(gml) {
93
+ if (typeof gml !== "string") throw new Error("GML: expected a string");
94
+ if (!gml.trim()) throw new Error("GML: input is empty");
95
+ const tokens = tokenize(gml);
96
+ if (tokens.length === 0) throw new Error("GML: no tokens found");
97
+ const graphBlock = parseBlock(tokens, 0).value["graph"];
98
+ if (!graphBlock) throw new Error("GML: missing top-level \"graph\" block");
99
+ const directed = graphBlock["directed"] === 1;
100
+ const graphId = String(graphBlock["id"] ?? "");
101
+ const nodes = [];
102
+ const edges = [];
103
+ function parseNodes(block, parentId) {
104
+ const nodeEntries = asArray(block["node"]);
105
+ for (const n of nodeEntries) {
106
+ const id = String(n["id"] ?? "");
107
+ const gfx = n["graphics"];
108
+ nodes.push({
109
+ type: "node",
110
+ id,
111
+ parentId,
112
+ initialNodeId: n["initialNodeId"] ?? null,
113
+ label: n["label"] ?? "",
114
+ data: n["data"] !== void 0 ? tryParseJSON(n["data"]) : void 0,
115
+ ...n["shape"] && { shape: n["shape"] },
116
+ ...n["color"] && { color: n["color"] },
117
+ ...gfx?.x !== void 0 && { x: gfx.x },
118
+ ...gfx?.y !== void 0 && { y: gfx.y },
119
+ ...gfx?.w !== void 0 && { width: gfx.w },
120
+ ...gfx?.h !== void 0 && { height: gfx.h }
121
+ });
122
+ if (n["node"] !== void 0) parseNodes(n, id);
123
+ }
124
+ }
125
+ parseNodes(graphBlock, null);
126
+ const edgeEntries = asArray(graphBlock["edge"]);
127
+ for (const e of edgeEntries) edges.push({
128
+ type: "edge",
129
+ id: String(e["id"] ?? `e${edges.length}`),
130
+ sourceId: String(e["source"] ?? ""),
131
+ targetId: String(e["target"] ?? ""),
132
+ label: e["label"] ?? "",
133
+ data: e["data"] !== void 0 ? tryParseJSON(e["data"]) : void 0,
134
+ ...e["color"] && { color: e["color"] }
135
+ });
136
+ return {
137
+ id: graphId,
138
+ type: directed ? "directed" : "undirected",
139
+ initialNodeId: null,
140
+ nodes,
141
+ edges,
142
+ data: void 0
143
+ };
144
+ }
145
+ function tokenize(input) {
146
+ const tokens = [];
147
+ let i = 0;
148
+ while (i < input.length) {
149
+ const ch = input[i];
150
+ if (/\s/.test(ch)) {
151
+ i++;
152
+ continue;
153
+ }
154
+ if (ch === "#") {
155
+ while (i < input.length && input[i] !== "\n") i++;
156
+ continue;
157
+ }
158
+ if (ch === "[") {
159
+ tokens.push({
160
+ type: "open",
161
+ value: "["
162
+ });
163
+ i++;
164
+ continue;
165
+ }
166
+ if (ch === "]") {
167
+ tokens.push({
168
+ type: "close",
169
+ value: "]"
170
+ });
171
+ i++;
172
+ continue;
173
+ }
174
+ if (ch === "\"") {
175
+ i++;
176
+ let s = "";
177
+ while (i < input.length && input[i] !== "\"") {
178
+ if (input[i] === "\\" && i + 1 < input.length) {
179
+ i++;
180
+ s += input[i];
181
+ } else s += input[i];
182
+ i++;
183
+ }
184
+ i++;
185
+ tokens.push({
186
+ type: "string",
187
+ value: s
188
+ });
189
+ continue;
190
+ }
191
+ if (/[-+0-9.]/.test(ch)) {
192
+ let num = "";
193
+ while (i < input.length && /[-+0-9.eE]/.test(input[i])) {
194
+ num += input[i];
195
+ i++;
196
+ }
197
+ const parsed = Number(num);
198
+ if (!isNaN(parsed)) tokens.push({
199
+ type: "number",
200
+ value: parsed
201
+ });
202
+ else tokens.push({
203
+ type: "word",
204
+ value: num
205
+ });
206
+ continue;
207
+ }
208
+ if (/[a-zA-Z_]/.test(ch)) {
209
+ let word = "";
210
+ while (i < input.length && /[a-zA-Z0-9_]/.test(input[i])) {
211
+ word += input[i];
212
+ i++;
213
+ }
214
+ tokens.push({
215
+ type: "word",
216
+ value: word
217
+ });
218
+ continue;
219
+ }
220
+ i++;
221
+ }
222
+ return tokens;
223
+ }
224
+ function parseBlock(tokens, pos) {
225
+ const result = {};
226
+ while (pos < tokens.length) {
227
+ const tok = tokens[pos];
228
+ if (tok.type === "close") {
229
+ pos++;
230
+ break;
231
+ }
232
+ if (tok.type === "word") {
233
+ const key = tok.value;
234
+ pos++;
235
+ if (pos >= tokens.length) break;
236
+ const next = tokens[pos];
237
+ if (next.type === "open") {
238
+ pos++;
239
+ const nested = parseBlock(tokens, pos);
240
+ pos = nested.pos;
241
+ if (result[key] !== void 0) {
242
+ if (!Array.isArray(result[key])) result[key] = [result[key]];
243
+ result[key].push(nested.value);
244
+ } else result[key] = nested.value;
245
+ } else {
246
+ if (result[key] !== void 0) {
247
+ if (!Array.isArray(result[key])) result[key] = [result[key]];
248
+ result[key].push(next.value);
249
+ } else result[key] = next.value;
250
+ pos++;
251
+ }
252
+ } else pos++;
253
+ }
254
+ return {
255
+ value: result,
256
+ pos
257
+ };
258
+ }
259
+ function asArray(val) {
260
+ if (val === void 0) return [];
261
+ return Array.isArray(val) ? val : [val];
262
+ }
263
+ function tryParseJSON(str) {
264
+ if (typeof str !== "string") return str;
265
+ try {
266
+ return JSON.parse(str);
267
+ } catch {
268
+ return str;
269
+ }
270
+ }
271
+ /**
272
+ * Bidirectional converter for GML (Graph Modelling Language) format.
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * import { createGraph } from '@statelyai/graph';
277
+ * import { gmlConverter } from '@statelyai/graph/formats/gml';
278
+ *
279
+ * const graph = createGraph({
280
+ * nodes: [{ id: 'a' }, { id: 'b' }],
281
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
282
+ * });
283
+ *
284
+ * const gml = gmlConverter.to(graph);
285
+ * const roundTripped = gmlConverter.from(gml);
286
+ * ```
287
+ */
288
+ const gmlConverter = createFormatConverter(toGML, fromGML);
289
+
290
+ //#endregion
291
+ export { fromGML, gmlConverter, toGML };
@@ -0,0 +1,9 @@
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
2
+
3
+ //#region src/formats/graphml/index.d.ts
4
+ declare function toGraphML(graph: Graph): string;
5
+ declare function fromGraphML(xml: string): Graph;
6
+ /** Bidirectional converter for GraphML XML format. */
7
+ declare const graphmlConverter: GraphFormatConverter<string>;
8
+ //#endregion
9
+ export { fromGraphML, graphmlConverter, toGraphML };
@@ -1,6 +1,7 @@
1
+ import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
1
2
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
2
3
 
3
- //#region src/formats/graphml.ts
4
+ //#region src/formats/graphml/index.ts
4
5
  const GRAPHML_NS = "http://graphml.graphdrawing.org/xmlns";
5
6
  function toGraphML(graph) {
6
7
  const keys = [
@@ -174,7 +175,8 @@ function toGraphML(graph) {
174
175
  }).build(obj);
175
176
  }
176
177
  function fromGraphML(xml) {
177
- const graphEl = new XMLParser({
178
+ if (typeof xml !== "string") throw new Error("GraphML: expected a string");
179
+ const parser = new XMLParser({
178
180
  ignoreAttributes: false,
179
181
  isArray: (name) => [
180
182
  "node",
@@ -182,7 +184,17 @@ function fromGraphML(xml) {
182
184
  "data",
183
185
  "key"
184
186
  ].includes(name)
185
- }).parse(xml).graphml.graph;
187
+ });
188
+ let parsed;
189
+ try {
190
+ parsed = parser.parse(xml);
191
+ } catch (e) {
192
+ throw new Error(`GraphML: invalid XML — ${e.message}`);
193
+ }
194
+ const graphml = parsed?.graphml;
195
+ if (!graphml) throw new Error("GraphML: missing <graphml> root element");
196
+ const graphEl = graphml.graph;
197
+ if (!graphEl) throw new Error("GraphML: missing <graph> element");
186
198
  const graphType = graphEl["@_edgedefault"] === "undirected" ? "undirected" : "directed";
187
199
  let graphData = void 0;
188
200
  if (graphEl.data) {
@@ -235,6 +247,8 @@ function tryParseJSON(str) {
235
247
  return str;
236
248
  }
237
249
  }
250
+ /** Bidirectional converter for GraphML XML format. */
251
+ const graphmlConverter = createFormatConverter(toGraphML, fromGraphML);
238
252
 
239
253
  //#endregion
240
- export { toGraphML as n, fromGraphML as t };
254
+ export { fromGraphML, graphmlConverter, toGraphML };
@@ -0,0 +1,79 @@
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-BDXC1O5b.mjs";
2
+
3
+ //#region src/formats/jgf/index.d.ts
4
+ interface JGFNode {
5
+ id: string;
6
+ label?: string;
7
+ metadata?: Record<string, any>;
8
+ }
9
+ interface JGFEdge {
10
+ id?: string;
11
+ source: string;
12
+ target: string;
13
+ label?: string;
14
+ metadata?: Record<string, any>;
15
+ }
16
+ interface JGFGraph {
17
+ graph: {
18
+ id?: string;
19
+ directed?: boolean;
20
+ metadata?: Record<string, any>;
21
+ nodes: JGFNode[];
22
+ edges: JGFEdge[];
23
+ };
24
+ }
25
+ /**
26
+ * Converts a graph to JSON Graph Format (JGF).
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { createGraph } from '@statelyai/graph';
31
+ * import { toJGF } from '@statelyai/graph/formats/jgf';
32
+ *
33
+ * const graph = createGraph({
34
+ * nodes: [{ id: 'a' }, { id: 'b' }],
35
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
36
+ * });
37
+ *
38
+ * const jgf = toJGF(graph);
39
+ * // { graph: { directed: true, nodes: [...], edges: [...] } }
40
+ * ```
41
+ */
42
+ declare function toJGF(graph: Graph): JGFGraph;
43
+ /**
44
+ * Parses a JSON Graph Format (JGF) object into a graph.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { fromJGF } from '@statelyai/graph/formats/jgf';
49
+ *
50
+ * const graph = fromJGF({
51
+ * graph: {
52
+ * directed: true,
53
+ * nodes: [{ id: 'a' }, { id: 'b' }],
54
+ * edges: [{ source: 'a', target: 'b' }],
55
+ * },
56
+ * });
57
+ * ```
58
+ */
59
+ declare function fromJGF(jgf: JGFGraph): Graph;
60
+ /**
61
+ * Bidirectional converter for JSON Graph Format.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { createGraph } from '@statelyai/graph';
66
+ * import { jgfConverter } from '@statelyai/graph/formats/jgf';
67
+ *
68
+ * const graph = createGraph({
69
+ * nodes: [{ id: 'a' }, { id: 'b' }],
70
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
71
+ * });
72
+ *
73
+ * const jgf = jgfConverter.to(graph);
74
+ * const roundTripped = jgfConverter.from(jgf);
75
+ * ```
76
+ */
77
+ declare const jgfConverter: GraphFormatConverter<JGFGraph>;
78
+ //#endregion
79
+ export { JGFEdge, JGFGraph, JGFNode, fromJGF, jgfConverter, toJGF };
@@ -0,0 +1,134 @@
1
+ import { n as createFormatConverter } from "../../converter-CchokMDg.mjs";
2
+
3
+ //#region src/formats/jgf/index.ts
4
+ /**
5
+ * Converts a graph to JSON Graph Format (JGF).
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createGraph } from '@statelyai/graph';
10
+ * import { toJGF } from '@statelyai/graph/formats/jgf';
11
+ *
12
+ * const graph = createGraph({
13
+ * nodes: [{ id: 'a' }, { id: 'b' }],
14
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
15
+ * });
16
+ *
17
+ * const jgf = toJGF(graph);
18
+ * // { graph: { directed: true, nodes: [...], edges: [...] } }
19
+ * ```
20
+ */
21
+ function toJGF(graph) {
22
+ const metadata = {};
23
+ if (graph.initialNodeId !== null) metadata.initialNodeId = graph.initialNodeId;
24
+ if (graph.data !== void 0) metadata.data = graph.data;
25
+ if (graph.direction) metadata.direction = graph.direction;
26
+ return { graph: {
27
+ id: graph.id || void 0,
28
+ directed: graph.type === "directed",
29
+ ...Object.keys(metadata).length > 0 && { metadata },
30
+ nodes: graph.nodes.map((n) => {
31
+ const meta = {};
32
+ if (n.parentId !== null) meta.parentId = n.parentId;
33
+ if (n.initialNodeId !== null) meta.initialNodeId = n.initialNodeId;
34
+ if (n.data !== void 0) meta.data = n.data;
35
+ if (n.x !== void 0) meta.x = n.x;
36
+ if (n.y !== void 0) meta.y = n.y;
37
+ if (n.width !== void 0) meta.width = n.width;
38
+ if (n.height !== void 0) meta.height = n.height;
39
+ if (n.shape) meta.shape = n.shape;
40
+ if (n.color) meta.color = n.color;
41
+ return {
42
+ id: n.id,
43
+ ...n.label && { label: n.label },
44
+ ...Object.keys(meta).length > 0 && { metadata: meta }
45
+ };
46
+ }),
47
+ edges: graph.edges.map((e) => {
48
+ const meta = {};
49
+ if (e.data !== void 0) meta.data = e.data;
50
+ if (e.color) meta.color = e.color;
51
+ return {
52
+ id: e.id,
53
+ source: e.sourceId,
54
+ target: e.targetId,
55
+ ...e.label && { label: e.label },
56
+ ...Object.keys(meta).length > 0 && { metadata: meta }
57
+ };
58
+ })
59
+ } };
60
+ }
61
+ /**
62
+ * Parses a JSON Graph Format (JGF) object into a graph.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { fromJGF } from '@statelyai/graph/formats/jgf';
67
+ *
68
+ * const graph = fromJGF({
69
+ * graph: {
70
+ * directed: true,
71
+ * nodes: [{ id: 'a' }, { id: 'b' }],
72
+ * edges: [{ source: 'a', target: 'b' }],
73
+ * },
74
+ * });
75
+ * ```
76
+ */
77
+ function fromJGF(jgf) {
78
+ if (!jgf || typeof jgf !== "object") throw new Error("JGF: expected an object");
79
+ if (!jgf.graph || typeof jgf.graph !== "object") throw new Error("JGF: missing \"graph\" property");
80
+ const g = jgf.graph;
81
+ if (!Array.isArray(g.nodes)) throw new Error("JGF: \"graph.nodes\" must be an array");
82
+ if (!Array.isArray(g.edges)) throw new Error("JGF: \"graph.edges\" must be an array");
83
+ return {
84
+ id: g.id ?? "",
85
+ type: g.directed === false ? "undirected" : "directed",
86
+ initialNodeId: g.metadata?.initialNodeId ?? null,
87
+ data: g.metadata?.data,
88
+ ...g.metadata?.direction && { direction: g.metadata.direction },
89
+ nodes: g.nodes.map((n) => ({
90
+ type: "node",
91
+ id: n.id,
92
+ parentId: n.metadata?.parentId ?? null,
93
+ initialNodeId: n.metadata?.initialNodeId ?? null,
94
+ label: n.label ?? "",
95
+ data: n.metadata?.data,
96
+ ...n.metadata?.x !== void 0 && { x: n.metadata.x },
97
+ ...n.metadata?.y !== void 0 && { y: n.metadata.y },
98
+ ...n.metadata?.width !== void 0 && { width: n.metadata.width },
99
+ ...n.metadata?.height !== void 0 && { height: n.metadata.height },
100
+ ...n.metadata?.shape && { shape: n.metadata.shape },
101
+ ...n.metadata?.color && { color: n.metadata.color }
102
+ })),
103
+ edges: g.edges.map((e, i) => ({
104
+ type: "edge",
105
+ id: e.id ?? `e${i}`,
106
+ sourceId: e.source,
107
+ targetId: e.target,
108
+ label: e.label ?? "",
109
+ data: e.metadata?.data,
110
+ ...e.metadata?.color && { color: e.metadata.color }
111
+ }))
112
+ };
113
+ }
114
+ /**
115
+ * Bidirectional converter for JSON Graph Format.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * import { createGraph } from '@statelyai/graph';
120
+ * import { jgfConverter } from '@statelyai/graph/formats/jgf';
121
+ *
122
+ * const graph = createGraph({
123
+ * nodes: [{ id: 'a' }, { id: 'b' }],
124
+ * edges: [{ id: 'e0', sourceId: 'a', targetId: 'b' }],
125
+ * });
126
+ *
127
+ * const jgf = jgfConverter.to(graph);
128
+ * const roundTripped = jgfConverter.from(jgf);
129
+ * ```
130
+ */
131
+ const jgfConverter = createFormatConverter(toJGF, fromJGF);
132
+
133
+ //#endregion
134
+ export { fromJGF, jgfConverter, toJGF };