@statelyai/graph 1.0.0 → 2.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 +55 -26
- package/dist/{adjacency-list-VsUaH9SJ.mjs → adjacency-list-GeL1Cu-L.mjs} +3 -1
- package/dist/{algorithms-fTqmvhzP.d.mts → algorithms-CsGNehct.d.mts} +137 -2
- package/dist/{algorithms-Ba7o7niK.mjs → algorithms-DF1pSQGv.mjs} +1476 -343
- package/dist/algorithms.d.mts +2 -2
- package/dist/algorithms.mjs +2 -2
- package/dist/{converter-udLITX36.mjs → converter-DyCJJfTe.mjs} +2 -2
- package/dist/format-support.mjs +38 -11
- 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 -1
- package/dist/formats/converter/index.mjs +1 -1
- package/dist/formats/cytoscape/index.d.mts +1 -1
- package/dist/formats/cytoscape/index.mjs +3 -1
- package/dist/formats/d2/index.d.mts +1 -1
- package/dist/formats/d2/index.mjs +26 -12
- package/dist/formats/d3/index.d.mts +1 -1
- package/dist/formats/d3/index.mjs +3 -1
- package/dist/formats/dot/index.d.mts +1 -1
- package/dist/formats/dot/index.mjs +22 -6
- 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 +21 -14
- package/dist/formats/gexf/index.d.mts +1 -1
- package/dist/formats/gexf/index.mjs +22 -15
- package/dist/formats/gml/index.d.mts +1 -1
- package/dist/formats/gml/index.mjs +21 -12
- package/dist/formats/graphml/index.d.mts +1 -1
- package/dist/formats/graphml/index.mjs +73 -22
- package/dist/formats/jgf/index.d.mts +1 -1
- package/dist/formats/jgf/index.mjs +5 -2
- package/dist/formats/mermaid/index.d.mts +1 -1
- package/dist/formats/mermaid/index.mjs +49 -12
- package/dist/formats/tgf/index.d.mts +1 -1
- package/dist/formats/tgf/index.mjs +1 -1
- package/dist/formats/xyflow/index.d.mts +1 -1
- package/dist/formats/xyflow/index.mjs +31 -4
- package/dist/{index-D9Kj6Fe3.d.mts → index-D51lJnt2.d.mts} +1 -1
- package/dist/{index-CHoriXZD.d.mts → index-DWmo1mIp.d.mts} +77 -18
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +143 -295
- package/dist/{queries-BlkA1HAN.d.mts → queries-BfXeTXRf.d.mts} +43 -12
- package/dist/queries-KirMDR7e.mjs +980 -0
- package/dist/queries.d.mts +1 -1
- package/dist/queries.mjs +1 -768
- package/dist/schemas.d.mts +1 -1
- package/dist/schemas.mjs +23 -84
- package/dist/{types-3-FS9NV2.d.mts → types-DNYdIU21.d.mts} +54 -5
- package/dist/validate-TtH-x3JV.mjs +190 -0
- package/package.json +13 -3
- package/dist/indexing-DR8M1vBy.mjs +0 -137
- /package/dist/{edge-list-DP4otyPU.mjs → edge-list-BcZ0h6zz.mjs} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getEdgeMode } from "../../mode-D8OnHFBk.mjs";
|
|
2
|
-
import { n as createFormatConverter } from "../../converter-
|
|
2
|
+
import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
|
|
3
3
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
4
4
|
|
|
5
5
|
//#region src/formats/gexf/index.ts
|
|
@@ -129,7 +129,7 @@ function toGEXF(graph) {
|
|
|
129
129
|
});
|
|
130
130
|
const node = {
|
|
131
131
|
"@_id": n.id,
|
|
132
|
-
"@_label": n.label
|
|
132
|
+
"@_label": n.label ?? ""
|
|
133
133
|
};
|
|
134
134
|
if (n.parentId) node["@_pid"] = n.parentId;
|
|
135
135
|
if (attvalues.length > 0) node.attvalues = { attvalue: attvalues };
|
|
@@ -250,7 +250,8 @@ function fromGEXF(xml) {
|
|
|
250
250
|
"attribute",
|
|
251
251
|
"attvalue",
|
|
252
252
|
"attributes"
|
|
253
|
-
].includes(name)
|
|
253
|
+
].includes(name),
|
|
254
|
+
trimValues: false
|
|
254
255
|
});
|
|
255
256
|
let parsed;
|
|
256
257
|
try {
|
|
@@ -283,16 +284,16 @@ function fromGEXF(xml) {
|
|
|
283
284
|
if (attvals["ports"] !== void 0) node.ports = tryParseJSON(attvals["ports"]);
|
|
284
285
|
const pos = n["viz:position"];
|
|
285
286
|
if (pos) {
|
|
286
|
-
node.x =
|
|
287
|
-
node.y =
|
|
287
|
+
node.x = parseNumber(pos["@_x"] ?? 0, "<viz:position> x", "node", id);
|
|
288
|
+
node.y = parseNumber(pos["@_y"] ?? 0, "<viz:position> y", "node", id);
|
|
288
289
|
}
|
|
289
290
|
const size = n["viz:size"];
|
|
290
291
|
if (size) {
|
|
291
|
-
node.width =
|
|
292
|
-
node.height =
|
|
292
|
+
node.width = parseNumber(size["@_value"] ?? 0, "<viz:size>", "node", id);
|
|
293
|
+
node.height = node.width;
|
|
293
294
|
}
|
|
294
|
-
if (attvals["width"] !== void 0) node.width =
|
|
295
|
-
if (attvals["height"] !== void 0) node.height =
|
|
295
|
+
if (attvals["width"] !== void 0) node.width = parseNumber(attvals["width"], "width attribute", "node", id);
|
|
296
|
+
if (attvals["height"] !== void 0) node.height = parseNumber(attvals["height"], "height attribute", "node", id);
|
|
296
297
|
const color = n["viz:color"];
|
|
297
298
|
if (color) {
|
|
298
299
|
const r = Number(color["@_r"] ?? 0);
|
|
@@ -308,18 +309,19 @@ function fromGEXF(xml) {
|
|
|
308
309
|
parseNodes(graphEl.nodes?.node ?? graphEl.node, null);
|
|
309
310
|
const edges = asArray(graphEl.edges?.edge ?? graphEl.edge).map((e, i) => {
|
|
310
311
|
const attvals = getAttValues(e, attrMap);
|
|
312
|
+
const id = String(e["@_id"] ?? `e${i}`);
|
|
311
313
|
const edge = {
|
|
312
314
|
type: "edge",
|
|
313
|
-
id
|
|
315
|
+
id,
|
|
314
316
|
sourceId: String(e["@_source"]),
|
|
315
317
|
targetId: String(e["@_target"]),
|
|
316
318
|
label: e["@_label"] ?? "",
|
|
317
319
|
data: attvals["data"] !== void 0 ? tryParseJSON(attvals["data"]) : void 0,
|
|
318
|
-
...attvals["weight"] !== void 0 && { weight:
|
|
319
|
-
...attvals["x"] !== void 0 && { x:
|
|
320
|
-
...attvals["y"] !== void 0 && { y:
|
|
321
|
-
...attvals["width"] !== void 0 && { width:
|
|
322
|
-
...attvals["height"] !== void 0 && { height:
|
|
320
|
+
...attvals["weight"] !== void 0 && { weight: parseNumber(attvals["weight"], "weight attribute", "edge", id) },
|
|
321
|
+
...attvals["x"] !== void 0 && { x: parseNumber(attvals["x"], "x attribute", "edge", id) },
|
|
322
|
+
...attvals["y"] !== void 0 && { y: parseNumber(attvals["y"], "y attribute", "edge", id) },
|
|
323
|
+
...attvals["width"] !== void 0 && { width: parseNumber(attvals["width"], "width attribute", "edge", id) },
|
|
324
|
+
...attvals["height"] !== void 0 && { height: parseNumber(attvals["height"], "height attribute", "edge", id) },
|
|
323
325
|
...attvals["style"] !== void 0 && { style: tryParseJSON(attvals["style"]) },
|
|
324
326
|
...attvals["sourcePort"] !== void 0 && { sourcePort: attvals["sourcePort"] },
|
|
325
327
|
...attvals["targetPort"] !== void 0 && { targetPort: attvals["targetPort"] }
|
|
@@ -366,6 +368,11 @@ function tryParseJSON(str) {
|
|
|
366
368
|
return str;
|
|
367
369
|
}
|
|
368
370
|
}
|
|
371
|
+
function parseNumber(value, field, kind, ownerId) {
|
|
372
|
+
const parsed = Number(value);
|
|
373
|
+
if (Number.isNaN(parsed)) throw new Error(`GEXF: ${field} value "${value}" on ${kind} "${ownerId}" is not a number. Fix the value or remove the attribute.`);
|
|
374
|
+
return parsed;
|
|
375
|
+
}
|
|
369
376
|
function hex(n) {
|
|
370
377
|
return n.toString(16).padStart(2, "0");
|
|
371
378
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createFormatConverter } from "../../converter-
|
|
1
|
+
import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/formats/gml/index.ts
|
|
4
4
|
/**
|
|
@@ -27,6 +27,7 @@ function toGML(graph) {
|
|
|
27
27
|
const lines = [];
|
|
28
28
|
lines.push("graph [");
|
|
29
29
|
lines.push(` directed ${graph.mode === "undirected" ? 0 : 1}`);
|
|
30
|
+
if (graph.mode === "bidirectional") lines.push(` mode ${gmlString(graph.mode)}`);
|
|
30
31
|
if (graph.id) lines.push(` id ${gmlString(graph.id)}`);
|
|
31
32
|
if (graph.initialNodeId) lines.push(` initialNodeId ${gmlString(graph.initialNodeId)}`);
|
|
32
33
|
if (graph.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(graph.data))}`);
|
|
@@ -67,6 +68,7 @@ function toGML(graph) {
|
|
|
67
68
|
lines.push(` id ${gmlString(edge.id)}`);
|
|
68
69
|
lines.push(` source ${gmlString(edge.sourceId)}`);
|
|
69
70
|
lines.push(` target ${gmlString(edge.targetId)}`);
|
|
71
|
+
if (edge.mode) lines.push(` mode ${gmlString(edge.mode)}`);
|
|
70
72
|
if (edge.label) lines.push(` label ${gmlString(edge.label)}`);
|
|
71
73
|
if (edge.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(edge.data))}`);
|
|
72
74
|
if (edge.weight !== void 0) lines.push(` weight ${edge.weight}`);
|
|
@@ -134,10 +136,10 @@ function fromGML(gml) {
|
|
|
134
136
|
...n["shape"] && { shape: n["shape"] },
|
|
135
137
|
...n["color"] && { color: n["color"] },
|
|
136
138
|
...n["style"] !== void 0 && { style: tryParseJSON(n["style"]) },
|
|
137
|
-
...gfx?.x !== void 0 && { x: gfx.x },
|
|
138
|
-
...gfx?.y !== void 0 && { y: gfx.y },
|
|
139
|
-
...gfx?.w !== void 0 && { width: gfx.w },
|
|
140
|
-
...gfx?.h !== void 0 && { height: gfx.h }
|
|
139
|
+
...gfx?.x !== void 0 && { x: parseNumber(gfx.x, "graphics x", "node", id) },
|
|
140
|
+
...gfx?.y !== void 0 && { y: parseNumber(gfx.y, "graphics y", "node", id) },
|
|
141
|
+
...gfx?.w !== void 0 && { width: parseNumber(gfx.w, "graphics w", "node", id) },
|
|
142
|
+
...gfx?.h !== void 0 && { height: parseNumber(gfx.h, "graphics h", "node", id) }
|
|
141
143
|
});
|
|
142
144
|
if (n["node"] !== void 0) parseNodes(n, id);
|
|
143
145
|
}
|
|
@@ -146,27 +148,29 @@ function fromGML(gml) {
|
|
|
146
148
|
const edgeEntries = asArray(graphBlock["edge"]);
|
|
147
149
|
for (const e of edgeEntries) {
|
|
148
150
|
const gfx = e["graphics"];
|
|
151
|
+
const id = String(e["id"] ?? `e${edges.length}`);
|
|
149
152
|
edges.push({
|
|
150
153
|
type: "edge",
|
|
151
|
-
id
|
|
154
|
+
id,
|
|
152
155
|
sourceId: String(e["source"] ?? ""),
|
|
153
156
|
targetId: String(e["target"] ?? ""),
|
|
154
157
|
label: e["label"] ?? "",
|
|
158
|
+
...e["mode"] && { mode: String(e["mode"]) },
|
|
155
159
|
data: e["data"] !== void 0 ? tryParseJSON(e["data"]) : void 0,
|
|
156
|
-
...e["weight"] !== void 0 && { weight:
|
|
160
|
+
...e["weight"] !== void 0 && { weight: parseNumber(e["weight"], "weight", "edge", id) },
|
|
157
161
|
...e["sourcePort"] !== void 0 && { sourcePort: String(e["sourcePort"]) },
|
|
158
162
|
...e["targetPort"] !== void 0 && { targetPort: String(e["targetPort"]) },
|
|
159
163
|
...e["color"] && { color: e["color"] },
|
|
160
164
|
...e["style"] !== void 0 && { style: tryParseJSON(e["style"]) },
|
|
161
|
-
...gfx?.x !== void 0 && { x: gfx.x },
|
|
162
|
-
...gfx?.y !== void 0 && { y: gfx.y },
|
|
163
|
-
...gfx?.w !== void 0 && { width: gfx.w },
|
|
164
|
-
...gfx?.h !== void 0 && { height: gfx.h }
|
|
165
|
+
...gfx?.x !== void 0 && { x: parseNumber(gfx.x, "graphics x", "edge", id) },
|
|
166
|
+
...gfx?.y !== void 0 && { y: parseNumber(gfx.y, "graphics y", "edge", id) },
|
|
167
|
+
...gfx?.w !== void 0 && { width: parseNumber(gfx.w, "graphics w", "edge", id) },
|
|
168
|
+
...gfx?.h !== void 0 && { height: parseNumber(gfx.h, "graphics h", "edge", id) }
|
|
165
169
|
});
|
|
166
170
|
}
|
|
167
171
|
return {
|
|
168
172
|
id: graphId,
|
|
169
|
-
mode: directed ? "directed" : "undirected",
|
|
173
|
+
mode: graphBlock["mode"] ? String(graphBlock["mode"]) : directed ? "directed" : "undirected",
|
|
170
174
|
initialNodeId: graphBlock["initialNodeId"] ?? null,
|
|
171
175
|
nodes,
|
|
172
176
|
edges,
|
|
@@ -301,6 +305,11 @@ function tryParseJSON(str) {
|
|
|
301
305
|
return str;
|
|
302
306
|
}
|
|
303
307
|
}
|
|
308
|
+
function parseNumber(value, field, kind, ownerId) {
|
|
309
|
+
const parsed = Number(value);
|
|
310
|
+
if (Number.isNaN(parsed)) throw new Error(`GML: ${field} value "${value}" on ${kind} "${ownerId}" is not a number. Fix the value or remove the attribute.`);
|
|
311
|
+
return parsed;
|
|
312
|
+
}
|
|
304
313
|
/**
|
|
305
314
|
* Bidirectional converter for GML (Graph Modelling Language) format.
|
|
306
315
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getEdgeMode } from "../../mode-D8OnHFBk.mjs";
|
|
2
|
-
import { n as createFormatConverter } from "../../converter-
|
|
2
|
+
import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
|
|
3
3
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
4
4
|
|
|
5
5
|
//#region src/formats/graphml/index.ts
|
|
@@ -281,8 +281,12 @@ function fromGraphML(xml) {
|
|
|
281
281
|
"node",
|
|
282
282
|
"edge",
|
|
283
283
|
"data",
|
|
284
|
-
"key"
|
|
285
|
-
|
|
284
|
+
"key",
|
|
285
|
+
"graph",
|
|
286
|
+
"port"
|
|
287
|
+
].includes(name),
|
|
288
|
+
parseTagValue: false,
|
|
289
|
+
trimValues: false
|
|
286
290
|
});
|
|
287
291
|
let parsed;
|
|
288
292
|
try {
|
|
@@ -292,50 +296,77 @@ function fromGraphML(xml) {
|
|
|
292
296
|
}
|
|
293
297
|
const graphml = parsed?.graphml;
|
|
294
298
|
if (!graphml) throw new Error("GraphML: missing <graphml> root element");
|
|
295
|
-
const graphEl = graphml.graph;
|
|
299
|
+
const graphEl = asArray(graphml.graph)[0];
|
|
296
300
|
if (!graphEl) throw new Error("GraphML: missing <graph> element");
|
|
297
301
|
const graphType = graphEl["@_edgedefault"] === "undirected" ? "undirected" : "directed";
|
|
298
302
|
const graphDataMap = parseDataElements(graphEl.data);
|
|
299
303
|
const graphData = graphDataMap.graphData !== void 0 ? tryParseJSON(graphDataMap.graphData) : void 0;
|
|
300
|
-
const
|
|
304
|
+
const nodeEntries = [];
|
|
305
|
+
const edgeEls = [];
|
|
306
|
+
function collectGraphContents(gEl, parentId) {
|
|
307
|
+
for (const nodeEl of asArray(gEl.node)) {
|
|
308
|
+
nodeEntries.push({
|
|
309
|
+
el: nodeEl,
|
|
310
|
+
structuralParentId: parentId
|
|
311
|
+
});
|
|
312
|
+
for (const subgraphEl of asArray(nodeEl.graph)) collectGraphContents(subgraphEl, String(nodeEl["@_id"]));
|
|
313
|
+
}
|
|
314
|
+
edgeEls.push(...asArray(gEl.edge));
|
|
315
|
+
}
|
|
316
|
+
collectGraphContents(graphEl, null);
|
|
317
|
+
const nodes = nodeEntries.map(({ el: nodeEl, structuralParentId }) => {
|
|
301
318
|
const dataMap = parseDataElements(nodeEl.data);
|
|
319
|
+
const id = String(nodeEl["@_id"]);
|
|
302
320
|
const node = {
|
|
303
321
|
type: "node",
|
|
304
|
-
id
|
|
305
|
-
parentId: dataMap.parentId ??
|
|
322
|
+
id,
|
|
323
|
+
parentId: dataMap.parentId ?? structuralParentId,
|
|
306
324
|
initialNodeId: dataMap.initialNodeId ?? null,
|
|
307
325
|
label: dataMap.label ?? "",
|
|
308
326
|
data: dataMap.data !== void 0 ? tryParseJSON(dataMap.data) : void 0
|
|
309
327
|
};
|
|
310
|
-
if (dataMap.x !== void 0) node.x = parseNumber(dataMap.x);
|
|
311
|
-
if (dataMap.y !== void 0) node.y = parseNumber(dataMap.y);
|
|
312
|
-
if (dataMap.width !== void 0) node.width = parseNumber(dataMap.width);
|
|
313
|
-
if (dataMap.height !== void 0) node.height = parseNumber(dataMap.height);
|
|
328
|
+
if (dataMap.x !== void 0) node.x = parseNumber(dataMap.x, "x", "node", id);
|
|
329
|
+
if (dataMap.y !== void 0) node.y = parseNumber(dataMap.y, "y", "node", id);
|
|
330
|
+
if (dataMap.width !== void 0) node.width = parseNumber(dataMap.width, "width", "node", id);
|
|
331
|
+
if (dataMap.height !== void 0) node.height = parseNumber(dataMap.height, "height", "node", id);
|
|
314
332
|
if (dataMap.shape !== void 0) node.shape = dataMap.shape;
|
|
315
333
|
if (dataMap.color !== void 0) node.color = dataMap.color;
|
|
316
334
|
if (dataMap.style !== void 0) node.style = tryParseJSON(dataMap.style);
|
|
317
335
|
if (dataMap.ports !== void 0) node.ports = tryParseJSON(dataMap.ports);
|
|
336
|
+
else if (nodeEl.port !== void 0) node.ports = collectPorts(nodeEl.port);
|
|
318
337
|
return node;
|
|
319
338
|
});
|
|
320
|
-
const
|
|
339
|
+
const usedEdgeIds = new Set(edgeEls.filter((edgeEl) => edgeEl["@_id"] != null).map((edgeEl) => String(edgeEl["@_id"])));
|
|
340
|
+
const edges = edgeEls.map((edgeEl, i) => {
|
|
321
341
|
const dataMap = parseDataElements(edgeEl.data);
|
|
342
|
+
const source = String(edgeEl["@_source"]);
|
|
343
|
+
const target = String(edgeEl["@_target"]);
|
|
344
|
+
let id;
|
|
345
|
+
if (edgeEl["@_id"] != null) id = String(edgeEl["@_id"]);
|
|
346
|
+
else {
|
|
347
|
+
id = `${source}-${target}-${i}`;
|
|
348
|
+
for (let suffix = 0; usedEdgeIds.has(id); suffix++) id = `${source}-${target}-${i}#e${suffix}`;
|
|
349
|
+
usedEdgeIds.add(id);
|
|
350
|
+
}
|
|
322
351
|
const edge = {
|
|
323
352
|
type: "edge",
|
|
324
|
-
id
|
|
325
|
-
sourceId:
|
|
326
|
-
targetId:
|
|
353
|
+
id,
|
|
354
|
+
sourceId: source,
|
|
355
|
+
targetId: target,
|
|
327
356
|
label: dataMap.label ?? "",
|
|
328
357
|
data: dataMap.data !== void 0 ? tryParseJSON(dataMap.data) : void 0
|
|
329
358
|
};
|
|
330
|
-
if (dataMap.weight !== void 0) edge.weight = parseNumber(dataMap.weight);
|
|
331
|
-
if (dataMap.x !== void 0) edge.x = parseNumber(dataMap.x);
|
|
332
|
-
if (dataMap.y !== void 0) edge.y = parseNumber(dataMap.y);
|
|
333
|
-
if (dataMap.width !== void 0) edge.width = parseNumber(dataMap.width);
|
|
334
|
-
if (dataMap.height !== void 0) edge.height = parseNumber(dataMap.height);
|
|
359
|
+
if (dataMap.weight !== void 0) edge.weight = parseNumber(dataMap.weight, "weight", "edge", id);
|
|
360
|
+
if (dataMap.x !== void 0) edge.x = parseNumber(dataMap.x, "x", "edge", id);
|
|
361
|
+
if (dataMap.y !== void 0) edge.y = parseNumber(dataMap.y, "y", "edge", id);
|
|
362
|
+
if (dataMap.width !== void 0) edge.width = parseNumber(dataMap.width, "width", "edge", id);
|
|
363
|
+
if (dataMap.height !== void 0) edge.height = parseNumber(dataMap.height, "height", "edge", id);
|
|
335
364
|
if (dataMap.color !== void 0) edge.color = dataMap.color;
|
|
336
365
|
if (dataMap.style !== void 0) edge.style = tryParseJSON(dataMap.style);
|
|
337
366
|
if (dataMap.sourcePort !== void 0) edge.sourcePort = dataMap.sourcePort;
|
|
367
|
+
else if (edgeEl["@_sourceport"] != null) edge.sourcePort = String(edgeEl["@_sourceport"]);
|
|
338
368
|
if (dataMap.targetPort !== void 0) edge.targetPort = dataMap.targetPort;
|
|
369
|
+
else if (edgeEl["@_targetport"] != null) edge.targetPort = String(edgeEl["@_targetport"]);
|
|
339
370
|
const directedAttr = edgeEl["@_directed"];
|
|
340
371
|
if (directedAttr !== void 0) edge.mode = String(directedAttr) === "false" ? "undirected" : "directed";
|
|
341
372
|
return edge;
|
|
@@ -368,8 +399,28 @@ function tryParseJSON(str) {
|
|
|
368
399
|
return str;
|
|
369
400
|
}
|
|
370
401
|
}
|
|
371
|
-
|
|
372
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Flattens native GraphML <port> elements (which may nest) into our port
|
|
404
|
+
* shape. Standard GraphML ports carry only a name; direction is unknowable,
|
|
405
|
+
* so they import as advisory 'inout' with null data.
|
|
406
|
+
*/
|
|
407
|
+
function collectPorts(portEls) {
|
|
408
|
+
const ports = [];
|
|
409
|
+
for (const portEl of asArray(portEls)) {
|
|
410
|
+
if (portEl?.["@_name"] == null) continue;
|
|
411
|
+
ports.push({
|
|
412
|
+
name: String(portEl["@_name"]),
|
|
413
|
+
direction: "inout",
|
|
414
|
+
data: null
|
|
415
|
+
});
|
|
416
|
+
if (portEl.port !== void 0) ports.push(...collectPorts(portEl.port) ?? []);
|
|
417
|
+
}
|
|
418
|
+
return ports;
|
|
419
|
+
}
|
|
420
|
+
function parseNumber(value, key, kind, ownerId) {
|
|
421
|
+
const parsed = Number(value);
|
|
422
|
+
if (Number.isNaN(parsed)) throw new Error(`GraphML: <data key="${key}"> value "${value}" on ${kind} "${ownerId}" is not a number. Fix the value or remove the attribute.`);
|
|
423
|
+
return parsed;
|
|
373
424
|
}
|
|
374
425
|
function parseDirection(value) {
|
|
375
426
|
return [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createFormatConverter } from "../../converter-
|
|
1
|
+
import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/formats/jgf/index.ts
|
|
4
4
|
/**
|
|
@@ -20,6 +20,7 @@ import { n as createFormatConverter } from "../../converter-udLITX36.mjs";
|
|
|
20
20
|
*/
|
|
21
21
|
function toJGF(graph) {
|
|
22
22
|
const metadata = {};
|
|
23
|
+
if (graph.mode === "bidirectional") metadata.mode = graph.mode;
|
|
23
24
|
if (graph.initialNodeId) metadata.initialNodeId = graph.initialNodeId;
|
|
24
25
|
if (graph.data !== void 0) metadata.data = graph.data;
|
|
25
26
|
if (graph.direction) metadata.direction = graph.direction;
|
|
@@ -49,6 +50,7 @@ function toJGF(graph) {
|
|
|
49
50
|
}),
|
|
50
51
|
edges: graph.edges.map((e) => {
|
|
51
52
|
const meta = {};
|
|
53
|
+
if (e.mode) meta.mode = e.mode;
|
|
52
54
|
if (e.data !== void 0) meta.data = e.data;
|
|
53
55
|
if (e.weight !== void 0) meta.weight = e.weight;
|
|
54
56
|
if (e.x !== void 0) meta.x = e.x;
|
|
@@ -93,7 +95,7 @@ function fromJGF(jgf) {
|
|
|
93
95
|
if (!Array.isArray(g.edges)) throw new Error("JGF: \"graph.edges\" must be an array");
|
|
94
96
|
return {
|
|
95
97
|
id: g.id ?? "",
|
|
96
|
-
mode: g.directed === false ? "undirected" : "directed",
|
|
98
|
+
mode: g.metadata?.mode ?? (g.directed === false ? "undirected" : "directed"),
|
|
97
99
|
initialNodeId: g.metadata?.initialNodeId ?? null,
|
|
98
100
|
data: g.metadata?.data,
|
|
99
101
|
...g.metadata?.direction && { direction: g.metadata.direction },
|
|
@@ -120,6 +122,7 @@ function fromJGF(jgf) {
|
|
|
120
122
|
sourceId: e.source,
|
|
121
123
|
targetId: e.target,
|
|
122
124
|
label: e.label ?? "",
|
|
125
|
+
...e.metadata?.mode && { mode: e.metadata.mode },
|
|
123
126
|
data: e.metadata?.data,
|
|
124
127
|
...e.metadata?.weight !== void 0 && { weight: e.metadata.weight },
|
|
125
128
|
...e.metadata?.x !== void 0 && { x: e.metadata.x },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createFormatConverter } from "../../converter-
|
|
1
|
+
import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/formats/mermaid/shared.ts
|
|
4
4
|
const MERMAID_TO_DIRECTION = {
|
|
@@ -16,11 +16,11 @@ const DIRECTION_TO_MERMAID = {
|
|
|
16
16
|
};
|
|
17
17
|
/** Escape a label for Mermaid output (quotes special chars). */
|
|
18
18
|
function escapeMermaidLabel(s) {
|
|
19
|
-
return s.replace(/\\/g, "\\\\").replace(/"/g, "#quot;").replace(/;/g, "#59;").replace(/#(?!quot;|59;|35;)/g, "#35;");
|
|
19
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, "#quot;").replace(/;/g, "#59;").replace(/\|/g, "#124;").replace(/#(?!quot;|59;|35;|124;)/g, "#35;");
|
|
20
20
|
}
|
|
21
21
|
/** Unescape a Mermaid label back to plain text. */
|
|
22
22
|
function unescapeMermaidLabel(s) {
|
|
23
|
-
return s.replace(/#quot;/g, "\"").replace(/#59;/g, ";").replace(/#35;/g, "#");
|
|
23
|
+
return s.replace(/#quot;/g, "\"").replace(/#59;/g, ";").replace(/#124;/g, "|").replace(/#35;/g, "#");
|
|
24
24
|
}
|
|
25
25
|
/** Generate a deterministic edge ID from source, target, and index. */
|
|
26
26
|
function generateEdgeId(sourceId, targetId, index) {
|
|
@@ -1389,6 +1389,19 @@ const mermaidFlowchartConverter = createFormatConverter(toMermaidFlowchart, from
|
|
|
1389
1389
|
//#endregion
|
|
1390
1390
|
//#region src/formats/mermaid/state.ts
|
|
1391
1391
|
/**
|
|
1392
|
+
* Whether a node is a parallel-region marker. Region nodes are generated with
|
|
1393
|
+
* the exact id `${parentId}_region_${integer}`, so a node only counts when its
|
|
1394
|
+
* id matches that structure for its *actual* parent and that parent is a
|
|
1395
|
+
* parallel state. User ids that merely contain `_region_` (e.g.
|
|
1396
|
+
* `foo_region_bar`) are ordinary states.
|
|
1397
|
+
*/
|
|
1398
|
+
function isParallelRegionNode(node, nodesById) {
|
|
1399
|
+
if (!node || !node.parentId) return false;
|
|
1400
|
+
if (nodesById.get(node.parentId)?.data?.stateType !== "parallel") return false;
|
|
1401
|
+
const prefix = `${node.parentId}_region_`;
|
|
1402
|
+
return node.id.startsWith(prefix) && /^\d+$/.test(node.id.slice(prefix.length));
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1392
1405
|
* Parses a Mermaid state diagram string into a Graph.
|
|
1393
1406
|
*
|
|
1394
1407
|
* @example
|
|
@@ -1410,6 +1423,7 @@ function fromMermaidState(input) {
|
|
|
1410
1423
|
let startCounter = 0;
|
|
1411
1424
|
let endCounter = 0;
|
|
1412
1425
|
let graphDirection;
|
|
1426
|
+
let initialNodeId = null;
|
|
1413
1427
|
const classDefs = {};
|
|
1414
1428
|
const classAssignments = {};
|
|
1415
1429
|
const parentStack = [null];
|
|
@@ -1474,6 +1488,7 @@ function fromMermaidState(input) {
|
|
|
1474
1488
|
const stateId = compositeStateAsMatch[2];
|
|
1475
1489
|
const node = ensureNode(stateId);
|
|
1476
1490
|
node.data.description = description;
|
|
1491
|
+
node.label = description;
|
|
1477
1492
|
parentStack.push(stateId);
|
|
1478
1493
|
continue;
|
|
1479
1494
|
}
|
|
@@ -1492,12 +1507,13 @@ function fromMermaidState(input) {
|
|
|
1492
1507
|
const stateId = stateAsMatch[2];
|
|
1493
1508
|
const node = ensureNode(stateId);
|
|
1494
1509
|
node.data.description = description;
|
|
1510
|
+
node.label = description;
|
|
1495
1511
|
continue;
|
|
1496
1512
|
}
|
|
1497
1513
|
if (line === "}" || line === "end") {
|
|
1498
1514
|
if (parentStack.length > 1) {
|
|
1499
1515
|
const top = parentStack[parentStack.length - 1];
|
|
1500
|
-
if (top &&
|
|
1516
|
+
if (top && isParallelRegionNode(nodeMap.get(top), nodeMap)) parentStack.pop();
|
|
1501
1517
|
parentStack.pop();
|
|
1502
1518
|
}
|
|
1503
1519
|
continue;
|
|
@@ -1506,7 +1522,7 @@ function fromMermaidState(input) {
|
|
|
1506
1522
|
const compositeParent = (() => {
|
|
1507
1523
|
for (let s = parentStack.length - 1; s >= 0; s--) {
|
|
1508
1524
|
const id = parentStack[s];
|
|
1509
|
-
if (id && !
|
|
1525
|
+
if (id && !isParallelRegionNode(nodeMap.get(id), nodeMap)) return id;
|
|
1510
1526
|
}
|
|
1511
1527
|
return null;
|
|
1512
1528
|
})();
|
|
@@ -1540,7 +1556,7 @@ function fromMermaidState(input) {
|
|
|
1540
1556
|
regionCounters.set(compositeParent, 2);
|
|
1541
1557
|
} else {
|
|
1542
1558
|
const top = parentStack[parentStack.length - 1];
|
|
1543
|
-
if (top &&
|
|
1559
|
+
if (top && isParallelRegionNode(nodeMap.get(top), nodeMap)) parentStack.pop();
|
|
1544
1560
|
const nextRegionId = `${compositeParent}_region_${regionIndex}`;
|
|
1545
1561
|
const nextRegion = {
|
|
1546
1562
|
type: "node",
|
|
@@ -1588,6 +1604,7 @@ function fromMermaidState(input) {
|
|
|
1588
1604
|
let targetId = transMatch[3];
|
|
1589
1605
|
const targetClass = transMatch[4];
|
|
1590
1606
|
const label = transMatch[5]?.trim() ?? "";
|
|
1607
|
+
const isTopLevelStart = sourceId === "[*]" && parentStack[parentStack.length - 1] === null;
|
|
1591
1608
|
if (sourceId === "[*]") sourceId = resolveStarNode("source");
|
|
1592
1609
|
else {
|
|
1593
1610
|
ensureNode(sourceId);
|
|
@@ -1603,6 +1620,7 @@ function fromMermaidState(input) {
|
|
|
1603
1620
|
if (!classAssignments[targetId]) classAssignments[targetId] = [];
|
|
1604
1621
|
classAssignments[targetId].push(targetClass);
|
|
1605
1622
|
}
|
|
1623
|
+
if (isTopLevelStart && initialNodeId === null) initialNodeId = targetId;
|
|
1606
1624
|
}
|
|
1607
1625
|
const edgeId = generateEdgeId(sourceId, targetId, edgeCounter++);
|
|
1608
1626
|
edges.push({
|
|
@@ -1676,7 +1694,7 @@ function fromMermaidState(input) {
|
|
|
1676
1694
|
return {
|
|
1677
1695
|
id: "",
|
|
1678
1696
|
mode: "directed",
|
|
1679
|
-
initialNodeId
|
|
1697
|
+
initialNodeId,
|
|
1680
1698
|
nodes: Array.from(nodeMap.values()),
|
|
1681
1699
|
edges,
|
|
1682
1700
|
data: {
|
|
@@ -1699,6 +1717,7 @@ function toMermaidState(graph) {
|
|
|
1699
1717
|
const mDir = DIRECTION_TO_MERMAID[graph.direction];
|
|
1700
1718
|
if (mDir) lines.push(` direction ${mDir}`);
|
|
1701
1719
|
}
|
|
1720
|
+
const nodesById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
1702
1721
|
const childrenMap = /* @__PURE__ */ new Map();
|
|
1703
1722
|
for (const node of graph.nodes) {
|
|
1704
1723
|
const pid = node.parentId ?? null;
|
|
@@ -1707,28 +1726,44 @@ function toMermaidState(graph) {
|
|
|
1707
1726
|
}
|
|
1708
1727
|
const isParent = /* @__PURE__ */ new Set();
|
|
1709
1728
|
for (const node of graph.nodes) if (childrenMap.has(node.id)) isParent.add(node.id);
|
|
1729
|
+
const referencedByEdge = /* @__PURE__ */ new Set();
|
|
1730
|
+
for (const edge of graph.edges) {
|
|
1731
|
+
referencedByEdge.add(edge.sourceId);
|
|
1732
|
+
referencedByEdge.add(edge.targetId);
|
|
1733
|
+
}
|
|
1734
|
+
if (graph.initialNodeId) referencedByEdge.add(graph.initialNodeId);
|
|
1710
1735
|
function writeNodes(parentId, indent) {
|
|
1711
1736
|
const children = childrenMap.get(parentId) ?? [];
|
|
1712
1737
|
for (const node of children) {
|
|
1713
1738
|
if (node.data?.isStart || node.data?.isEnd) continue;
|
|
1714
|
-
if (node
|
|
1715
|
-
|
|
1739
|
+
if (isParallelRegionNode(node, nodesById)) continue;
|
|
1740
|
+
const description = node.data?.description ?? (node.label && node.label !== node.id ? node.label : void 0);
|
|
1741
|
+
let declared = false;
|
|
1742
|
+
if (node.data?.stateType && node.data.stateType !== "parallel") {
|
|
1743
|
+
lines.push(`${indent}state ${node.id} <<${node.data.stateType}>>`);
|
|
1744
|
+
declared = true;
|
|
1745
|
+
}
|
|
1716
1746
|
if (isParent.has(node.id)) {
|
|
1717
|
-
const stateDecl =
|
|
1747
|
+
const stateDecl = description ? `state "${escapeMermaidLabel(description)}" as ${node.id} {` : `state ${node.id} {`;
|
|
1718
1748
|
lines.push(`${indent}${stateDecl}`);
|
|
1719
1749
|
if (node.data?.direction) {
|
|
1720
1750
|
const mDir = DIRECTION_TO_MERMAID[node.data.direction];
|
|
1721
1751
|
if (mDir) lines.push(`${indent} direction ${mDir}`);
|
|
1722
1752
|
}
|
|
1723
1753
|
if (node.data?.stateType === "parallel") {
|
|
1724
|
-
const regions = (childrenMap.get(node.id) ?? []).filter((r) => r
|
|
1754
|
+
const regions = (childrenMap.get(node.id) ?? []).filter((r) => isParallelRegionNode(r, nodesById));
|
|
1725
1755
|
for (let ri = 0; ri < regions.length; ri++) {
|
|
1726
1756
|
if (ri > 0) lines.push(`${indent} --`);
|
|
1727
1757
|
writeNodes(regions[ri].id, indent + " ");
|
|
1728
1758
|
}
|
|
1729
1759
|
} else writeNodes(node.id, indent + " ");
|
|
1730
1760
|
lines.push(`${indent}}`);
|
|
1731
|
-
|
|
1761
|
+
declared = true;
|
|
1762
|
+
} else if (description) {
|
|
1763
|
+
lines.push(`${indent}state "${escapeMermaidLabel(description)}" as ${node.id}`);
|
|
1764
|
+
declared = true;
|
|
1765
|
+
}
|
|
1766
|
+
if (!declared && !referencedByEdge.has(node.id)) lines.push(`${indent}${node.id}`);
|
|
1732
1767
|
if (node.data?.notes) for (const note of node.data.notes) if (note.format === "block" || note.text.includes("\n")) {
|
|
1733
1768
|
lines.push(`${indent}note ${note.position} of ${node.id}`);
|
|
1734
1769
|
for (const noteLine of note.text.split("\n")) lines.push(`${indent} ${escapeMermaidLabel(noteLine)}`);
|
|
@@ -1737,6 +1772,8 @@ function toMermaidState(graph) {
|
|
|
1737
1772
|
}
|
|
1738
1773
|
}
|
|
1739
1774
|
writeNodes(null, " ");
|
|
1775
|
+
const hasTopLevelStart = graph.nodes.some((n) => n.data?.isStart && (n.parentId ?? null) === null);
|
|
1776
|
+
if (graph.initialNodeId && !hasTopLevelStart) lines.push(` [*] --> ${graph.initialNodeId}`);
|
|
1740
1777
|
for (const edge of graph.edges) {
|
|
1741
1778
|
let sourceId = edge.sourceId;
|
|
1742
1779
|
let targetId = edge.targetId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { L as VisualGraphFormatConverter, P as VisualGraph } from "../../types-DNYdIU21.mjs";
|
|
2
2
|
import { EdgeBase, NodeBase } from "@xyflow/system";
|
|
3
3
|
|
|
4
4
|
//#region src/formats/xyflow/index.d.ts
|
|
@@ -19,8 +19,33 @@ function readMetadata(value) {
|
|
|
19
19
|
}
|
|
20
20
|
function readUserData(value) {
|
|
21
21
|
const metadata = readMetadata(value);
|
|
22
|
-
if (metadata
|
|
23
|
-
return
|
|
22
|
+
if (!metadata) return value;
|
|
23
|
+
return "data" in metadata ? metadata.data : void 0;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* React Flow requires parent nodes to appear before their children in the
|
|
27
|
+
* nodes array. Reorders iteratively, keeping authored order otherwise: each
|
|
28
|
+
* pass emits nodes whose parent is already emitted (or absent). Nodes left
|
|
29
|
+
* over by a parentId cycle are appended in authored order rather than hanging.
|
|
30
|
+
*/
|
|
31
|
+
function orderParentsFirst(nodes) {
|
|
32
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
33
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
34
|
+
const result = [];
|
|
35
|
+
let remaining = nodes;
|
|
36
|
+
while (remaining.length > 0) {
|
|
37
|
+
const deferred = [];
|
|
38
|
+
for (const node of remaining) if (!node.parentId || emitted.has(node.parentId) || !nodeIds.has(node.parentId)) {
|
|
39
|
+
result.push(node);
|
|
40
|
+
emitted.add(node.id);
|
|
41
|
+
} else deferred.push(node);
|
|
42
|
+
if (deferred.length === remaining.length) {
|
|
43
|
+
result.push(...deferred);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
remaining = deferred;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
24
49
|
}
|
|
25
50
|
/**
|
|
26
51
|
* Converts a visual graph to xyflow (React Flow / Svelte Flow) format.
|
|
@@ -52,7 +77,7 @@ function toXYFlow(graph) {
|
|
|
52
77
|
direction: graph.direction,
|
|
53
78
|
style: graph.style
|
|
54
79
|
} } },
|
|
55
|
-
nodes: graph.nodes.map((n) => {
|
|
80
|
+
nodes: orderParentsFirst(graph.nodes.map((n) => {
|
|
56
81
|
const node = {
|
|
57
82
|
id: n.id,
|
|
58
83
|
position: {
|
|
@@ -72,7 +97,7 @@ function toXYFlow(graph) {
|
|
|
72
97
|
if (n.width !== void 0) node.width = n.width;
|
|
73
98
|
if (n.height !== void 0) node.height = n.height;
|
|
74
99
|
return node;
|
|
75
|
-
}),
|
|
100
|
+
})),
|
|
76
101
|
edges: graph.edges.map((e) => {
|
|
77
102
|
const edge = {
|
|
78
103
|
id: e.id,
|
|
@@ -83,6 +108,7 @@ function toXYFlow(graph) {
|
|
|
83
108
|
if (e.targetPort) edge.targetHandle = e.targetPort;
|
|
84
109
|
edge.data = withMetadata(e.data, { edge: {
|
|
85
110
|
label: e.label,
|
|
111
|
+
mode: e.mode,
|
|
86
112
|
weight: e.weight,
|
|
87
113
|
color: e.color,
|
|
88
114
|
style: e.style,
|
|
@@ -158,6 +184,7 @@ function fromXYFlow(flow) {
|
|
|
158
184
|
y: metadata?.y ?? 0,
|
|
159
185
|
width: metadata?.width ?? 0,
|
|
160
186
|
height: metadata?.height ?? 0,
|
|
187
|
+
...metadata?.mode !== void 0 && { mode: metadata.mode },
|
|
161
188
|
...metadata?.weight !== void 0 && { weight: metadata.weight },
|
|
162
189
|
...metadata?.color !== void 0 && { color: metadata.color },
|
|
163
190
|
...metadata?.style !== void 0 && { style: metadata.style }
|