@statelyai/graph 0.13.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.
Files changed (57) hide show
  1. package/README.md +57 -26
  2. package/dist/{adjacency-list-Ca0VjKIf.mjs → adjacency-list-GeL1Cu-L.mjs} +5 -3
  3. package/dist/{algorithms-BlM-qoJb.d.mts → algorithms-CsGNehct.d.mts} +137 -2
  4. package/dist/{algorithms-BNDQcHU3.mjs → algorithms-DF1pSQGv.mjs} +1494 -357
  5. package/dist/algorithms.d.mts +2 -2
  6. package/dist/algorithms.mjs +2 -2
  7. package/dist/{converter-Dspillnn.mjs → converter-DyCJJfTe.mjs} +2 -2
  8. package/dist/{edge-list-gKe8-iRa.mjs → edge-list-BcZ0h6zz.mjs} +1 -1
  9. package/dist/format-support.mjs +67 -11
  10. package/dist/formats/adjacency-list/index.d.mts +1 -1
  11. package/dist/formats/adjacency-list/index.mjs +1 -1
  12. package/dist/formats/converter/index.d.mts +1 -60
  13. package/dist/formats/converter/index.mjs +1 -1
  14. package/dist/formats/cytoscape/index.d.mts +1 -1
  15. package/dist/formats/cytoscape/index.mjs +5 -3
  16. package/dist/formats/d2/index.d.mts +109 -0
  17. package/dist/formats/d2/index.mjs +1100 -0
  18. package/dist/formats/d3/index.d.mts +2 -2
  19. package/dist/formats/d3/index.mjs +5 -3
  20. package/dist/formats/dot/index.d.mts +1 -1
  21. package/dist/formats/dot/index.mjs +24 -8
  22. package/dist/formats/edge-list/index.d.mts +1 -1
  23. package/dist/formats/edge-list/index.mjs +1 -1
  24. package/dist/formats/elk/index.d.mts +1 -1
  25. package/dist/formats/elk/index.mjs +23 -16
  26. package/dist/formats/gexf/index.d.mts +1 -1
  27. package/dist/formats/gexf/index.mjs +30 -17
  28. package/dist/formats/gml/index.d.mts +1 -1
  29. package/dist/formats/gml/index.mjs +22 -13
  30. package/dist/formats/graphml/index.d.mts +1 -1
  31. package/dist/formats/graphml/index.mjs +83 -25
  32. package/dist/formats/jgf/index.d.mts +1 -1
  33. package/dist/formats/jgf/index.mjs +6 -3
  34. package/dist/formats/mermaid/index.d.mts +1 -1
  35. package/dist/formats/mermaid/index.mjs +57 -20
  36. package/dist/formats/tgf/index.d.mts +1 -1
  37. package/dist/formats/tgf/index.mjs +2 -2
  38. package/dist/formats/xyflow/index.d.mts +1 -1
  39. package/dist/formats/xyflow/index.mjs +33 -6
  40. package/dist/index-D51lJnt2.d.mts +61 -0
  41. package/dist/index-DWmo1mIp.d.mts +697 -0
  42. package/dist/index.d.mts +6 -631
  43. package/dist/index.mjs +144 -295
  44. package/dist/mode-D8OnHFBk.mjs +15 -0
  45. package/dist/queries-BfXeTXRf.d.mts +547 -0
  46. package/dist/queries-KirMDR7e.mjs +980 -0
  47. package/dist/queries.d.mts +1 -514
  48. package/dist/queries.mjs +1 -766
  49. package/dist/schemas.d.mts +21 -10
  50. package/dist/schemas.mjs +35 -86
  51. package/dist/{types-CnZ01raw.d.mts → types-DNYdIU21.d.mts} +83 -11
  52. package/dist/validate-TtH-x3JV.mjs +190 -0
  53. package/package.json +14 -3
  54. package/schemas/edge.schema.json +11 -0
  55. package/schemas/graph.schema.json +24 -3
  56. package/schemas/node.schema.json +6 -0
  57. package/dist/indexing-DUl3kTqm.mjs +0 -137
