@graphty/graph-io 0.3.8 → 0.3.10

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 (125) hide show
  1. package/README.md +29 -5
  2. package/dist/chunks/{escape-BjmIaFFo.js → escape-D1f9-cwf.js} +4 -4
  3. package/dist/chunks/{escape-BjmIaFFo.js.map → escape-D1f9-cwf.js.map} +1 -1
  4. package/dist/chunks/{importer-BddBW3sf.js → importer-9PxhqH4v.js} +336 -92
  5. package/dist/chunks/importer-9PxhqH4v.js.map +1 -0
  6. package/dist/chunks/{importer-DfI8POXb.js → importer-B8lsjFWx.js} +6 -5
  7. package/dist/chunks/importer-B8lsjFWx.js.map +1 -0
  8. package/dist/chunks/{importer-BtHFICnf.js → importer-C3HcYWAn.js} +5 -3
  9. package/dist/chunks/importer-C3HcYWAn.js.map +1 -0
  10. package/dist/chunks/{importer-BOxwmef_.js → importer-C7mnGdr_.js} +125 -8
  11. package/dist/chunks/importer-C7mnGdr_.js.map +1 -0
  12. package/dist/chunks/{records-BKSMowhR.js → records-IHsCfv7s.js} +20 -7
  13. package/dist/chunks/records-IHsCfv7s.js.map +1 -0
  14. package/dist/chunks/{writer-BdMak4_J.js → writer-BtWpUaiH.js} +4 -4
  15. package/dist/chunks/{writer-BdMak4_J.js.map → writer-BtWpUaiH.js.map} +1 -1
  16. package/dist/csv.js +239 -18
  17. package/dist/csv.js.map +1 -1
  18. package/dist/dot.js +1 -1
  19. package/dist/gexf.js +72 -33
  20. package/dist/gexf.js.map +1 -1
  21. package/dist/gml.js +44 -23
  22. package/dist/gml.js.map +1 -1
  23. package/dist/graph-io.js +11 -11
  24. package/dist/graphml.js +1 -1
  25. package/dist/json.js +1 -1
  26. package/dist/neo4j.js +5 -4
  27. package/dist/neo4j.js.map +1 -1
  28. package/dist/pajek.js +1 -1
  29. package/dist/src/common/export.js +1 -1
  30. package/dist/src/common/export.js.map +1 -1
  31. package/dist/src/formats/csv/exporter.d.ts +15 -4
  32. package/dist/src/formats/csv/exporter.d.ts.map +1 -1
  33. package/dist/src/formats/csv/exporter.js +125 -6
  34. package/dist/src/formats/csv/exporter.js.map +1 -1
  35. package/dist/src/formats/csv/importer.d.ts +36 -4
  36. package/dist/src/formats/csv/importer.d.ts.map +1 -1
  37. package/dist/src/formats/csv/importer.js +169 -8
  38. package/dist/src/formats/csv/importer.js.map +1 -1
  39. package/dist/src/formats/csv/records.d.ts +14 -1
  40. package/dist/src/formats/csv/records.d.ts.map +1 -1
  41. package/dist/src/formats/csv/records.js +28 -7
  42. package/dist/src/formats/csv/records.js.map +1 -1
  43. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  44. package/dist/src/formats/dot/exporter.js +2 -0
  45. package/dist/src/formats/dot/exporter.js.map +1 -1
  46. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  47. package/dist/src/formats/gexf/exporter.js +36 -10
  48. package/dist/src/formats/gexf/exporter.js.map +1 -1
  49. package/dist/src/formats/gexf/importer.d.ts +9 -4
  50. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  51. package/dist/src/formats/gexf/importer.js +44 -21
  52. package/dist/src/formats/gexf/importer.js.map +1 -1
  53. package/dist/src/formats/gexf/index.d.ts +1 -1
  54. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  55. package/dist/src/formats/gexf/index.js +1 -1
  56. package/dist/src/formats/gexf/index.js.map +1 -1
  57. package/dist/src/formats/gexf/schema.d.ts +2 -0
  58. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  59. package/dist/src/formats/gexf/schema.js +18 -0
  60. package/dist/src/formats/gexf/schema.js.map +1 -1
  61. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  62. package/dist/src/formats/gml/exporter.js +1 -0
  63. package/dist/src/formats/gml/exporter.js.map +1 -1
  64. package/dist/src/formats/gml/importer.d.ts +7 -4
  65. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  66. package/dist/src/formats/gml/importer.js +32 -19
  67. package/dist/src/formats/gml/importer.js.map +1 -1
  68. package/dist/src/formats/gml/index.d.ts +3 -1
  69. package/dist/src/formats/gml/index.d.ts.map +1 -1
  70. package/dist/src/formats/gml/index.js +4 -2
  71. package/dist/src/formats/gml/index.js.map +1 -1
  72. package/dist/src/formats/graphml/constants.d.ts +2 -0
  73. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  74. package/dist/src/formats/graphml/constants.js +2 -0
  75. package/dist/src/formats/graphml/constants.js.map +1 -1
  76. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  77. package/dist/src/formats/graphml/exporter.js +14 -2
  78. package/dist/src/formats/graphml/exporter.js.map +1 -1
  79. package/dist/src/formats/graphml/importer.d.ts +2 -1
  80. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  81. package/dist/src/formats/graphml/importer.js +91 -27
  82. package/dist/src/formats/graphml/importer.js.map +1 -1
  83. package/dist/src/formats/graphml/yfiles.d.ts +62 -0
  84. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -0
  85. package/dist/src/formats/graphml/yfiles.js +269 -0
  86. package/dist/src/formats/graphml/yfiles.js.map +1 -0
  87. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  88. package/dist/src/formats/json/exporter.js +9 -1
  89. package/dist/src/formats/json/exporter.js.map +1 -1
  90. package/dist/src/formats/json/importer.d.ts +14 -0
  91. package/dist/src/formats/json/importer.d.ts.map +1 -1
  92. package/dist/src/formats/json/importer.js +165 -4
  93. package/dist/src/formats/json/importer.js.map +1 -1
  94. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  95. package/dist/src/formats/neo4j/exporter.js +1 -0
  96. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  97. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  98. package/dist/src/formats/pajek/exporter.js +3 -2
  99. package/dist/src/formats/pajek/exporter.js.map +1 -1
  100. package/package.json +2 -2
  101. package/src/common/export.ts +1 -1
  102. package/src/formats/csv/exporter.ts +140 -10
  103. package/src/formats/csv/importer.ts +207 -18
  104. package/src/formats/csv/records.ts +41 -6
  105. package/src/formats/dot/exporter.ts +2 -0
  106. package/src/formats/gexf/exporter.ts +40 -11
  107. package/src/formats/gexf/importer.ts +47 -25
  108. package/src/formats/gexf/index.ts +1 -1
  109. package/src/formats/gexf/schema.ts +18 -0
  110. package/src/formats/gml/exporter.ts +1 -0
  111. package/src/formats/gml/importer.ts +47 -24
  112. package/src/formats/gml/index.ts +4 -1
  113. package/src/formats/graphml/constants.ts +2 -0
  114. package/src/formats/graphml/exporter.ts +21 -2
  115. package/src/formats/graphml/importer.ts +99 -28
  116. package/src/formats/graphml/yfiles.ts +292 -0
  117. package/src/formats/json/exporter.ts +9 -1
  118. package/src/formats/json/importer.ts +202 -4
  119. package/src/formats/neo4j/exporter.ts +1 -0
  120. package/src/formats/pajek/exporter.ts +3 -1
  121. package/dist/chunks/importer-BOxwmef_.js.map +0 -1
  122. package/dist/chunks/importer-BddBW3sf.js.map +0 -1
  123. package/dist/chunks/importer-BtHFICnf.js.map +0 -1
  124. package/dist/chunks/importer-DfI8POXb.js.map +0 -1
  125. package/dist/chunks/records-BKSMowhR.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { GraphFormatError, INVALID_INDEX, MAX_COUNT } from "@graphty/graph-format";
