@statelyai/graph 0.11.1 → 0.13.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.
@@ -20,14 +20,26 @@ import { n as createFormatConverter } from "../../converter-Dspillnn.mjs";
20
20
  */
21
21
  function toD3Graph(graph) {
22
22
  return {
23
+ ...graph.id && { id: graph.id },
24
+ type: graph.type,
25
+ initialNodeId: graph.initialNodeId,
26
+ ...graph.data !== void 0 && { data: graph.data },
27
+ ...graph.direction && { direction: graph.direction },
28
+ ...graph.style !== void 0 && { style: graph.style },
23
29
  nodes: graph.nodes.map((n) => {
24
30
  const node = { id: n.id };
31
+ if (n.parentId) node.parentId = n.parentId;
32
+ if (n.initialNodeId) node.initialNodeId = n.initialNodeId;
25
33
  if (n.label) node.label = n.label;
26
34
  if (n.data !== void 0) node.data = n.data;
27
35
  if (n.x !== void 0) node.x = n.x;
28
36
  if (n.y !== void 0) node.y = n.y;
37
+ if (n.width !== void 0) node.width = n.width;
38
+ if (n.height !== void 0) node.height = n.height;
29
39
  if (n.color) node.color = n.color;
30
40
  if (n.shape) node.shape = n.shape;
41
+ if (n.style !== void 0) node.style = n.style;
42
+ if (n.ports !== void 0) node.ports = n.ports;
31
43
  return node;
32
44
  }),
33
45
  links: graph.edges.map((e) => {
@@ -38,7 +50,15 @@ function toD3Graph(graph) {
38
50
  if (e.id) link.id = e.id;
39
51
  if (e.label) link.label = e.label;
40
52
  if (e.data !== void 0) link.data = e.data;
53
+ if (e.weight !== void 0) link.weight = e.weight;
54
+ if (e.x !== void 0) link.x = e.x;
55
+ if (e.y !== void 0) link.y = e.y;
56
+ if (e.width !== void 0) link.width = e.width;
57
+ if (e.height !== void 0) link.height = e.height;
41
58
  if (e.color) link.color = e.color;
59
+ if (e.style !== void 0) link.style = e.style;
60
+ if (e.sourcePort !== void 0) link.sourcePort = e.sourcePort;
61
+ if (e.targetPort !== void 0) link.targetPort = e.targetPort;
42
62
  return link;
43
63
  })
44
64
  };
@@ -61,21 +81,27 @@ function fromD3Graph(d3) {
61
81
  if (!Array.isArray(d3.nodes)) throw new Error("D3: \"nodes\" must be an array");
62
82
  if (!Array.isArray(d3.links)) throw new Error("D3: \"links\" must be an array");
63
83
  return {
64
- id: "",
65
- type: "directed",
66
- initialNodeId: null,
67
- data: void 0,
84
+ id: d3.id ?? "",
85
+ type: d3.type === "undirected" ? "undirected" : "directed",
86
+ initialNodeId: d3.initialNodeId ?? null,
87
+ data: d3.data,
88
+ ...d3.direction && { direction: d3.direction },
89
+ ...d3.style !== void 0 && { style: d3.style },
68
90
  nodes: d3.nodes.map((n) => ({
69
91
  type: "node",
70
92
  id: n.id,
71
- parentId: null,
72
- initialNodeId: null,
93
+ parentId: n.parentId ?? null,
94
+ initialNodeId: n.initialNodeId ?? null,
73
95
  label: n.label ?? "",
74
96
  data: n.data,
75
97
  ...n.x !== void 0 && { x: n.x },
76
98
  ...n.y !== void 0 && { y: n.y },
99
+ ...n.width !== void 0 && { width: n.width },
100
+ ...n.height !== void 0 && { height: n.height },
77
101
  ...n.color && { color: n.color },
78
- ...n.shape && { shape: n.shape }
102
+ ...n.shape && { shape: n.shape },
103
+ ...n.style !== void 0 && { style: n.style },
104
+ ...n.ports !== void 0 && { ports: n.ports }
79
105
  })),
80
106
  edges: d3.links.map((l, i) => ({
81
107
  type: "edge",
@@ -84,7 +110,15 @@ function fromD3Graph(d3) {
84
110
  targetId: typeof l.target === "string" ? l.target : l.target.id,
85
111
  label: l.label ?? "",
86
112
  data: l.data,
87
- ...l.color && { color: l.color }
113
+ ...l.weight !== void 0 && { weight: l.weight },
114
+ ...l.x !== void 0 && { x: l.x },
115
+ ...l.y !== void 0 && { y: l.y },
116
+ ...l.width !== void 0 && { width: l.width },
117
+ ...l.height !== void 0 && { height: l.height },
118
+ ...l.color && { color: l.color },
119
+ ...l.style !== void 0 && { style: l.style },
120
+ ...l.sourcePort !== void 0 && { sourcePort: l.sourcePort },
121
+ ...l.targetPort !== void 0 && { targetPort: l.targetPort }
88
122
  }))
89
123
  };
90
124
  }
@@ -11,6 +11,9 @@ function escapeId(id) {
11
11
  function escapeLabel(label) {
12
12
  return label.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
13
13
  }
14
+ function formatEndpoint(id, port) {
15
+ return `${escapeId(id)}${port ? `:${escapeId(port)}` : ""}`;
16
+ }
14
17
  const DIRECTION_TO_RANKDIR = {
15
18
  down: "TB",
16
19
  up: "BT",
@@ -72,7 +75,7 @@ function toDOT(graph) {
72
75
  if (edge.label) attrs.push(`label="${escapeLabel(edge.label)}"`);
73
76
  if (edge.color) attrs.push(`color="${escapeLabel(edge.color)}"`);
74
77
  const attrStr = attrs.length > 0 ? ` [${attrs.join(", ")}]` : "";
75
- lines.push(` ${escapeId(edge.sourceId)} ${edgeOp} ${escapeId(edge.targetId)}${attrStr};`);
78
+ lines.push(` ${formatEndpoint(edge.sourceId, edge.sourcePort)} ${edgeOp} ${formatEndpoint(edge.targetId, edge.targetPort)}${attrStr};`);
76
79
  }
77
80
  lines.push("}");
78
81
  return lines.join("\n");
@@ -95,6 +98,10 @@ const DOT_TO_SHAPE = {
95
98
  cylinder: "cylinder",
96
99
  parallelogram: "parallelogram"
97
100
  };
101
+ function getPortId(nodeId) {
102
+ const port = nodeId.port;
103
+ return typeof port?.id === "string" ? port.id : void 0;
104
+ }
98
105
  function attrsToMap(attrList) {
99
106
  const map = {};
100
107
  for (const a of attrList) map[a.id] = String(a.eq);
@@ -209,24 +216,29 @@ function fromDOT(dot) {
209
216
  const endpointGroups = [];
210
217
  for (const item of stmt.edge_list) if (item.type === "node_id") {
211
218
  ensureNode(item.id, parentId, nd);
212
- endpointGroups.push([item.id]);
219
+ endpointGroups.push([{
220
+ id: item.id,
221
+ ...getPortId(item) && { port: getPortId(item) }
222
+ }]);
213
223
  } else if (item.type === "subgraph") {
214
224
  walkChildren(item.children, parentId, nd, ed);
215
225
  const subNodeIds = getNodeIdsFromSubgraph(item.children);
216
226
  for (const subNodeId of subNodeIds) ensureNode(subNodeId, parentId, nd);
217
- if (subNodeIds.length > 0) endpointGroups.push(subNodeIds);
227
+ if (subNodeIds.length > 0) endpointGroups.push(subNodeIds.map((id) => ({ id })));
218
228
  }
219
229
  for (let i = 0; i < endpointGroups.length - 1; i++) {
220
230
  const left = endpointGroups[i];
221
231
  const right = endpointGroups[i + 1];
222
- for (const sourceId of left) for (const targetId of right) {
232
+ for (const source of left) for (const target of right) {
223
233
  const edge = {
224
234
  type: "edge",
225
235
  id: `e${edgeIdx++}`,
226
- sourceId,
227
- targetId,
236
+ sourceId: source.id,
237
+ targetId: target.id,
228
238
  label: mergedEdgeAttrs["label"] ?? "",
229
239
  data: void 0,
240
+ ...source.port && { sourcePort: source.port },
241
+ ...target.port && { targetPort: target.port },
230
242
  ...mergedEdgeAttrs["color"] && { color: mergedEdgeAttrs["color"] }
231
243
  };
232
244
  edges.push(edge);
@@ -1,6 +1,23 @@
1
1
  import { getChildren } from "../../queries.mjs";
2
2
 
3
3
  //#region src/formats/elk/index.ts
4
+ const STATELYAI_METADATA_KEY = "statelyai.metadata";
5
+ function addMetadata(target, metadata) {
6
+ target.layoutOptions = {
7
+ ...target.layoutOptions ?? {},
8
+ [STATELYAI_METADATA_KEY]: JSON.stringify(metadata)
9
+ };
10
+ return target;
11
+ }
12
+ function readMetadata(value) {
13
+ const raw = value.layoutOptions?.[STATELYAI_METADATA_KEY];
14
+ if (typeof raw !== "string") return void 0;
15
+ try {
16
+ return JSON.parse(raw);
17
+ } catch {
18
+ return;
19
+ }
20
+ }
4
21
  const DIRECTION_TO_ELK = {
5
22
  down: "DOWN",
6
23
  up: "UP",
@@ -20,7 +37,21 @@ function convertEdge(edge) {
20
37
  targets: [edge.targetPort ?? edge.targetId]
21
38
  };
22
39
  if (edge.label) elkEdge.labels = [{ text: edge.label }];
23
- return elkEdge;
40
+ return addMetadata(elkEdge, { edge: {
41
+ sourceId: edge.sourceId,
42
+ targetId: edge.targetId,
43
+ sourcePort: edge.sourcePort,
44
+ targetPort: edge.targetPort,
45
+ label: edge.label,
46
+ data: edge.data,
47
+ weight: edge.weight,
48
+ color: edge.color,
49
+ style: edge.style,
50
+ x: edge.x,
51
+ y: edge.y,
52
+ width: edge.width,
53
+ height: edge.height
54
+ } });
24
55
  }
25
56
  function convertPort(port) {
26
57
  const elkPort = {
@@ -32,7 +63,10 @@ function convertPort(port) {
32
63
  };
33
64
  if (port.label) elkPort.labels = [{ text: port.label }];
34
65
  if (port.direction !== "inout") elkPort.layoutOptions = { "org.eclipse.elk.port.side": port.direction === "in" ? "WEST" : "EAST" };
35
- return elkPort;
66
+ return addMetadata(elkPort, { port: {
67
+ data: port.data,
68
+ style: port.style
69
+ } });
36
70
  }
37
71
  function convertNode(graph, node) {
38
72
  const elkNode = {
@@ -44,6 +78,13 @@ function convertNode(graph, node) {
44
78
  };
45
79
  if (node.label) elkNode.labels = [{ text: node.label }];
46
80
  if (node.ports && node.ports.length > 0) elkNode.ports = node.ports.map(convertPort);
81
+ addMetadata(elkNode, { node: {
82
+ initialNodeId: node.initialNodeId,
83
+ data: node.data,
84
+ shape: node.shape,
85
+ color: node.color,
86
+ style: node.style
87
+ } });
47
88
  const children = getChildren(graph, node.id);
48
89
  if (children.length > 0) {
49
90
  elkNode.children = children.map((child) => convertNode(graph, child));
@@ -85,6 +126,14 @@ function toELK(graph) {
85
126
  const root = { id: graph.id };
86
127
  const elkDir = DIRECTION_TO_ELK[graph.direction];
87
128
  if (elkDir) root.layoutOptions = { "elk.direction": elkDir };
129
+ addMetadata(root, { graph: {
130
+ id: graph.id,
131
+ type: graph.type,
132
+ initialNodeId: graph.initialNodeId,
133
+ data: graph.data,
134
+ direction: graph.direction,
135
+ style: graph.style
136
+ } });
88
137
  const roots = getChildren(graph, null);
89
138
  if (roots.length > 0) root.children = roots.map((node) => convertNode(graph, node));
90
139
  const allInnerEdgeIds = /* @__PURE__ */ new Set();
@@ -100,20 +149,25 @@ function toELK(graph) {
100
149
  function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
101
150
  if (elkNode.children) for (const child of elkNode.children) {
102
151
  const label = child.labels?.[0]?.text ?? "";
152
+ const metadata = readMetadata(child)?.node;
103
153
  const node = {
104
154
  type: "node",
105
155
  id: child.id,
106
156
  parentId,
107
- initialNodeId: null,
157
+ initialNodeId: metadata && "initialNodeId" in metadata ? metadata.initialNodeId : null,
108
158
  label,
109
- data: void 0,
159
+ data: metadata && "data" in metadata ? metadata.data : void 0,
110
160
  x: child.x ?? 0,
111
161
  y: child.y ?? 0,
112
162
  width: child.width ?? 0,
113
- height: child.height ?? 0
163
+ height: child.height ?? 0,
164
+ ...metadata?.shape !== void 0 && { shape: metadata.shape },
165
+ ...metadata?.color !== void 0 && { color: metadata.color },
166
+ ...metadata?.style !== void 0 && { style: metadata.style }
114
167
  };
115
168
  if (child.ports && child.ports.length > 0) node.ports = child.ports.map((elkPort) => {
116
169
  portOwner.set(elkPort.id, child.id);
170
+ const metadata$1 = readMetadata(elkPort)?.port;
117
171
  const sideOpt = elkPort.layoutOptions?.["org.eclipse.elk.port.side"];
118
172
  let direction = "inout";
119
173
  if (sideOpt === "WEST") direction = "in";
@@ -122,33 +176,40 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
122
176
  name: elkPort.id,
123
177
  direction,
124
178
  label: elkPort.labels?.[0]?.text,
125
- data: void 0,
179
+ data: metadata$1 && "data" in metadata$1 ? metadata$1.data : void 0,
126
180
  x: elkPort.x ?? 0,
127
181
  y: elkPort.y ?? 0,
128
182
  width: elkPort.width ?? 0,
129
- height: elkPort.height ?? 0
183
+ height: elkPort.height ?? 0,
184
+ ...metadata$1?.style !== void 0 && { style: metadata$1.style }
130
185
  };
131
186
  });
132
187
  nodes.push(node);
133
188
  flattenElkNodes(child, child.id, nodes, edges, edgeIdx, portOwner);
134
189
  }
135
190
  if (elkNode.edges) for (const elkEdge of elkNode.edges) for (const source of elkEdge.sources) for (const target of elkEdge.targets) {
191
+ const metadata = readMetadata(elkEdge)?.edge;
136
192
  const sourceNodeId = portOwner.get(source);
137
193
  const targetNodeId = portOwner.get(target);
138
194
  const edge = {
139
195
  type: "edge",
140
196
  id: elkEdge.id ?? `e${edgeIdx.value++}`,
141
- sourceId: sourceNodeId ?? source,
142
- targetId: targetNodeId ?? target,
143
- label: elkEdge.labels?.[0]?.text ?? "",
144
- data: void 0,
145
- x: 0,
146
- y: 0,
147
- width: 0,
148
- height: 0
197
+ sourceId: metadata?.sourceId ?? sourceNodeId ?? source,
198
+ targetId: metadata?.targetId ?? targetNodeId ?? target,
199
+ label: metadata && "label" in metadata ? metadata.label : elkEdge.labels?.[0]?.text ?? "",
200
+ data: metadata && "data" in metadata ? metadata.data : void 0,
201
+ x: metadata?.x ?? 0,
202
+ y: metadata?.y ?? 0,
203
+ width: metadata?.width ?? 0,
204
+ height: metadata?.height ?? 0,
205
+ ...metadata?.weight !== void 0 && { weight: metadata.weight },
206
+ ...metadata?.color !== void 0 && { color: metadata.color },
207
+ ...metadata?.style !== void 0 && { style: metadata.style }
149
208
  };
150
- if (sourceNodeId) edge.sourcePort = source;
151
- if (targetNodeId) edge.targetPort = target;
209
+ if (metadata && "sourcePort" in metadata) edge.sourcePort = metadata.sourcePort;
210
+ else if (sourceNodeId) edge.sourcePort = source;
211
+ if (metadata && "targetPort" in metadata) edge.targetPort = metadata.targetPort;
212
+ else if (targetNodeId) edge.targetPort = target;
152
213
  edges.push(edge);
153
214
  }
154
215
  }
@@ -179,15 +240,17 @@ function fromELK(elkRoot) {
179
240
  const seenEdges = /* @__PURE__ */ new Map();
180
241
  for (const edge of edges) if (!seenEdges.has(edge.id)) seenEdges.set(edge.id, edge);
181
242
  const elkDir = elkRoot.layoutOptions?.["elk.direction"];
182
- const direction = (elkDir ? ELK_TO_DIRECTION[elkDir] : void 0) ?? "down";
243
+ const graphMetadata = readMetadata(elkRoot)?.graph;
244
+ const direction = graphMetadata?.direction ?? (elkDir ? ELK_TO_DIRECTION[elkDir] : void 0) ?? "down";
183
245
  return {
184
- id: elkRoot.id,
185
- type: "directed",
186
- initialNodeId: null,
246
+ id: graphMetadata?.id ?? elkRoot.id,
247
+ type: graphMetadata?.type === "undirected" ? "undirected" : "directed",
248
+ initialNodeId: graphMetadata && "initialNodeId" in graphMetadata ? graphMetadata.initialNodeId : null,
187
249
  nodes,
188
250
  edges: [...seenEdges.values()],
189
- data: void 0,
190
- direction
251
+ data: graphMetadata && "data" in graphMetadata ? graphMetadata.data : void 0,
252
+ direction,
253
+ ...graphMetadata?.style !== void 0 && { style: graphMetadata.style }
191
254
  };
192
255
  }
193
256
  /**
@@ -23,13 +23,75 @@ function toGEXF(graph) {
23
23
  "@_id": "a_shape",
24
24
  "@_title": "shape",
25
25
  "@_type": "string"
26
+ },
27
+ {
28
+ "@_id": "a_width",
29
+ "@_title": "width",
30
+ "@_type": "double"
31
+ },
32
+ {
33
+ "@_id": "a_height",
34
+ "@_title": "height",
35
+ "@_type": "double"
36
+ },
37
+ {
38
+ "@_id": "a_style",
39
+ "@_title": "style",
40
+ "@_type": "string"
41
+ },
42
+ {
43
+ "@_id": "a_ports",
44
+ "@_title": "ports",
45
+ "@_type": "string"
46
+ }
47
+ ];
48
+ const edgeAttrs = [
49
+ {
50
+ "@_id": "a_edgeData",
51
+ "@_title": "data",
52
+ "@_type": "string"
53
+ },
54
+ {
55
+ "@_id": "a_edgeWeight",
56
+ "@_title": "weight",
57
+ "@_type": "double"
58
+ },
59
+ {
60
+ "@_id": "a_edgeX",
61
+ "@_title": "x",
62
+ "@_type": "double"
63
+ },
64
+ {
65
+ "@_id": "a_edgeY",
66
+ "@_title": "y",
67
+ "@_type": "double"
68
+ },
69
+ {
70
+ "@_id": "a_edgeWidth",
71
+ "@_title": "width",
72
+ "@_type": "double"
73
+ },
74
+ {
75
+ "@_id": "a_edgeHeight",
76
+ "@_title": "height",
77
+ "@_type": "double"
78
+ },
79
+ {
80
+ "@_id": "a_edgeStyle",
81
+ "@_title": "style",
82
+ "@_type": "string"
83
+ },
84
+ {
85
+ "@_id": "a_sourcePort",
86
+ "@_title": "sourcePort",
87
+ "@_type": "string"
88
+ },
89
+ {
90
+ "@_id": "a_targetPort",
91
+ "@_title": "targetPort",
92
+ "@_type": "string"
26
93
  }
27
94
  ];
28
- const edgeAttrs = [{
29
- "@_id": "a_edgeData",
30
- "@_title": "data",
31
- "@_type": "string"
32
- }];
33
95
  const nodes = graph.nodes.map((n) => {
34
96
  const attvalues = [];
35
97
  if (n.parentId) attvalues.push({
@@ -48,6 +110,22 @@ function toGEXF(graph) {
48
110
  "@_for": "a_shape",
49
111
  "@_value": n.shape
50
112
  });
113
+ if (n.width !== void 0) attvalues.push({
114
+ "@_for": "a_width",
115
+ "@_value": n.width
116
+ });
117
+ if (n.height !== void 0) attvalues.push({
118
+ "@_for": "a_height",
119
+ "@_value": n.height
120
+ });
121
+ if (n.style !== void 0) attvalues.push({
122
+ "@_for": "a_style",
123
+ "@_value": JSON.stringify(n.style)
124
+ });
125
+ if (n.ports !== void 0) attvalues.push({
126
+ "@_for": "a_ports",
127
+ "@_value": JSON.stringify(n.ports)
128
+ });
51
129
  const node = {
52
130
  "@_id": n.id,
53
131
  "@_label": n.label || n.id
@@ -80,6 +158,40 @@ function toGEXF(graph) {
80
158
  "@_for": "a_edgeData",
81
159
  "@_value": JSON.stringify(e.data)
82
160
  }] };
161
+ const edgeAttvalues = edge.attvalues?.attvalue ?? [];
162
+ if (e.weight !== void 0) edgeAttvalues.push({
163
+ "@_for": "a_edgeWeight",
164
+ "@_value": e.weight
165
+ });
166
+ if (e.x !== void 0) edgeAttvalues.push({
167
+ "@_for": "a_edgeX",
168
+ "@_value": e.x
169
+ });
170
+ if (e.y !== void 0) edgeAttvalues.push({
171
+ "@_for": "a_edgeY",
172
+ "@_value": e.y
173
+ });
174
+ if (e.width !== void 0) edgeAttvalues.push({
175
+ "@_for": "a_edgeWidth",
176
+ "@_value": e.width
177
+ });
178
+ if (e.height !== void 0) edgeAttvalues.push({
179
+ "@_for": "a_edgeHeight",
180
+ "@_value": e.height
181
+ });
182
+ if (e.style !== void 0) edgeAttvalues.push({
183
+ "@_for": "a_edgeStyle",
184
+ "@_value": JSON.stringify(e.style)
185
+ });
186
+ if (e.sourcePort !== void 0) edgeAttvalues.push({
187
+ "@_for": "a_sourcePort",
188
+ "@_value": e.sourcePort
189
+ });
190
+ if (e.targetPort !== void 0) edgeAttvalues.push({
191
+ "@_for": "a_targetPort",
192
+ "@_value": e.targetPort
193
+ });
194
+ if (edgeAttvalues.length > 0) edge.attvalues = { attvalue: edgeAttvalues };
83
195
  if (e.color) {
84
196
  const hex$1 = e.color.replace("#", "");
85
197
  if (hex$1.length === 6) edge["viz:color"] = {
@@ -90,15 +202,6 @@ function toGEXF(graph) {
90
202
  }
91
203
  return edge;
92
204
  });
93
- const graphData = [];
94
- if (graph.initialNodeId) graphData.push({
95
- "@_for": "a_initialNodeId",
96
- "@_value": graph.initialNodeId
97
- });
98
- if (graph.data !== void 0) graphData.push({
99
- "@_for": "a_data",
100
- "@_value": JSON.stringify(graph.data)
101
- });
102
205
  const obj = {
103
206
  "?xml": {
104
207
  "@_version": "1.0",
@@ -111,6 +214,10 @@ function toGEXF(graph) {
111
214
  graph: {
112
215
  "@_defaultedgetype": graph.type === "directed" ? "directed" : "undirected",
113
216
  ...graph.id && { "@_id": graph.id },
217
+ ...graph.initialNodeId && { "@_initialNodeId": graph.initialNodeId },
218
+ ...graph.direction && { "@_direction": graph.direction },
219
+ ...graph.data !== void 0 && { "@_data": JSON.stringify(graph.data) },
220
+ ...graph.style !== void 0 && { "@_style": JSON.stringify(graph.style) },
114
221
  attributes: [{
115
222
  "@_class": "node",
116
223
  attribute: nodeAttrs
@@ -168,6 +275,8 @@ function fromGEXF(xml) {
168
275
  data: attvals["data"] !== void 0 ? tryParseJSON(attvals["data"]) : void 0
169
276
  };
170
277
  if (attvals["shape"]) node.shape = attvals["shape"];
278
+ if (attvals["style"] !== void 0) node.style = tryParseJSON(attvals["style"]);
279
+ if (attvals["ports"] !== void 0) node.ports = tryParseJSON(attvals["ports"]);
171
280
  const pos = n["viz:position"];
172
281
  if (pos) {
173
282
  node.x = Number(pos["@_x"] ?? 0);
@@ -178,6 +287,8 @@ function fromGEXF(xml) {
178
287
  node.width = Number(size["@_value"] ?? 0);
179
288
  node.height = Number(size["@_value"] ?? 0);
180
289
  }
290
+ if (attvals["width"] !== void 0) node.width = Number(attvals["width"]);
291
+ if (attvals["height"] !== void 0) node.height = Number(attvals["height"]);
181
292
  const color = n["viz:color"];
182
293
  if (color) {
183
294
  const r = Number(color["@_r"] ?? 0);
@@ -199,7 +310,15 @@ function fromGEXF(xml) {
199
310
  sourceId: String(e["@_source"]),
200
311
  targetId: String(e["@_target"]),
201
312
  label: e["@_label"] ?? "",
202
- data: attvals["data"] !== void 0 ? tryParseJSON(attvals["data"]) : void 0
313
+ 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"]) },
319
+ ...attvals["style"] !== void 0 && { style: tryParseJSON(attvals["style"]) },
320
+ ...attvals["sourcePort"] !== void 0 && { sourcePort: attvals["sourcePort"] },
321
+ ...attvals["targetPort"] !== void 0 && { targetPort: attvals["targetPort"] }
203
322
  };
204
323
  const color = e["viz:color"];
205
324
  if (color) {
@@ -213,10 +332,12 @@ function fromGEXF(xml) {
213
332
  return {
214
333
  id: String(graphEl["@_id"] ?? ""),
215
334
  type: graphType,
216
- initialNodeId: null,
335
+ initialNodeId: graphEl["@_initialNodeId"] ?? null,
217
336
  nodes,
218
337
  edges,
219
- data: void 0
338
+ data: graphEl["@_data"] !== void 0 ? tryParseJSON(String(graphEl["@_data"])) : void 0,
339
+ ...graphEl["@_direction"] && { direction: graphEl["@_direction"] },
340
+ ...graphEl["@_style"] !== void 0 && { style: tryParseJSON(String(graphEl["@_style"])) }
220
341
  };
221
342
  }
222
343
  function asArray(val) {
@@ -28,6 +28,10 @@ function toGML(graph) {
28
28
  lines.push("graph [");
29
29
  lines.push(` directed ${graph.type === "directed" ? 1 : 0}`);
30
30
  if (graph.id) lines.push(` id ${gmlString(graph.id)}`);
31
+ if (graph.initialNodeId) lines.push(` initialNodeId ${gmlString(graph.initialNodeId)}`);
32
+ if (graph.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(graph.data))}`);
33
+ if (graph.direction) lines.push(` direction ${gmlString(graph.direction)}`);
34
+ if (graph.style !== void 0) lines.push(` style ${gmlString(JSON.stringify(graph.style))}`);
31
35
  const childrenMap = /* @__PURE__ */ new Map();
32
36
  for (const node of graph.nodes) {
33
37
  const pid = node.parentId ?? null;
@@ -40,8 +44,10 @@ function toGML(graph) {
40
44
  if (node.label) lines.push(`${indent} label ${gmlString(node.label)}`);
41
45
  if (node.initialNodeId) lines.push(`${indent} initialNodeId ${gmlString(node.initialNodeId)}`);
42
46
  if (node.data !== void 0) lines.push(`${indent} data ${gmlString(JSON.stringify(node.data))}`);
47
+ if (node.ports !== void 0) lines.push(`${indent} ports ${gmlString(JSON.stringify(node.ports))}`);
43
48
  if (node.shape) lines.push(`${indent} shape ${gmlString(node.shape)}`);
44
49
  if (node.color) lines.push(`${indent} color ${gmlString(node.color)}`);
50
+ if (node.style !== void 0) lines.push(`${indent} style ${gmlString(JSON.stringify(node.style))}`);
45
51
  if (node.x !== void 0 || node.y !== void 0 || node.width !== void 0 || node.height !== void 0) {
46
52
  lines.push(`${indent} graphics [`);
47
53
  if (node.x !== void 0) lines.push(`${indent} x ${node.x}`);
@@ -63,7 +69,19 @@ function toGML(graph) {
63
69
  lines.push(` target ${gmlString(edge.targetId)}`);
64
70
  if (edge.label) lines.push(` label ${gmlString(edge.label)}`);
65
71
  if (edge.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(edge.data))}`);
72
+ if (edge.weight !== void 0) lines.push(` weight ${edge.weight}`);
73
+ if (edge.sourcePort !== void 0) lines.push(` sourcePort ${gmlString(edge.sourcePort)}`);
74
+ if (edge.targetPort !== void 0) lines.push(` targetPort ${gmlString(edge.targetPort)}`);
66
75
  if (edge.color) lines.push(` color ${gmlString(edge.color)}`);
76
+ if (edge.style !== void 0) lines.push(` style ${gmlString(JSON.stringify(edge.style))}`);
77
+ if (edge.x !== void 0 || edge.y !== void 0 || edge.width !== void 0 || edge.height !== void 0) {
78
+ lines.push(" graphics [");
79
+ if (edge.x !== void 0) lines.push(` x ${edge.x}`);
80
+ if (edge.y !== void 0) lines.push(` y ${edge.y}`);
81
+ if (edge.width !== void 0) lines.push(` w ${edge.width}`);
82
+ if (edge.height !== void 0) lines.push(` h ${edge.height}`);
83
+ lines.push(" ]");
84
+ }
67
85
  lines.push(" ]");
68
86
  }
69
87
  lines.push("]");
@@ -112,8 +130,10 @@ function fromGML(gml) {
112
130
  initialNodeId: n["initialNodeId"] ?? null,
113
131
  label: n["label"] ?? "",
114
132
  data: n["data"] !== void 0 ? tryParseJSON(n["data"]) : void 0,
133
+ ...n["ports"] !== void 0 && { ports: tryParseJSON(n["ports"]) },
115
134
  ...n["shape"] && { shape: n["shape"] },
116
135
  ...n["color"] && { color: n["color"] },
136
+ ...n["style"] !== void 0 && { style: tryParseJSON(n["style"]) },
117
137
  ...gfx?.x !== void 0 && { x: gfx.x },
118
138
  ...gfx?.y !== void 0 && { y: gfx.y },
119
139
  ...gfx?.w !== void 0 && { width: gfx.w },
@@ -124,22 +144,35 @@ function fromGML(gml) {
124
144
  }
125
145
  parseNodes(graphBlock, null);
126
146
  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
- });
147
+ for (const e of edgeEntries) {
148
+ const gfx = e["graphics"];
149
+ edges.push({
150
+ type: "edge",
151
+ id: String(e["id"] ?? `e${edges.length}`),
152
+ sourceId: String(e["source"] ?? ""),
153
+ targetId: String(e["target"] ?? ""),
154
+ label: e["label"] ?? "",
155
+ data: e["data"] !== void 0 ? tryParseJSON(e["data"]) : void 0,
156
+ ...e["weight"] !== void 0 && { weight: Number(e["weight"]) },
157
+ ...e["sourcePort"] !== void 0 && { sourcePort: String(e["sourcePort"]) },
158
+ ...e["targetPort"] !== void 0 && { targetPort: String(e["targetPort"]) },
159
+ ...e["color"] && { color: e["color"] },
160
+ ...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
+ });
166
+ }
136
167
  return {
137
168
  id: graphId,
138
169
  type: directed ? "directed" : "undirected",
139
- initialNodeId: null,
170
+ initialNodeId: graphBlock["initialNodeId"] ?? null,
140
171
  nodes,
141
172
  edges,
142
- data: void 0
173
+ data: graphBlock["data"] !== void 0 ? tryParseJSON(graphBlock["data"]) : void 0,
174
+ ...graphBlock["direction"] && { direction: String(graphBlock["direction"]) },
175
+ ...graphBlock["style"] !== void 0 && { style: tryParseJSON(graphBlock["style"]) }
143
176
  };
144
177
  }
145
178
  function tokenize(input) {