@@ -1,4 +1,4 @@
1
- import { h as GraphFormatConverter, u as Graph } from "../../types-CnZ01raw.mjs";
1
+ import { d as Graph, g as GraphFormatConverter } from "../../types-DNYdIU21.mjs";
2
2
 
3
3
  //#region src/formats/d3/index.d.ts
4
4
  interface D3Node {
@@ -12,7 +12,7 @@ interface D3Link {
12
12
  }
13
13
  interface D3Graph {
14
14
  id?: string;
15
- type?: Graph['type'];
15
+ mode?: Graph['mode'];
16
16
  initialNodeId?: string | null;
17
17
  data?: any;
18
18
  direction?: Graph['direction'];
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-Dspillnn.mjs";
1
+ import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
2
2
 
3
3
  //#region src/formats/d3/index.ts
4
4
  /**
@@ -21,7 +21,7 @@ import { n as createFormatConverter } from "../../converter-Dspillnn.mjs";
21
21
  function toD3Graph(graph) {
22
22
  return {
23
23
  ...graph.id && { id: graph.id },
24
- type: graph.type,
24
+ mode: graph.mode,
25
25
  initialNodeId: graph.initialNodeId,
26
26
  ...graph.data !== void 0 && { data: graph.data },
27
27
  ...graph.direction && { direction: graph.direction },
@@ -49,6 +49,7 @@ function toD3Graph(graph) {
49
49
  };
50
50
  if (e.id) link.id = e.id;
51
51
  if (e.label) link.label = e.label;
52
+ if (e.mode) link.mode = e.mode;
52
53
  if (e.data !== void 0) link.data = e.data;
53
54
  if (e.weight !== void 0) link.weight = e.weight;
54
55
  if (e.x !== void 0) link.x = e.x;
@@ -82,7 +83,7 @@ function fromD3Graph(d3) {
82
83
  if (!Array.isArray(d3.links)) throw new Error("D3: \"links\" must be an array");
83
84
  return {
84
85
  id: d3.id ?? "",
85
- type: d3.type === "undirected" ? "undirected" : "directed",
86
+ mode: d3.mode ?? "directed",
86
87
  initialNodeId: d3.initialNodeId ?? null,
87
88
  data: d3.data,
88
89
  ...d3.direction && { direction: d3.direction },
@@ -109,6 +110,7 @@ function fromD3Graph(d3) {
109
110
  sourceId: typeof l.source === "string" ? l.source : l.source.id,
110
111
  targetId: typeof l.target === "string" ? l.target : l.target.id,
111
112
  label: l.label ?? "",
113
+ ...l.mode && { mode: l.mode },
112
114
  data: l.data,
113
115
  ...l.weight !== void 0 && { weight: l.weight },
114
116
  ...l.x !== void 0 && { x: l.x },
@@ -1,4 +1,4 @@
1
- import { h as GraphFormatConverter, u as Graph } from "../../types-CnZ01raw.mjs";
1
+ import { d as Graph, g as GraphFormatConverter } from "../../types-DNYdIU21.mjs";
2
2
 
3
3
  //#region src/formats/dot/index.d.ts
4
4
 
@@ -1,15 +1,31 @@
1
- import { n as createFormatConverter } from "../../converter-Dspillnn.mjs";
1
+ import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
2
2
  import parse from "dotparser";
3
3
 
4
4
  //#region src/formats/dot/index.ts
5
+ /** DOT reserved keywords — must be quoted when used as identifiers. */
6
+ const DOT_KEYWORDS = new Set([
7
+ "node",
8
+ "edge",
9
+ "graph",
10
+ "digraph",
11
+ "subgraph",
12
+ "strict"
13
+ ]);
5
14
  /** Escape a DOT identifier */
6
15
  function escapeId(id) {
7
- if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id)) return id;
16
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id) && !DOT_KEYWORDS.has(id.toLowerCase())) return id;
8
17
  return `"${id.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
9
18
  }
10
19
  /** Escape a DOT label string */
11
20
  function escapeLabel(label) {
12
- return label.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
21
+ return label.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
22
+ }
23
+ /**
24
+ * Invert {@link escapeLabel}. dotparser unescapes `\"` itself but passes
25
+ * `\\` and `\n` through verbatim.
26
+ */
27
+ function unescapeLabel(label) {
28
+ return label.replace(/\\(\\|n)/g, (_, ch) => ch === "n" ? "\n" : "\\");
13
29
  }