2
2
  import { c as childrenCsr } from "./children-CL3Cy0ez.js";
3
- import { z as MIXED_DIRECTION_CODE, g as DIRECTION_FORCED_CODE, h as DIRECTION_REFUSED_CODE, Y as SINK_OPTION_CODE, T as PRECISION_CODE, B as BAD_DEFAULT_CODE, a3 as UNKNOWN_ATTR_TYPE_CODE, X as ROLE_TAKEN_CODE, C as COLUMN_RENAMED_CODE, P as OPTION_IGNORED_CODE, o as ID_MERGED_CODE, _ as STRAY_TEXT_CODE, a4 as UNKNOWN_ELEMENT_CODE, H as HYPEREDGE_CODE, d as COUNT_HINT_CODE, A as MULTIPLE_GRAPHS_CODE, i as DUPLICATE_EDGE_ID_CODE, k as DUPLICATE_NODE_CODE, M as MISSING_ENDPOINT_CODE, y as MISSING_ID_CODE, j as DUPLICATE_KEY_CODE, K as NO_GRAPH_CODE, a5 as UNKNOWN_ENCODING_CODE, n as ENCODING_FALLBACK_CODE, s as INVALID_ENCODING_CODE, u as INVALID_UTF8_CODE, aa as XML_SYNTAX_CODE, q as ID_TEXT_TYPE_CODE, W as ROLE_DROPPED_CODE, R as PARENTS_DROPPED_CODE, F as MUTUAL_AS_UNDIRECTED_CODE, aE as resolveExportOptions, ax as joinText, ak as encodeChunks, ac as capabilities, ad as checkCapabilities, L as LOSS, az as pairFolding, aG as sanitizeIds, al as explicitWeights, av as isNmtoken, at as isCanonicalIntegerText, ay as mangleNmtoken, aq as formatInteger, ao as formatF64, an as formatF32, aF as resolveImportOptions, I as ImportReportBuilder, aC as reportSinkOptions, aD as reportUnusedOptions, t as textChunks, a as throwIfAborted, v as IdCoercer, l as DirectionResolver, aw as isWeightField, aO as declareAttribute, aL as uniqueColumnName, ai as declareResolved, b0 as takenIn, aA as parseWeightText, aQ as parseDeclaredValue, aR as losesPrecision } from "./writer-BdMak4_J.js";
4
- import { f as escapeXmlText, e as isWhitespace, m as isXmlName, g as escapeXmlAttribute, x as xmlIllegalTextNotes, t as tokenizeXml, d as xmlDeclaredEncoding, X as XmlSyntaxError, l as localName } from "./escape-BjmIaFFo.js";
3
+ import { z as MIXED_DIRECTION_CODE, g as DIRECTION_FORCED_CODE, h as DIRECTION_REFUSED_CODE, Y as SINK_OPTION_CODE, T as PRECISION_CODE, B as BAD_DEFAULT_CODE, a3 as UNKNOWN_ATTR_TYPE_CODE, X as ROLE_TAKEN_CODE, C as COLUMN_RENAMED_CODE, P as OPTION_IGNORED_CODE, o as ID_MERGED_CODE, _ as STRAY_TEXT_CODE, a4 as UNKNOWN_ELEMENT_CODE, H as HYPEREDGE_CODE, d as COUNT_HINT_CODE, A as MULTIPLE_GRAPHS_CODE, i as DUPLICATE_EDGE_ID_CODE, k as DUPLICATE_NODE_CODE, M as MISSING_ENDPOINT_CODE, y as MISSING_ID_CODE, j as DUPLICATE_KEY_CODE, K as NO_GRAPH_CODE, a5 as UNKNOWN_ENCODING_CODE, n as ENCODING_FALLBACK_CODE, s as INVALID_ENCODING_CODE, u as INVALID_UTF8_CODE, aa as XML_SYNTAX_CODE, q as ID_TEXT_TYPE_CODE, W as ROLE_DROPPED_CODE, R as PARENTS_DROPPED_CODE, F as MUTUAL_AS_UNDIRECTED_CODE, aE as resolveExportOptions, ax as joinText, ak as encodeChunks, ac as capabilities, ad as checkCapabilities, L as LOSS, az as pairFolding, aG as sanitizeIds, al as explicitWeights, av as isNmtoken, at as isCanonicalIntegerText, ay as mangleNmtoken, aq as formatInteger, ao as formatF64, an as formatF32, aF as resolveImportOptions, I as ImportReportBuilder, aC as reportSinkOptions, aD as reportUnusedOptions, t as textChunks, a as throwIfAborted, v as IdCoercer, l as DirectionResolver, aw as isWeightField, aO as declareAttribute, aL as uniqueColumnName, ai as declareResolved, b1 as takenIn, aA as parseWeightText, aQ as parseDeclaredValue, aR as losesPrecision } from "./writer-BtWpUaiH.js";
4
+ import { f as escapeXmlText, e as isWhitespace, n as isXmlName, g as escapeXmlAttribute, l as localName, x as xmlIllegalTextNotes, t as tokenizeXml, d as xmlDeclaredEncoding, X as XmlSyntaxError } from "./escape-D1f9-cwf.js";
5
5
  const GRAPHML_NAMESPACE = "http://graphml.graphdrawing.org/xmlns";
