@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.
- package/README.md +65 -15
- package/dist/{adjacency-list-CXpOCibq.mjs → adjacency-list-ITO40kmn.mjs} +34 -1
- package/dist/{algorithms-R35X6ro4.mjs → algorithms-NWSB2RWj.mjs} +753 -19
- package/dist/algorithms.d.mts +488 -11
- package/dist/algorithms.mjs +2 -2
- package/dist/converter-CchokMDg.mjs +67 -0
- package/dist/edge-list-CgX6bBIF.mjs +71 -0
- package/dist/formats/adjacency-list/index.d.mts +44 -0
- package/dist/formats/adjacency-list/index.mjs +3 -0
- package/dist/formats/converter/index.d.mts +61 -0
- package/dist/formats/converter/index.mjs +3 -0
- package/dist/formats/cytoscape/index.d.mts +83 -0
- package/dist/formats/cytoscape/index.mjs +135 -0
- package/dist/formats/d3/index.d.mts +68 -0
- package/dist/formats/d3/index.mjs +111 -0
- package/dist/formats/dot/index.d.mts +63 -0
- package/dist/formats/dot/index.mjs +288 -0
- package/dist/formats/edge-list/index.d.mts +43 -0
- package/dist/formats/edge-list/index.mjs +3 -0
- package/dist/formats/gexf/index.d.mts +9 -0
- package/dist/formats/gexf/index.mjs +249 -0
- package/dist/formats/gml/index.d.mts +65 -0
- package/dist/formats/gml/index.mjs +291 -0
- package/dist/formats/graphml/index.d.mts +9 -0
- package/dist/{graphml-CUTNRXqd.mjs → formats/graphml/index.mjs} +18 -4
- package/dist/formats/jgf/index.d.mts +79 -0
- package/dist/formats/jgf/index.mjs +134 -0
- package/dist/formats/mermaid/index.d.mts +381 -0
- package/dist/formats/mermaid/index.mjs +2237 -0
- package/dist/formats/tgf/index.d.mts +54 -0
- package/dist/formats/tgf/index.mjs +111 -0
- package/dist/index.d.mts +332 -21
- package/dist/index.mjs +117 -13
- package/dist/{indexing-BHg1VhqN.mjs → indexing-eNDrXdDA.mjs} +31 -2
- package/dist/queries.d.mts +430 -9
- package/dist/queries.mjs +472 -9
- package/dist/{types-XV3S5Jnh.d.mts → types-BDXC1O5b.d.mts} +37 -2
- package/package.json +43 -17
- package/dist/adjacency-list-DW-lAUe8.d.mts +0 -10
- package/dist/dot-BRtq3e3c.mjs +0 -59
- package/dist/dot-HmJeUMsj.d.mts +0 -6
- package/dist/edge-list-BRujEnnU.mjs +0 -39
- package/dist/edge-list-CJmfoNu2.d.mts +0 -10
- package/dist/formats/adjacency-list.d.mts +0 -2
- package/dist/formats/adjacency-list.mjs +0 -3
- package/dist/formats/dot.d.mts +0 -2
- package/dist/formats/dot.mjs +0 -3
- package/dist/formats/edge-list.d.mts +0 -2
- package/dist/formats/edge-list.mjs +0 -3
- package/dist/formats/graphml.d.mts +0 -2
- package/dist/formats/graphml.mjs +0 -3
- 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
|
-
|
|
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
|
-
})
|
|
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 {
|
|
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 };
|