14
30
  function formatEndpoint(id, port) {
15
31
  return `${escapeId(id)}${port ? `:${escapeId(port)}` : ""}`;
@@ -50,7 +66,7 @@ const SHAPE_TO_DOT = {
50
66
  * ```
51
67
  */
52
68
  function toDOT(graph) {
53
- const isDirected = graph.type === "directed";
69
+ const isDirected = graph.mode !== "undirected";
54
70
  const keyword = isDirected ? "digraph" : "graph";
55
71
  const edgeOp = isDirected ? "->" : "--";
56
72
  const lines = [];
@@ -112,7 +128,7 @@ function nodeFromAttrs(id, attrs, defaults, parentId) {
112
128
  ...defaults,
113
129
  ...attrs
114
130
  };
115
- const label = merged["label"] ?? "";
131
+ const label = unescapeLabel(merged["label"] ?? "");
116
132
  const rawShape = merged["shape"];
117
133
  const shape = rawShape ? DOT_TO_SHAPE[rawShape] ?? rawShape : void 0;
118
134
  const color = merged["fillcolor"] ?? merged["color"] ?? void 0;
@@ -235,7 +251,7 @@ function fromDOT(dot) {
235
251
  id: `e${edgeIdx++}`,
236
252
  sourceId: source.id,
237
253
  targetId: target.id,
238
- label: mergedEdgeAttrs["label"] ?? "",
254
+ label: unescapeLabel(mergedEdgeAttrs["label"] ?? ""),
239
255
  data: void 0,
240
256
  ...source.port && { sourcePort: source.port },
241
257
  ...target.port && { targetPort: target.port },
@@ -251,7 +267,7 @@ function fromDOT(dot) {
251
267
  let subLabel = "";
252
268
  for (const child of stmt.children) if (child.type === "attr_stmt" && child.target === "graph") {
253
269
  const ga = attrsToMap(child.attr_list);
254
- if (ga["label"]) subLabel = ga["label"];
270
+ if (ga["label"]) subLabel = unescapeLabel(ga["label"]);
255
271
  }
256
272
  const subNode = {
257
273
  type: "node",
@@ -270,7 +286,7 @@ function fromDOT(dot) {
270
286
  walkChildren(root.children, null, {}, {});
271
287
  return {
272
288
  id: root.id ?? "",
273
- type: isDirected ? "directed" : "undirected",
289
+ mode: isDirected ? "directed" : "undirected",
274
290
  initialNodeId: null,
275
291
  nodes: [...nodeMap.values()],
276
292
  edges,
@@ -1,4 +1,4 @@
1
- import { u as Graph } from "../../types-CnZ01raw.mjs";
1
+ import { d as Graph } from "../../types-DNYdIU21.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-gKe8-iRa.mjs";
1
+ import { n as toEdgeList, t as fromEdgeList } from "../../edge-list-BcZ0h6zz.mjs";
2
2
 
3
3
  export { fromEdgeList, toEdgeList };
@@ -1,4 +1,4 @@
1
- import { P as VisualGraphFormatConverter, j as VisualGraph } from "../../types-CnZ01raw.mjs";
1
+ import { L as VisualGraphFormatConverter, P as VisualGraph } from "../../types-DNYdIU21.mjs";
2
2
  import { ElkEdge, ElkEdgeSection, ElkExtendedEdge, ElkGraphElement, ElkLabel, ElkNode, ElkNode as ElkNode$1, ElkPoint, ElkPort, ElkPrimitiveEdge, ElkShape, LayoutOptions } from "elkjs/lib/elk-api";
3
3
 
4
4
  //#region src/formats/elk/index.d.ts
@@ -1,4 +1,4 @@
1
- import { getChildren } from "../../queries.mjs";
1
+ import { n as getChildren } from "../../queries-KirMDR7e.mjs";
2
2
 
3
3
  //#region src/formats/elk/index.ts
4
4
  const STATELYAI_METADATA_KEY = "statelyai.metadata";
@@ -33,8 +33,8 @@ const ELK_TO_DIRECTION = {
33
33
  function convertEdge(edge) {
34
34
  const elkEdge = {
35
35
  id: edge.id,
36
- sources: [edge.sourcePort ?? edge.sourceId],
37
- targets: [edge.targetPort ?? edge.targetId]
36
+ sources: [edge.sourcePort != null ? `${edge.sourceId}__${edge.sourcePort}` : edge.sourceId],
37
+ targets: [edge.targetPort != null ? `${edge.targetId}__${edge.targetPort}` : edge.targetId]
38
38
  };
39
39
  if (edge.label) elkEdge.labels = [{ text: edge.label }];
40
40
  return addMetadata(elkEdge, { edge: {
@@ -43,6 +43,7 @@ function convertEdge(edge) {
43
43
  sourcePort: edge.sourcePort,
44
44
  targetPort: edge.targetPort,
45
45
  label: edge.label,
46
+ mode: edge.mode,
46
47
  data: edge.data,
47
48
  weight: edge.weight,
48
49
  color: edge.color,
@@ -53,9 +54,9 @@ function convertEdge(edge) {
53
54
  height: edge.height
54
55
  } });
55
56
  }
56
- function convertPort(port) {
57
+ function convertPort(nodeId, port) {
57
58
  const elkPort = {
58
- id: port.name,
59
+ id: `${nodeId}__${port.name}`,
59
60
  x: port.x,
60
61
  y: port.y,
61
62
  width: port.width,
@@ -64,6 +65,7 @@ function convertPort(port) {
64
65
  if (port.label) elkPort.labels = [{ text: port.label }];
65
66
  if (port.direction !== "inout") elkPort.layoutOptions = { "org.eclipse.elk.port.side": port.direction === "in" ? "WEST" : "EAST" };
66
67
  return addMetadata(elkPort, { port: {
68
+ name: port.name,
67
69
  data: port.data,
68
70
  style: port.style
69
71
  } });
@@ -77,7 +79,7 @@ function convertNode(graph, node) {
77
79
  height: node.height
78
80
  };
79
81
  if (node.label) elkNode.labels = [{ text: node.label }];
80
- if (node.ports && node.ports.length > 0) elkNode.ports = node.ports.map(convertPort);
82
+ if (node.ports && node.ports.length > 0) elkNode.ports = node.ports.map((port) => convertPort(node.id, port));
81
83
  addMetadata(elkNode, { node: {
82
84
  initialNodeId: node.initialNodeId,
83
85
  data: node.data,
@@ -128,7 +130,7 @@ function toELK(graph) {
128
130
  if (elkDir) root.layoutOptions = { "elk.direction": elkDir };
129
131
  addMetadata(root, { graph: {
130
132
  id: graph.id,
131
- type: graph.type,
133
+ mode: graph.mode,
132
134
  initialNodeId: graph.initialNodeId,
133
135
  data: graph.data,
134
136
  direction: graph.direction,
@@ -166,14 +168,18 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
166
168
  ...metadata?.style !== void 0 && { style: metadata.style }
167
169
  };
168
170
  if (child.ports && child.ports.length > 0) node.ports = child.ports.map((elkPort) => {
169
- portOwner.set(elkPort.id, child.id);
170
171
  const metadata$1 = readMetadata(elkPort)?.port;
172
+ const portName = metadata$1?.name ?? elkPort.id;
173
+ portOwner.set(elkPort.id, {
174
+ nodeId: child.id,
175
+ portName
176
+ });
171
177
  const sideOpt = elkPort.layoutOptions?.["org.eclipse.elk.port.side"];
172
178
  let direction = "inout";
173
179
  if (sideOpt === "WEST") direction = "in";
174
180
  else if (sideOpt === "EAST") direction = "out";
175
181
  return {
176
- name: elkPort.id,
182
+ name: portName,
177
183
  direction,
178
184
  label: elkPort.labels?.[0]?.text,
179
185
  data: metadata$1 && "data" in metadata$1 ? metadata$1.data : void 0,
@@ -189,27 +195,28 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
189
195
  }
190
196
  if (elkNode.edges) for (const elkEdge of elkNode.edges) for (const source of elkEdge.sources) for (const target of elkEdge.targets) {
191
197
  const metadata = readMetadata(elkEdge)?.edge;
192
- const sourceNodeId = portOwner.get(source);
193
- const targetNodeId = portOwner.get(target);
198
+ const sourceOwner = portOwner.get(source);
199
+ const targetOwner = portOwner.get(target);
194
200
  const edge = {
195
201
  type: "edge",
196
202
  id: elkEdge.id ?? `e${edgeIdx.value++}`,
197
- sourceId: metadata?.sourceId ?? sourceNodeId ?? source,
198
- targetId: metadata?.targetId ?? targetNodeId ?? target,
203
+ sourceId: metadata?.sourceId ?? sourceOwner?.nodeId ?? source,
204
+ targetId: metadata?.targetId ?? targetOwner?.nodeId ?? target,
199
205
  label: metadata && "label" in metadata ? metadata.label : elkEdge.labels?.[0]?.text ?? "",
200
206
  data: metadata && "data" in metadata ? metadata.data : void 0,
201
207
  x: metadata?.x ?? 0,
202
208
  y: metadata?.y ?? 0,
203
209
  width: metadata?.width ?? 0,
204
210
  height: metadata?.height ?? 0,
211
+ ...metadata?.mode !== void 0 && { mode: metadata.mode },
205
212
  ...metadata?.weight !== void 0 && { weight: metadata.weight },
206
213
  ...metadata?.color !== void 0 && { color: metadata.color },
207
214
  ...metadata?.style !== void 0 && { style: metadata.style }
208
215
  };
209
216
  if (metadata && "sourcePort" in metadata) edge.sourcePort = metadata.sourcePort;
210
- else if (sourceNodeId) edge.sourcePort = source;
217
+ else if (sourceOwner) edge.sourcePort = sourceOwner.portName;
211
218
  if (metadata && "targetPort" in metadata) edge.targetPort = metadata.targetPort;
212
- else if (targetNodeId) edge.targetPort = target;
219
+ else if (targetOwner) edge.targetPort = targetOwner.portName;
213
220
  edges.push(edge);
214
221
  }
215
222
  }
@@ -244,7 +251,7 @@ function fromELK(elkRoot) {
244
251
  const direction = graphMetadata?.direction ?? (elkDir ? ELK_TO_DIRECTION[elkDir] : void 0) ?? "down";
245
252
  return {
246
253
  id: graphMetadata?.id ?? elkRoot.id,
247
- type: graphMetadata?.type === "undirected" ? "undirected" : "directed",
254
+ mode: graphMetadata?.mode ?? "directed",
248
255
  initialNodeId: graphMetadata && "initialNodeId" in graphMetadata ? graphMetadata.initialNodeId : null,
249
256
  nodes,
250
257
  edges: [...seenEdges.values()],
@@ -1,4 +1,4 @@
1
- import { h as GraphFormatConverter, u as Graph } from "../../types-CnZ01raw.mjs";
1
+ import { d as Graph, g as GraphFormatConverter } from "../../types-DNYdIU21.mjs";
2
2
 
3
3
  //#region src/formats/gexf/index.d.ts
4
4
  declare function toGEXF(graph: Graph): string;
@@ -1,4 +1,5 @@
1
- import { n as createFormatConverter } from "../../converter-Dspillnn.mjs";
1
+ import { t as getEdgeMode } from "../../mode-D8OnHFBk.mjs";
2
+ import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
2
3
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
3
4
 
4
5
  //#region src/formats/gexf/index.ts
@@ -128,7 +129,7 @@ function toGEXF(graph) {
128
129
  });
129
130
  const node = {
130
131
  "@_id": n.id,
131
- "@_label": n.label || n.id
132
+ "@_label": n.label ?? ""
132
133
  };
133
134
  if (n.parentId) node["@_pid"] = n.parentId;
134
135
  if (attvalues.length > 0) node.attvalues = { attvalue: attvalues };
@@ -147,12 +148,15 @@ function toGEXF(graph) {
147
148
  if (n.width !== void 0 || n.height !== void 0) node["viz:size"] = { "@_value": n.width ?? n.height ?? 0 };
148
149
  return node;
149
150
  });
151
+ const graphDirected = graph.mode !== "undirected";
150
152
  const edges = graph.edges.map((e) => {
151
153
  const edge = {
152
154
  "@_id": e.id,
153
155
  "@_source": e.sourceId,
154
156
  "@_target": e.targetId
155
157
  };
158
+ const edgeDirected = getEdgeMode(graph, e) !== "undirected";
159
+ if (edgeDirected !== graphDirected) edge["@_type"] = edgeDirected ? "directed" : "undirected";
156
160
  if (e.label) edge["@_label"] = e.label;
157
161
  if (e.data !== void 0) edge.attvalues = { attvalue: [{
158
162
  "@_for": "a_edgeData",
@@ -212,7 +216,7 @@ function toGEXF(graph) {
212
216
  "@_xmlns:viz": "http://gexf.net/1.3/viz",
213
217
  "@_version": "1.3",
214
218
  graph: {
215
- "@_defaultedgetype": graph.type === "directed" ? "directed" : "undirected",
219
+ "@_defaultedgetype": graph.mode === "undirected" ? "undirected" : "directed",
216
220
  ...graph.id && { "@_id": graph.id },
217
221
  ...graph.initialNodeId && { "@_initialNodeId": graph.initialNodeId },
218
222
  ...graph.direction && { "@_direction": graph.direction },
@@ -246,7 +250,8 @@ function fromGEXF(xml) {
246
250
  "attribute",
247
251
  "attvalue",
248
252
  "attributes"
249
- ].includes(name)
253
+ ].includes(name),
254
+ trimValues: false
250
255
  });
251
256
  let parsed;
252
257
  try {
@@ -279,16 +284,16 @@ function fromGEXF(xml) {
279
284
  if (attvals["ports"] !== void 0) node.ports = tryParseJSON(attvals["ports"]);
280
285
  const pos = n["viz:position"];
281
286
  if (pos) {
282
- node.x = Number(pos["@_x"] ?? 0);
283
- node.y = Number(pos["@_y"] ?? 0);
287
+ node.x = parseNumber(pos["@_x"] ?? 0, "<viz:position> x", "node", id);
288
+ node.y = parseNumber(pos["@_y"] ?? 0, "<viz:position> y", "node", id);
284
289
  }
285
290
  const size = n["viz:size"];
286
291
  if (size) {
287
- node.width = Number(size["@_value"] ?? 0);
288
- node.height = Number(size["@_value"] ?? 0);
292
+ node.width = parseNumber(size["@_value"] ?? 0, "<viz:size>", "node", id);
293
+ node.height = node.width;
289
294
  }
290
- if (attvals["width"] !== void 0) node.width = Number(attvals["width"]);
291
- if (attvals["height"] !== void 0) node.height = Number(attvals["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);
292
297
  const color = n["viz:color"];
293
298
  if (color) {
294
299
  const r = Number(color["@_r"] ?? 0);
@@ -304,18 +309,19 @@ function fromGEXF(xml) {
304
309
  parseNodes(graphEl.nodes?.node ?? graphEl.node, null);
305
310
  const edges = asArray(graphEl.edges?.edge ?? graphEl.edge).map((e, i) => {
306
311
  const attvals = getAttValues(e, attrMap);
312
+ const id = String(e["@_id"] ?? `e${i}`);
307
313
  const edge = {
308
314
  type: "edge",
309
- id: String(e["@_id"] ?? `e${i}`),
315
+ id,
310
316
  sourceId: String(e["@_source"]),
311
317
  targetId: String(e["@_target"]),
312
318
  label: e["@_label"] ?? "",
313
319
  data: attvals["data"] !== void 0 ? tryParseJSON(attvals["data"]) : void 0,
314
- ...attvals["weight"] !== void 0 && { weight: Number(attvals["weight"]) },
315
- ...attvals["x"] !== void 0 && { x: Number(attvals["x"]) },
316
- ...attvals["y"] !== void 0 && { y: Number(attvals["y"]) },
317
- ...attvals["width"] !== void 0 && { width: Number(attvals["width"]) },
318
- ...attvals["height"] !== void 0 && { height: Number(attvals["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) },
319
325
  ...attvals["style"] !== void 0 && { style: tryParseJSON(attvals["style"]) },
320
326
  ...attvals["sourcePort"] !== void 0 && { sourcePort: attvals["sourcePort"] },
321
327
  ...attvals["targetPort"] !== void 0 && { targetPort: attvals["targetPort"] }
@@ -327,11 +333,13 @@ function fromGEXF(xml) {
327
333
  const b = Number(color["@_b"] ?? 0);
328
334
  edge.color = `#${hex(r)}${hex(g)}${hex(b)}`;
329
335
  }
336
+ const typeAttr = e["@_type"];
337
+ if (typeAttr !== void 0) edge.mode = String(typeAttr) === "undirected" ? "undirected" : "directed";
330
338
  return edge;
331
339
  });