6
6
  const YFILES_NAMESPACE = "http://www.yworks.com/xml/graphml";
7
7
  const XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
@@ -139,6 +139,8 @@ const GRAPHML_LOSS = Object.freeze({
139
139
  ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
140
140
  /** A numeric edge id column reads back as string. */
141
141
  EDGE_ID_TEXT: "W_GRAPHML_EDGE_ID_TEXT",
142
+ /** A `yfiles.*` graphics column that no longer matches its yFiles tree: only the tree is written. */
143
+ YFILES_GRAPHICS_STALE: "W_GRAPHML_YFILES_GRAPHICS_STALE",
142
144
  /** A yfiles json value that is not a serialisable tree: export() will throw E_COLUMN_TYPE. */
143
145
  YFILES_TREE: "E_GRAPHML_YFILES_TREE"
144
146
  });
@@ -175,9 +177,9 @@ class XmlTreeBuilder {
175
177
  * Character data inside the current element.
176
178
  * @param text - the text
177
179
  */
178
- text(text) {
180
+ text(text2) {
179
181
  const frame = this.frames.length > 0 ? this.frames[this.frames.length - 1] : this.root;
180
- frame.text += text;
182
+ frame.text += text2;
181
183
  }
182
184
  /** The current element ends; its value is attached to its parent. */
183
185
  end() {
@@ -250,9 +252,9 @@ function treeProblem(value) {
250
252
  if (typeof value !== "object") {
251
253
  return `a ${typeof value} cannot be written as XML`;
252
254
  }
253
- for (const [key, child] of Object.entries(value)) {
255
+ for (const [key, child2] of Object.entries(value)) {
254
256
  if (key === TEXT_KEY) {
255
- if (child !== null && typeof child === "object") {
257
+ if (child2 !== null && typeof child2 === "object") {
256
258
  return "#text must be a scalar";
257
259
  }
258
260
  continue;
@@ -262,7 +264,7 @@ function treeProblem(value) {
262
264
  if (!isXmlName(name)) {
263
265
  return `"${name}" is not an XML attribute name`;
264
266
  }
265
- if (child !== null && typeof child === "object") {
267
+ if (child2 !== null && typeof child2 === "object") {
266
268
  return `attribute ${name} must be a scalar`;
267
269
  }
268
270
  continue;
@@ -270,7 +272,7 @@ function treeProblem(value) {
270
272
  if (!isXmlName(key)) {
271
273
  return `"${key}" is not an XML element name`;
272
274
  }
273
- const problem = treeProblem(child);
275
+ const problem = treeProblem(child2);
274
276
  if (problem !== null) {
275
277
  return problem;
276
278
  }
@@ -289,16 +291,16 @@ function writeXmlTree(value, indent, out) {
289
291
  return;
290
292
  }
291
293
  const object = value;
292
- let text = null;
293
- for (const [key, child] of Object.entries(object)) {
294
+ let text2 = null;
295
+ for (const [key, child2] of Object.entries(object)) {
294
296
  if (key === TEXT_KEY) {
295
- text = scalarText(child);
297
+ text2 = scalarText(child2);
296
298
  } else if (!key.startsWith(ATTRIBUTE_PREFIX)) {
297
- writeElements(key, child, indent, out);
299
+ writeElements(key, child2, indent, out);
298
300
  }
299
301
  }
300
- if (text !== null) {
301
- out.push(escapeXmlText(text));
302
+ if (text2 !== null) {
303
+ out.push(escapeXmlText(text2));
302
304
  }
303
305
  }
304
306
  function writeElements(name, value, indent, out) {
@@ -309,43 +311,43 @@ function writeElements(name, value, indent, out) {
309
311
  return;
310
312
  }
311
313
  if (value === null || typeof value !== "object") {
312
- const text2 = scalarText(value);
313
- out.push(text2.length === 0 ? `
314
+ const text22 = scalarText(value);
315
+ out.push(text22.length === 0 ? `
314
316
  ${indent}<${name}/>` : `
315
- ${indent}<${name}>${escapeXmlText(text2)}</${name}>`);
317
+ ${indent}<${name}>${escapeXmlText(text22)}</${name}>`);
316
318
  return;
317
319
  }
318
320
  const object = value;
319
321
  let attrs = "";
320
- let text = null;
322
+ let text2 = null;
321
323
  let hasChildren = false;
322
- for (const [key, child] of Object.entries(object)) {
324
+ for (const [key, child2] of Object.entries(object)) {
323
325
  if (key === TEXT_KEY) {
324
- text = scalarText(child);
326
+ text2 = scalarText(child2);
325
327
  } else if (key.startsWith(ATTRIBUTE_PREFIX)) {
326
- attrs += ` ${key.slice(ATTRIBUTE_PREFIX.length)}="${escapeXmlAttribute(scalarText(child))}"`;
328
+ attrs += ` ${key.slice(ATTRIBUTE_PREFIX.length)}="${escapeXmlAttribute(scalarText(child2))}"`;
327
329
  } else {
328
330
  hasChildren = true;
329
331
  }
330
332
  }
331
333
  if (!hasChildren) {
332
334
  out.push(
333
- text === null || text.length === 0 ? `
335
+ text2 === null || text2.length === 0 ? `
334
336
  ${indent}<${name}${attrs}/>` : `
335
- ${indent}<${name}${attrs}>${escapeXmlText(text)}</${name}>`
337
+ ${indent}<${name}${attrs}>${escapeXmlText(text2)}</${name}>`
336
338
  );
337
339
  return;
338
340
  }
339
341
  out.push(`
340
342
  ${indent}<${name}${attrs}>`);
341
343
  const inner = `${indent} `;
342
- for (const [key, child] of Object.entries(object)) {
344
+ for (const [key, child2] of Object.entries(object)) {
343
345
  if (key !== TEXT_KEY && !key.startsWith(ATTRIBUTE_PREFIX)) {
344
- writeElements(key, child, inner, out);
346
+ writeElements(key, child2, inner, out);
345
347
  }
346
348
  }
347
- if (text !== null) {
348
- out.push(escapeXmlText(text));
349
+ if (text2 !== null) {
350
+ out.push(escapeXmlText(text2));
349
351
  }
350
352
  out.push(`
351
353
  ${indent}</${name}>`);
@@ -362,6 +364,167 @@ function scalarText(value) {
362
364
  return "";
363
365
  }
364
366
  }
367
+ const YFILES_COLUMN_PREFIX = "yfiles.";
368
+ const NODE_FIELDS = {
369
+ position: { dtype: "f64", components: 3, role: "position", mutable: true },
370
+ width: { dtype: "f64" },
371
+ height: { dtype: "f64" },
372
+ color: { dtype: "string" },
373
+ borderColor: { dtype: "string" },
374
+ borderWidth: { dtype: "f64" },
375
+ label: { dtype: "string", role: "label" },
376
+ shape: { dtype: "string" }
377
+ };
378
+ const EDGE_FIELDS = {
379
+ color: { dtype: "string" },
380
+ width: { dtype: "f64" },
381
+ directed: { dtype: "bool" },
382
+ targetArrow: { dtype: "string" },
383
+ sourceArrow: { dtype: "string" }
384
+ };
385
+ function graphicsDecl(domain, field, keyId) {
386
+ const fields = domain === "node" ? NODE_FIELDS : EDGE_FIELDS;
387
+ return {
388
+ ...fields[field],
389
+ name: YFILES_COLUMN_PREFIX + field,
390
+ nullable: true,
391
+ origin: {
392
+ format: "graphml",
393
+ id: keyId,
394
+ title: null,
395
+ type: domain === "node" ? "ShapeNode" : "PolyLineEdge",
396
+ namespace: "yfiles"
397
+ }
398
+ };
399
+ }
400
+ function isGraphicsColumn(meta) {
401
+ return meta.dtype !== "json" && meta.origin?.namespace === "yfiles" && meta.name.startsWith(YFILES_COLUMN_PREFIX);
402
+ }
403
+ function staleGraphicsRows(column, table, domain) {
404
+ const field = column.meta.name.slice(YFILES_COLUMN_PREFIX.length);
405
+ const trees = [];
406
+ for (const c of table) {
407
+ if (c.dtype === "json" && c.meta.origin?.namespace === "yfiles") {
408
+ trees.push(c);
409
+ }
410
+ }
411
+ let stale = 0;
412
+ for (let r = 0; r < column.length; r++) {
413
+ const wants = /* @__PURE__ */ new Set();
414
+ for (const tree of trees) {
415
+ const want = graphicsValues(domain, tree.values[r]).find(([f]) => f === field);
416
+ if (want !== void 0) {
417
+ wants.add(JSON.stringify(want[1]));
418
+ }
419
+ }
420
+ if (wants.size === 0) {
421
+ wants.add(JSON.stringify(void 0));
422
+ }
423
+ const value = column.value(r);
424
+ const have = column.meta.components > 1 && value !== void 0 ? Array.from(value) : value;
425
+ if (!wants.has(JSON.stringify(have))) {
426
+ stale++;
427
+ }
428
+ }
429
+ return stale;
430
+ }
431
+ function graphicsValues(domain, tree) {
432
+ const out = [];
433
+ if (domain === "node") {
434
+ const shape = child(tree, "ShapeNode");
435
+ if (shape !== void 0) {
436
+ shapeNodeValues(shape, out);
437
+ }
438
+ } else {
439
+ const edge = child(tree, "PolyLineEdge");
440
+ if (edge !== void 0) {
441
+ polyLineEdgeValues(edge, out);
442
+ }
443
+ }
444
+ return out;
445
+ }
446
+ function shapeNodeValues(shape, out) {
447
+ const geometry = child(shape, "Geometry");
448
+ const x = number(attr(geometry, "x"));
449
+ const y = number(attr(geometry, "y"));
450
+ if (x !== void 0 && y !== void 0) {
451
+ out.push(["position", [x, y, 0]]);
452
+ }
453
+ push(out, "width", number(attr(geometry, "width")));
454
+ push(out, "height", number(attr(geometry, "height")));
455
+ push(out, "color", color(attr(child(shape, "Fill"), "color")));
456
+ const border = child(shape, "BorderStyle");
457
+ push(out, "borderColor", color(attr(border, "color")));
458
+ push(out, "borderWidth", number(attr(border, "width")));
459
+ const label = child(shape, "NodeLabel");
460
+ if (typeof label === "string" ? label.length > 0 : label !== void 0) {
461
+ out.push(["label", (typeof label === "string" ? label : text(label) ?? "").trim()]);
462
+ }
463
+ push(out, "shape", nonEmpty(attr(child(shape, "Shape"), "type")));
464
+ }
465
+ function polyLineEdgeValues(edge, out) {
466
+ const line = child(edge, "LineStyle");
467
+ push(out, "color", color(attr(line, "color")));
468
+ push(out, "width", number(attr(line, "width")));
469
+ const arrows = child(edge, "Arrows");
470
+ if (arrows !== void 0 && typeof arrows === "object") {
471
+ const target = attr(arrows, "target");
472
+ const source = attr(arrows, "source");
473
+ out.push(["directed", target !== void 0 && target !== "none"]);
474
+ push(out, "targetArrow", target === "none" ? void 0 : nonEmpty(target));
475
+ push(out, "sourceArrow", source === "none" ? void 0 : nonEmpty(source));
476
+ }
477
+ }
478
+ function push(out, field, value) {
479
+ if (value !== void 0) {
480
+ out.push([field, value]);
481
+ }
482
+ }
483
+ function child(node, local) {
484
+ if (typeof node !== "object" || node === null) {
485
+ return void 0;
486
+ }
487
+ for (const [key, value] of Object.entries(node)) {
488
+ if (!key.startsWith("@_") && key !== "#text" && localName(key) === local) {
489
+ return Array.isArray(value) ? value[0] : value;
490
+ }
491
+ }
492
+ return void 0;
493
+ }
494
+ function attr(node, name) {
495
+ if (typeof node !== "object" || node === null) {
496
+ return void 0;
497
+ }
498
+ const value = node[`@_${name}`];
499
+ return typeof value === "string" ? value : void 0;
500
+ }
501
+ function text(node) {
502
+ const value = typeof node === "object" && node !== null ? node["#text"] : void 0;
503
+ return typeof value === "string" ? value : void 0;
504
+ }
505
+ function nonEmpty(value) {
506
+ return value === void 0 || value.length === 0 ? void 0 : value;
507
+ }
508
+ function number(value) {
509
+ if (value === void 0 || value.length === 0) {
510
+ return void 0;
511
+ }
512
+ const n = Number.parseFloat(value);
513
+ return Number.isNaN(n) ? void 0 : n;
514
+ }
515
+ function color(value) {
516
+ if (value === void 0 || value.length === 0) {
517
+ return void 0;
518
+ }
519
+ if (/^#[0-9A-Fa-f]{6}$/.test(value)) {
520
+ return value.toUpperCase();
521
+ }
522
+ if (/^#[0-9A-Fa-f]{3}$/.test(value)) {
523
+ const [, r, g, b] = value;
524
+ return `#${r}${r}${g}${g}${b}${b}`.toUpperCase();
525
+ }
526
+ return value;
527
+ }
365
528
  const CAPABILITIES = capabilities({
366
529
  mixedDirection: true,
367
530
  multiEdges: true,
@@ -399,7 +562,8 @@ const DROPPED_ROLES = /* @__PURE__ */ new Set([
399
562
  "timestamp",
400
563
  "timestamps",
401
564
  "spells",
402
- "open"
565
+ "open",
566
+ "spellsOpen"
403
567
  ]);
404
568
  const SLOT_ROLES = /* @__PURE__ */ new Set(["label", "id", "sourcePort", "targetPort", "parent"]);
405
569
  const SLOT_ROLES_BY_DOMAIN = {
@@ -466,10 +630,13 @@ function graphmlMetaOf(snapshot) {
466
630
  return { graphId, edgedefault, namespaces };
467
631
  }
468
632
  function planExport(snapshot, options, format) {
633
+ const graphics = new Set(
634
+ [...snapshot.nodes, ...snapshot.edges].filter((c) => isGraphicsColumn(c.meta)).map((c) => c.meta.name)
635
+ );
469
636
  const notes = checkCapabilities(snapshot, CAPABILITIES, options, {
470
637
  roles: SLOT_ROLES,
471
638
  roleNames: ROLE_NAMES
472
- }).filter((n) => n.code !== LOSS.JSON);
639
+ }).filter((n) => n.code !== LOSS.JSON && (n.column === null || !graphics.has(n.column)));
473
640
  const note = (code, message, column = null, count = null) => {
474
641
  notes.push(Object.freeze({ code, message, column, count }));
475
642
  };
@@ -720,6 +887,18 @@ function planTable(table, domain, notes, note) {
720
887
  for (const column of columns) {
721
888
  const { meta } = column;
722
889
  const { role, name } = meta;
890
+ if (isGraphicsColumn(meta)) {
891
+ const stale = domain === "graph" ? 0 : staleGraphicsRows(column, columns, domain);
892
+ if (stale > 0) {
893
+ note(
894
+ GRAPHML_LOSS.YFILES_GRAPHICS_STALE,
895
+ `${stale} value(s) of ${domain} column "${name}" differ from the yFiles tree it was read from; only the tree is written, so they are lost`,
896
+ name,
897
+ stale
898
+ );
899
+ }
900
+ continue;
901
+ }
723
902
  if (role === "parents") {
724
903
  note(
725
904
  GRAPHML_LOSS.PARENTS_DROPPED,
@@ -1035,24 +1214,24 @@ ${indent}</data>`);
1035
1214
  }
1036
1215
  function keyElement(key, indent) {
1037
1216
  const domain = key.domains.length === 3 ? "all" : key.domains[0];
1038
- let text = `${indent}<key id="${escapeXmlAttribute(key.id)}" for="${domain}"`;
1217
+ let text2 = `${indent}<key id="${escapeXmlAttribute(key.id)}" for="${domain}"`;
1039
1218
  if (key.attrName !== null) {
1040
- text += ` attr.name="${escapeXmlAttribute(key.attrName)}"`;
1219
+ text2 += ` attr.name="${escapeXmlAttribute(key.attrName)}"`;
1041
1220
  }
1042
1221
  if (key.attrType !== null) {
1043
- text += ` attr.type="${escapeXmlAttribute(key.attrType)}"`;
1222
+ text2 += ` attr.type="${escapeXmlAttribute(key.attrType)}"`;
1044
1223
  }
1045
1224
  if (key.yfilesType !== null) {
1046
- text += ` yfiles.type="${escapeXmlAttribute(key.yfilesType)}"`;
1225
+ text2 += ` yfiles.type="${escapeXmlAttribute(key.yfilesType)}"`;
1047
1226
  }
1048
1227
  const { column } = key;
1049
1228
  const desc = column?.meta.extra.desc;
1050
1229
  const hasDefault = column !== null && column.meta.default !== void 0;
1051
1230
  if (column === null || typeof desc !== "string" && !hasDefault) {
1052
- return `${text}/>
1231
+ return `${text2}/>
1053
1232
  `;
1054
1233
  }
1055
- const parts = [`${text}>`];
1234
+ const parts = [`${text2}>`];
1056
1235
  const inner = `${indent} `;
1057
1236
  if (typeof desc === "string") {
1058
1237
  parts.push(`
@@ -1094,10 +1273,10 @@ function sanitizeEdgeIds(column, mode, edgeCount) {
1094
1273
  if (!column.isSet(e)) {
1095
1274
  continue;
1096
1275
  }
1097
- const text = edgeIdText(column, e);
1098
- if (isNmtoken(text)) {
1099
- out[e] = text;
1100
- used.add(text);
1276
+ const text2 = edgeIdText(column, e);
1277
+ if (isNmtoken(text2)) {
1278
+ out[e] = text2;
1279
+ used.add(text2);
1101
1280
  } else {
1102
1281
  bad.push(e);
1103
1282
  }
@@ -1342,7 +1521,14 @@ const graphmlExporter = Object.freeze({
1342
1521
  });
1343
1522
  const GRAPH_ATTRIBUTES = /* @__PURE__ */ new Set(["id", "edgedefault", "parse.nodes", "parse.edges"]);
1344
1523
  const NODE_ATTRIBUTES = /* @__PURE__ */ new Set(["id"]);
1345
- const EDGE_ATTRIBUTES = /* @__PURE__ */ new Set(["id", "source", "target", "directed", "sourceport", "targetport"]);
1524
+ const EDGE_ATTRIBUTES = /* @__PURE__ */ new Set([
1525
+ "id",
1526
+ "source",
1527
+ "target",
1528
+ "directed",
1529
+ "sourceport",
1530
+ "targetport"
1531
+ ]);
1346
1532
  const FORMAT_DEFAULTS = { ids: "canonical", defaultDirected: false, weightFrom: "weight" };
1347
1533
  const SNIFF_BYTES = 4096;
1348
1534
  const YFILES_MODES = /* @__PURE__ */ new Set(["json", "skip"]);
@@ -1432,6 +1618,7 @@ class GraphmlReader {
1432
1618
  this.namespaces = {};
1433
1619
  this.weightOrigin = null;
1434
1620
  this.yfilesLossNoted = false;
1621
+ this.graphicsColumns = /* @__PURE__ */ new Map();
1435
1622
  this.hyperedgeCount = 0;
1436
1623
  this.edgeIdHandle = INVALID_INDEX;
1437
1624
  this.sourcePortHandle = INVALID_INDEX;
@@ -1556,27 +1743,27 @@ class GraphmlReader {
1556
1743
  * @param text - the text
1557
1744
  * @param line - the line
1558
1745
  */
1559
- text(text, line) {
1746
+ text(text2, line) {
1560
1747
  const ctx = this.ctx.length === 0 ? null : this.ctx[this.ctx.length - 1];
1561
1748
  switch (ctx) {
1562
1749
  case 9:
1563
- this.content.tree?.text(text);
1750
+ this.content.tree?.text(text2);
1564
1751
  return;
1565
1752
  case 8:
1566
1753
  case 3:
1567
1754
  if (this.content.tree === null) {
1568
- this.content.text += text;
1755
+ this.content.text += text2;
1569
1756
  } else {
1570
- this.content.tree.text(text);
1757
+ this.content.tree.text(text2);
1571
1758
  }
1572
1759
  return;
1573
1760
  case 1:
1574
- this.descText += text;
1761
+ this.descText += text2;
1575
1762
  return;
1576
1763
  case 10:
1577
1764
  return;
1578
1765
  default:
1579
- if (!isWhitespace(text)) {
1766
+ if (!isWhitespace(text2)) {
1580
1767
  this.report.warnOnce(
1581
1768
  "validation-error",
1582
1769
  GRAPHML_ISSUE.STRAY_TEXT,
@@ -1892,11 +2079,11 @@ class GraphmlReader {
1892
2079
  }
1893
2080
  /** Close a `<desc>`. */
1894
2081
  finishDesc() {
1895
- const text = this.descText.trim();
2082
+ const text2 = this.descText.trim();
1896
2083
  if (this.descTarget === "key" && this.pendingKey !== null) {
1897
- this.pendingKey.desc = text;
1898
- } else if (this.descTarget === "graph" && text.length > 0) {
1899
- this.description = text;
2084
+ this.pendingKey.desc = text2;
2085
+ } else if (this.descTarget === "graph" && text2.length > 0) {
2086
+ this.description = text2;
1900
2087
  }
1901
2088
  this.descTarget = null;
1902
2089
  this.descText = "";
@@ -2003,16 +2190,16 @@ class GraphmlReader {
2003
2190
  * @param line - the line
2004
2191
  * @returns the count, or null
2005
2192
  */
2006
- hint(text, name, line) {
2007
- if (text === void 0) {
2193
+ hint(text2, name, line) {
2194
+ if (text2 === void 0) {
2008
2195
  return null;
2009
2196
  }
2010
- const n = hint(text);
2197
+ const n = hint(text2);
2011
2198
  if (n === null || n > MAX_COUNT) {
2012
2199
  this.report.warning(
2013
2200
  "validation-error",
2014
2201
  GRAPHML_ISSUE.COUNT_HINT,
2015
- `<graph ${name}="${text}"> is not a count the sink can reserve; ignored`,
2202
+ `<graph ${name}="${text2}"> is not a count the sink can reserve; ignored`,
2016
2203
  { line, element: name }
2017
2204
  );
2018
2205
  return null;
@@ -2060,8 +2247,9 @@ class GraphmlReader {
2060
2247
  }
2061
2248
  this.pendingKey = {
2062
2249
  id,
2063
- attrName: attrs.get("attr.name") ?? null,
2064
- attrType: attrs.get("attr.type") ?? null,
2250
+ // `name` / `type` without the `attr.` prefix are written by some tools; the spec's spelling wins
2251
+ attrName: attrs.get("attr.name") ?? attrs.get("name") ?? null,
2252
+ attrType: attrs.get("attr.type") ?? attrs.get("type") ?? null,
2065
2253
  yfilesType: attrs.get("yfiles.type") ?? null,
2066
2254
  domains,
2067
2255
  line,
@@ -2086,11 +2274,12 @@ class GraphmlReader {
2086
2274
  this.report.error("validation-error", GRAPHML_ISSUE.KEY_MISSING_ID, "<key> without an id", where);
2087
2275
  return;
2088
2276
  }
2089
- if (this.keys.has(key.id)) {
2277
+ const declared = this.keys.get(key.id) ?? [];
2278
+ if (declared.some((entry) => entry.domains.some((domain) => key.domains.includes(domain)))) {
2090
2279
  this.report.error(
2091
2280
  "validation-error",
2092
2281
  GRAPHML_ISSUE.DUPLICATE_KEY,
2093
- `key "${key.id}" is declared twice`,
2282
+ `key "${key.id}" is declared twice for the same kind of element`,
2094
2283
  where
2095
2284
  );
2096
2285
  return;
@@ -2141,18 +2330,21 @@ class GraphmlReader {
2141
2330
  this.report.recordError(err, where);
2142
2331
  }
2143
2332
  }
2144
- this.keys.set(key.id, {
2145
- id: key.id,
2146
- name,
2147
- node,
2148
- edge,
2149
- graph,
2150
- weight,
2151
- originalId,
2152
- yfiles,
2153
- skipped,
2154
- domains: key.domains
2155
- });
2333
+ this.keys.set(key.id, [
2334
+ ...declared,
2335
+ {
2336
+ id: key.id,
2337
+ name,
2338
+ node,
2339
+ edge,
2340
+ graph,
2341
+ weight,
2342
+ originalId,
2343
+ yfiles,
2344
+ skipped,
2345
+ domains: key.domains
2346
+ }
2347
+ ]);
2156
2348
  }
2157
2349
  /**
2158
2350
  * Declare the column of a typed (or untyped, then string) key in one domain.
@@ -2292,7 +2484,8 @@ class GraphmlReader {
2292
2484
  line
2293
2485
  });
2294
2486
  } else {
2295
- const key = this.keys.get(keyId);
2487
+ const entries = this.keys.get(keyId);
2488
+ const key = entries?.find((entry) => entry.domains.includes(domain)) ?? entries?.[0];
2296
2489
  if (key === void 0) {
2297
2490
  this.report.error(
2298
2491
  "validation-error",
@@ -2377,17 +2570,17 @@ class GraphmlReader {
2377
2570
  }
2378
2571
  return;
2379
2572
  }
2573
+ if (key.yfiles) {
2574
+ for (const [handle, mapped] of this.graphics("node", key, value, where)) {
2575
+ this.setNodeData(node, handle, mapped);
2576
+ }
2577
+ }
2380
2578
  if (key.node === null) {
2381
2579
  return;
2382
2580
  }
2383
2581
  const parsed = this.parseValue(key.node, value, where);
2384
- if (parsed === void 0) {
2385
- return;
2386
- }
2387
- if (node.index === INVALID_INDEX) {
2388
- node.pending.push(key.node.handle, parsed);
2389
- } else if (!node.failed) {
2390
- this.sink.setNodeValue(key.node.handle, node.index, parsed);
2582
+ if (parsed !== void 0) {
2583
+ this.setNodeData(node, key.node.handle, parsed);
2391
2584
  }
2392
2585
  return;
2393
2586
  }
@@ -2414,6 +2607,11 @@ class GraphmlReader {
2414
2607
  }
2415
2608
  return;
2416
2609
  }
2610
+ if (key.yfiles) {
2611
+ for (const [handle, mapped] of this.graphics("edge", key, value, where)) {
2612
+ edge.pending.push(handle, mapped);
2613
+ }
2614
+ }
2417
2615
  if (key.edge === null) {
2418
2616
  return;
2419
2617
  }
@@ -2443,6 +2641,52 @@ class GraphmlReader {
2443
2641
  }
2444
2642
  }
2445
2643
  }
2644
+ /**
2645
+ * Write a node value now, or keep it until the node is added.
2646
+ * @param node - the node state
2647
+ * @param handle - the column
2648
+ * @param value - the value
2649
+ */
2650
+ setNodeData(node, handle, value) {
2651
+ if (node.index === INVALID_INDEX) {
2652
+ node.pending.push(handle, value);
2653
+ } else if (!node.failed) {
2654
+ this.sink.setNodeValue(handle, node.index, value);
2655
+ }
2656
+ }
2657
+ /**
2658
+ * The typed values mapped from a yFiles graphics tree (yfiles.ts), each with its column,
2659
+ * declared on first use.
2660
+ * @param domain - node or edge
2661
+ * @param key - the yFiles key
2662
+ * @param tree - the `<data>` content
2663
+ * @param where - the location for issues
2664
+ * @returns column handle and value pairs
2665
+ */
2666
+ graphics(domain, key, tree, where) {
2667
+ const out = [];
2668
+ for (const [field, value] of graphicsValues(domain, tree)) {
2669
+ const id = `${domain}:${field}`;
2670
+ let handle = this.graphicsColumns.get(id);
2671
+ if (handle === void 0) {
2672
+ try {
2673
+ ({ handle } = declareResolved(
2674
+ this.sink,
2675
+ domain,
2676
+ graphicsDecl(domain, field, key.id),
2677
+ this.report,
2678
+ where
2679
+ ));
2680
+ } catch (err) {
2681
+ this.report.recordError(err, where);
2682
+ continue;
2683
+ }
2684
+ this.graphicsColumns.set(id, handle);
2685
+ }
2686
+ out.push([handle, value]);
2687
+ }
2688
+ return out;
2689
+ }
2446
2690
  /**
2447
2691
  * Parse the content of a `<data>` by its column's declaration.
2448
2692
  * @param target - the column
@@ -3002,13 +3246,13 @@ class GraphmlReader {
3002
3246
  * @param line - the line
3003
3247
  * @returns the node id
3004
3248
  */
3005
- endpointId(text, line) {
3006
- const restored = this.restored.get(text);
3249
+ endpointId(text2, line) {
3250
+ const restored = this.restored.get(text2);
3007
3251
  if (restored !== void 0) {
3008
3252
  return restored;
3009
3253
  }
3010
- const id = this.coercer.text(text);
3011
- this.reportMerge(text, line);
3254
+ const id = this.coercer.text(text2);
3255
+ this.reportMerge(text2, line);
3012
3256
  return id;
3013
3257
  }
3014
3258
  /**
@@ -3016,23 +3260,23 @@ class GraphmlReader {
3016
3260
  * @param text - the id text just coerced
3017
3261
  * @param line - the line
3018
3262
  */
3019
- reportMerge(text, line) {
3263
+ reportMerge(text2, line) {
3020
3264
  const merge = this.coercer.lastMerge;
3021
3265
  if (merge !== null) {
3022
3266
  this.report.warning(
3023
3267
  "coercion",
3024
3268
  GRAPHML_ISSUE.ID_MERGED,
3025
3269
  `id "${merge.text}" merged with "${merge.previousText}" as ${merge.id} under ids: "number"`,
3026
- { line, element: text }
3270
+ { line, element: text2 }
3027
3271
  );
3028
3272
  }
3029
3273
  }
3030
3274
  }
3031
- function hint(text) {
3032
- if (text === void 0 || !/^[0-9]+$/.test(text)) {
3275
+ function hint(text2) {
3276
+ if (text2 === void 0 || !/^[0-9]+$/.test(text2)) {
3033
3277
  return null;
3034
3278
  }
3035
- const n = Number(text);
3279
+ const n = Number(text2);
3036
3280
  return Number.isSafeInteger(n) ? n : null;
3037
3281
  }
3038
3282
  function resolveYfiles(options) {
@@ -3049,12 +3293,12 @@ function resolveYfiles(options) {
3049
3293
  return value;
3050
3294
  }
3051
3295
  function sniffGraphml(head) {
3052
- const text = new TextDecoder("utf-8", { fatal: false }).decode(head.subarray(0, SNIFF_BYTES));
3053
- const root = text.indexOf("<graphml");
3296
+ const text2 = new TextDecoder("utf-8", { fatal: false }).decode(head.subarray(0, SNIFF_BYTES));
3297
+ const root = text2.indexOf("<graphml");
3054
3298
  if (root >= 0) {
3055
- return text.includes(GRAPHML_NAMESPACE, root) ? 1 : 0.9;
3299
+ return text2.includes(GRAPHML_NAMESPACE, root) ? 1 : 0.9;
3056
3300
  }
3057
- return /^\s*<(\?xml|!--|!DOCTYPE)/.test(text) ? 0.05 : 0;
3301
+ return /^\s*<(\?xml|!--|!DOCTYPE)/.test(text2) ? 0.05 : 0;
3058
3302
  }
3059
3303
  async function importGraphml(input, sink, options) {
3060
3304
  const common = resolveImportOptions(options, FORMAT_DEFAULTS);
@@ -3088,4 +3332,4 @@ export {
3088
3332
  graphmlImporter as b,
3089
3333
  graphmlExporter as g
3090
3334
  };
3091
- //# sourceMappingURL=importer-BddBW3sf.js.map
3335
+ //# sourceMappingURL=importer-9PxhqH4v.js.map