332
340
  return {
333
341
  id: String(graphEl["@_id"] ?? ""),
334
- type: graphType,
342
+ mode: graphType,
335
343
  initialNodeId: graphEl["@_initialNodeId"] ?? null,
336
344
  nodes,
337
345
  edges,
@@ -360,6 +368,11 @@ function tryParseJSON(str) {
360
368
  return str;
361
369
  }
362
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
+ }
363
376
  function hex(n) {
364
377
  return n.toString(16).padStart(2, "0");
365
378
  }
@@ -1,4 +1,4 @@
1
- import { h as GraphFormatConverter, u as Graph } from "../../types-CnZ01raw.mjs";
1
+ import { d as Graph, g as GraphFormatConverter } from "../../types-DNYdIU21.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-Dspillnn.mjs";
1
+ import { n as createFormatConverter } from "../../converter-DyCJJfTe.mjs";
2
2
 
3
3
  //#region src/formats/gml/index.ts
4
4
  /**
@@ -26,7 +26,8 @@ import { n as createFormatConverter } from "../../converter-Dspillnn.mjs";
26
26
  function toGML(graph) {
27
27
  const lines = [];
28
28
  lines.push("graph [");
29
- lines.push(` directed ${graph.type === "directed" ? 1 : 0}`);
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: String(e["id"] ?? `e${edges.length}`),
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: Number(e["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
- type: 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,4 +1,4 @@
1
- import { h as GraphFormatConverter, u as Graph } from "../../types-CnZ01raw.mjs";
1
+ import { d as Graph, g as GraphFormatConverter } from "../../types-DNYdIU21.mjs";
2
2
 
3
3
  //#region src/formats/graphml/index.d.ts
4
4
  declare function toGraphML(graph: Graph): string;