@graphty/graph-io 0.3.49 → 0.3.50

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 (208) hide show
  1. package/dist/chunks/{approximations-Bzn3L-je.js → approximations-BRXsll0T.js} +2 -2
  2. package/dist/chunks/{approximations-Bzn3L-je.js.map → approximations-BRXsll0T.js.map} +1 -1
  3. package/dist/chunks/{block-iptrlMBb.js → block-yl0f_qLO.js} +27 -2
  4. package/dist/chunks/block-yl0f_qLO.js.map +1 -0
  5. package/dist/chunks/{context-C3p0RQxR.js → context-DizmppHy.js} +2 -2
  6. package/dist/chunks/{context-C3p0RQxR.js.map → context-DizmppHy.js.map} +1 -1
  7. package/dist/chunks/{credentials-DXHx7nwR.js → credentials-CmvJXYeH.js} +58 -9
  8. package/dist/chunks/credentials-CmvJXYeH.js.map +1 -0
  9. package/dist/chunks/{descriptor-DLA5kINx.js → descriptor-Dhez_Cew.js} +5 -1
  10. package/dist/chunks/{descriptor-DLA5kINx.js.map → descriptor-Dhez_Cew.js.map} +1 -1
  11. package/dist/chunks/{emit-BwHQkZle.js → emit-7Jkpg0yd.js} +132 -4
  12. package/dist/chunks/emit-7Jkpg0yd.js.map +1 -0
  13. package/dist/chunks/{exporter-nDfxIK5D.js → exporter-1Ajud15J.js} +4 -4
  14. package/dist/chunks/{exporter-nDfxIK5D.js.map → exporter-1Ajud15J.js.map} +1 -1
  15. package/dist/chunks/{exporter-D9yMtocA.js → exporter-B6yvL3wy.js} +3 -3
  16. package/dist/chunks/{exporter-D9yMtocA.js.map → exporter-B6yvL3wy.js.map} +1 -1
  17. package/dist/chunks/{exporter-_jrBTnos.js → exporter-CA5q8ROP.js} +4 -4
  18. package/dist/chunks/{exporter-_jrBTnos.js.map → exporter-CA5q8ROP.js.map} +1 -1
  19. package/dist/chunks/{exporter-D2CETOMK.js → exporter-CQQZFJ2v.js} +72 -10
  20. package/dist/chunks/exporter-CQQZFJ2v.js.map +1 -0
  21. package/dist/chunks/{generic-ymWCETAJ.js → generic-Dc21MAdg.js} +3 -3
  22. package/dist/chunks/{generic-ymWCETAJ.js.map → generic-Dc21MAdg.js.map} +1 -1
  23. package/dist/chunks/{importer-CT9mJ1nn.js → importer-C6NVpCXp.js} +7 -6
  24. package/dist/chunks/importer-C6NVpCXp.js.map +1 -0
  25. package/dist/chunks/{importer-GREKMdIf.js → importer-CKFe4q54.js} +4 -4
  26. package/dist/chunks/{importer-GREKMdIf.js.map → importer-CKFe4q54.js.map} +1 -1
  27. package/dist/chunks/{importer-PqaaKKnY.js → importer-CKbUbsJA.js} +4 -4
  28. package/dist/chunks/{importer-PqaaKKnY.js.map → importer-CKbUbsJA.js.map} +1 -1
  29. package/dist/chunks/{importer-DaLTjbds.js → importer-ClCsdgTJ.js} +97 -7
  30. package/dist/chunks/importer-ClCsdgTJ.js.map +1 -0
  31. package/dist/chunks/{importer-B92x1xY5.js → importer-CyVZTrf0.js} +3 -3
  32. package/dist/chunks/{importer-B92x1xY5.js.map → importer-CyVZTrf0.js.map} +1 -1
  33. package/dist/chunks/{importer-vRq4h3j5.js → importer-DgG4v1Ms.js} +89 -20
  34. package/dist/chunks/importer-DgG4v1Ms.js.map +1 -0
  35. package/dist/chunks/{importer-Vn5QvmmJ.js → importer-asFUrIsj.js} +2 -2
  36. package/dist/chunks/{importer-Vn5QvmmJ.js.map → importer-asFUrIsj.js.map} +1 -1
  37. package/dist/chunks/{lexicon-Clf4QIyl.js → lexicon-IVRq2Uoo.js} +2 -2
  38. package/dist/chunks/{lexicon-Clf4QIyl.js.map → lexicon-IVRq2Uoo.js.map} +1 -1
  39. package/dist/chunks/{round-trip-Zm3_ivgq.js → round-trip-Cy3_SHRZ.js} +2 -2
  40. package/dist/chunks/{round-trip-Zm3_ivgq.js.map → round-trip-Cy3_SHRZ.js.map} +1 -1
  41. package/dist/chunks/{sigv4-EokFic1V.js → sigv4-BXwVbza3.js} +3 -3
  42. package/dist/chunks/{sigv4-EokFic1V.js.map → sigv4-BXwVbza3.js.map} +1 -1
  43. package/dist/chunks/{styles-CCs214_3.js → styles-CAVmBLgp.js} +4 -4
  44. package/dist/chunks/{styles-CCs214_3.js.map → styles-CAVmBLgp.js.map} +1 -1
  45. package/dist/chunks/{styles-DPQQIig4.js → styles-CPczw1Jz.js} +4 -4
  46. package/dist/chunks/{styles-DPQQIig4.js.map → styles-CPczw1Jz.js.map} +1 -1
  47. package/dist/chunks/{styles-Cm62yyQX.js → styles-boK6Ln54.js} +65 -30
  48. package/dist/chunks/styles-boK6Ln54.js.map +1 -0
  49. package/dist/chunks/{styles-yed-UMFdNqze.js → styles-yed-XeFBQ6VS.js} +8 -8
  50. package/dist/chunks/{styles-yed-UMFdNqze.js.map → styles-yed-XeFBQ6VS.js.map} +1 -1
  51. package/dist/cx.js +2 -2
  52. package/dist/cx2.js +2 -2
  53. package/dist/cys.js +1 -1
  54. package/dist/dot.js +7 -4
  55. package/dist/gexf.js +2 -2
  56. package/dist/gml.js +8 -8
  57. package/dist/gml.js.map +1 -1
  58. package/dist/graph-io.js +129 -104
  59. package/dist/graph-io.js.map +1 -1
  60. package/dist/graphml.js +2 -2
  61. package/dist/json.js +1 -1
  62. package/dist/pajek.js +1 -1
  63. package/dist/sources-cypher.js +25 -22
  64. package/dist/sources-cypher.js.map +1 -1
  65. package/dist/sources-ndex.js +2 -2
  66. package/dist/sources-rest.js +20 -4
  67. package/dist/sources-rest.js.map +1 -1
  68. package/dist/sources-sparql.js +40 -6
  69. package/dist/sources-sparql.js.map +1 -1
  70. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  71. package/dist/src/formats/dot/exporter.js +34 -2
  72. package/dist/src/formats/dot/exporter.js.map +1 -1
  73. package/dist/src/formats/dot/importer.d.ts +6 -1
  74. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  75. package/dist/src/formats/dot/importer.js +65 -5
  76. package/dist/src/formats/dot/importer.js.map +1 -1
  77. package/dist/src/formats/dot/index.d.ts +3 -1
  78. package/dist/src/formats/dot/index.d.ts.map +1 -1
  79. package/dist/src/formats/dot/index.js +2 -1
  80. package/dist/src/formats/dot/index.js.map +1 -1
  81. package/dist/src/formats/dot/label-places.d.ts +7 -0
  82. package/dist/src/formats/dot/label-places.d.ts.map +1 -1
  83. package/dist/src/formats/dot/label-places.js +9 -3
  84. package/dist/src/formats/dot/label-places.js.map +1 -1
  85. package/dist/src/formats/dot/names.d.ts +13 -0
  86. package/dist/src/formats/dot/names.d.ts.map +1 -1
  87. package/dist/src/formats/dot/names.js +13 -0
  88. package/dist/src/formats/dot/names.js.map +1 -1
  89. package/dist/src/formats/dot/styles.d.ts +10 -0
  90. package/dist/src/formats/dot/styles.d.ts.map +1 -1
  91. package/dist/src/formats/dot/styles.js +49 -3
  92. package/dist/src/formats/dot/styles.js.map +1 -1
  93. package/dist/src/formats/gml/styles.js +1 -1
  94. package/dist/src/formats/gml/styles.js.map +1 -1
  95. package/dist/src/formats/graphml/styles-yed.js +3 -3
  96. package/dist/src/formats/graphml/styles-yed.js.map +1 -1
  97. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
  98. package/dist/src/formats/graphml/styles-yfiles3.js +6 -4
  99. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
  100. package/dist/src/formats/json/styles.d.ts.map +1 -1
  101. package/dist/src/formats/json/styles.js +94 -6
  102. package/dist/src/formats/json/styles.js.map +1 -1
  103. package/dist/src/formats/pajek/draw.d.ts +8 -0
  104. package/dist/src/formats/pajek/draw.d.ts.map +1 -1
  105. package/dist/src/formats/pajek/draw.js +10 -0
  106. package/dist/src/formats/pajek/draw.js.map +1 -1
  107. package/dist/src/formats/pajek/styles.d.ts.map +1 -1
  108. package/dist/src/formats/pajek/styles.js +142 -25
  109. package/dist/src/formats/pajek/styles.js.map +1 -1
  110. package/dist/src/index.d.ts +3 -3
  111. package/dist/src/index.d.ts.map +1 -1
  112. package/dist/src/index.js +2 -2
  113. package/dist/src/index.js.map +1 -1
  114. package/dist/src/sources/credentials.d.ts +50 -1
  115. package/dist/src/sources/credentials.d.ts.map +1 -1
  116. package/dist/src/sources/credentials.js +69 -0
  117. package/dist/src/sources/credentials.js.map +1 -1
  118. package/dist/src/sources/cypher/index.d.ts.map +1 -1
  119. package/dist/src/sources/cypher/index.js +12 -11
  120. package/dist/src/sources/cypher/index.js.map +1 -1
  121. package/dist/src/sources/cypher/query-builder.d.ts +6 -6
  122. package/dist/src/sources/cypher/query-builder.d.ts.map +1 -1
  123. package/dist/src/sources/cypher/query-builder.js +15 -15
  124. package/dist/src/sources/cypher/query-builder.js.map +1 -1
  125. package/dist/src/sources/rest/descriptor.d.ts.map +1 -1
  126. package/dist/src/sources/rest/descriptor.js +4 -0
  127. package/dist/src/sources/rest/descriptor.js.map +1 -1
  128. package/dist/src/sources/rest/openalex.d.ts +4 -2
  129. package/dist/src/sources/rest/openalex.d.ts.map +1 -1
  130. package/dist/src/sources/rest/openalex.js +4 -2
  131. package/dist/src/sources/rest/openalex.js.map +1 -1
  132. package/dist/src/sources/rest/source.d.ts.map +1 -1
  133. package/dist/src/sources/rest/source.js +23 -0
  134. package/dist/src/sources/rest/source.js.map +1 -1
  135. package/dist/src/sources/run.d.ts +2 -0
  136. package/dist/src/sources/run.d.ts.map +1 -1
  137. package/dist/src/sources/run.js +30 -3
  138. package/dist/src/sources/run.js.map +1 -1
  139. package/dist/src/sources/sparql/index.d.ts.map +1 -1
  140. package/dist/src/sources/sparql/index.js +14 -1
  141. package/dist/src/sources/sparql/index.js.map +1 -1
  142. package/dist/src/sources/sparql/query.d.ts +2 -1
  143. package/dist/src/sources/sparql/query.d.ts.map +1 -1
  144. package/dist/src/sources/sparql/query.js +55 -4
  145. package/dist/src/sources/sparql/query.js.map +1 -1
  146. package/dist/src/sources/types.d.ts +29 -0
  147. package/dist/src/sources/types.d.ts.map +1 -1
  148. package/dist/src/styles/canon/enums.d.ts.map +1 -1
  149. package/dist/src/styles/canon/enums.js +8 -0
  150. package/dist/src/styles/canon/enums.js.map +1 -1
  151. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
  152. package/dist/src/styles/cyjs/cascade.js +124 -0
  153. package/dist/src/styles/cyjs/cascade.js.map +1 -1
  154. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
  155. package/dist/src/styles/cyjs/defaults-table.js +1 -0
  156. package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
  157. package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
  158. package/dist/src/styles/cyjs/emit.js +30 -1
  159. package/dist/src/styles/cyjs/emit.js.map +1 -1
  160. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
  161. package/dist/src/styles/cyjs/mappers.js +14 -0
  162. package/dist/src/styles/cyjs/mappers.js.map +1 -1
  163. package/dist/src/styles/types.d.ts +65 -0
  164. package/dist/src/styles/types.d.ts.map +1 -1
  165. package/dist/src/styles/vocabulary.d.ts.map +1 -1
  166. package/dist/src/styles/vocabulary.js +21 -0
  167. package/dist/src/styles/vocabulary.js.map +1 -1
  168. package/dist/styles.js +6 -6
  169. package/dist/xgmml.js +3 -3
  170. package/package.json +2 -2
  171. package/src/formats/dot/exporter.ts +37 -1
  172. package/src/formats/dot/importer.ts +75 -4
  173. package/src/formats/dot/index.ts +3 -1
  174. package/src/formats/dot/label-places.ts +16 -3
  175. package/src/formats/dot/names.ts +15 -0
  176. package/src/formats/dot/styles.ts +55 -3
  177. package/src/formats/gml/styles.ts +1 -1
  178. package/src/formats/graphml/styles-yed.ts +3 -3
  179. package/src/formats/graphml/styles-yfiles3.ts +6 -4
  180. package/src/formats/json/styles.ts +102 -6
  181. package/src/formats/pajek/draw.ts +11 -0
  182. package/src/formats/pajek/styles.ts +151 -26
  183. package/src/index.ts +7 -0
  184. package/src/sources/credentials.ts +90 -1
  185. package/src/sources/cypher/index.ts +12 -11
  186. package/src/sources/cypher/query-builder.ts +15 -15
  187. package/src/sources/rest/descriptor.ts +4 -0
  188. package/src/sources/rest/openalex.ts +4 -2
  189. package/src/sources/rest/source.ts +25 -0
  190. package/src/sources/run.ts +35 -3
  191. package/src/sources/sparql/index.ts +16 -1
  192. package/src/sources/sparql/query.ts +58 -4
  193. package/src/sources/types.ts +33 -0
  194. package/src/styles/canon/enums.ts +9 -0
  195. package/src/styles/cyjs/cascade.ts +143 -0
  196. package/src/styles/cyjs/defaults-table.ts +1 -0
  197. package/src/styles/cyjs/emit.ts +35 -1
  198. package/src/styles/cyjs/mappers.ts +14 -0
  199. package/src/styles/types.ts +65 -0
  200. package/src/styles/vocabulary.ts +21 -0
  201. package/dist/chunks/block-iptrlMBb.js.map +0 -1
  202. package/dist/chunks/credentials-DXHx7nwR.js.map +0 -1
  203. package/dist/chunks/emit-BwHQkZle.js.map +0 -1
  204. package/dist/chunks/exporter-D2CETOMK.js.map +0 -1
  205. package/dist/chunks/importer-CT9mJ1nn.js.map +0 -1
  206. package/dist/chunks/importer-DaLTjbds.js.map +0 -1
  207. package/dist/chunks/importer-vRq4h3j5.js.map +0 -1
  208. package/dist/chunks/styles-Cm62yyQX.js.map +0 -1
@@ -6,10 +6,11 @@ import { L as LOSS, c as capabilities, r as resolveExportOptions, a as checkCapa
6
6
  import { p as plural, a as agree, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, b as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, I as ID_MERGED_CODE, O as OPTION_IGNORED_CODE, C as COLUMN_RENAMED_CODE, R as ROLE_TAKEN_CODE, l as DUPLICATE_ATTRIBUTE_CODE, P as PRECISION_CODE, W as AMBIGUOUS_GRAPH_NAME_CODE, Y as GRAPH_NOT_FOUND_CODE, i as MULTIPLE_GRAPHS_CODE, r as ENCODING_CONFLICT_CODE, s as UNKNOWN_ENCODING_CODE, t as ENCODING_FALLBACK_CODE, u as INVALID_ENCODING_CODE, v as INVALID_UTF8_CODE, E as EMPTY_INPUT_CODE, Q as SYNTAX_CODE, y as ELEMENT_ISSUE, z as INPUT_ISSUE, K as ImportReportBuilder } from "./report-CwMfYa9J.js";
7
7
  import { i as inferTextDtype, W as WIDENING_UNSUPPORTED_CODE, p as parseTextCell, T as TextCellWriter } from "./text-ystE2bYi.js";
8
8
  import { j as joinText, e as encodeChunks, i as indentUnit } from "./writer-DxjgHwSe.js";
9
- import { a as DOT_ATTRIBUTE_CLASH_CODE, b as DOT_FORMAT, d as dotStyledExport, D as DOT_STYLE_CAPABILITIES, C as CLUSTER_COLUMN, P as POS_ATTRIBUTE, K as KEY_ATTRIBUTE, L as LABEL_ATTRIBUTE, c as DOT_META_KEY, i as isHtmlString, e as PARENT_COLUMN, T as TARGET_PORT_COLUMN, S as SOURCE_PORT_COLUMN, f as dotStyles, g as DOT_ORIGIN, h as PIN_ATTRIBUTE, j as POSITION_COLUMN } from "./styles-Cm62yyQX.js";
9
+ import { b as DOT_ATTRIBUTE_CLASH_CODE, c as DOT_FORMAT, d as dotStyledExport, D as DOT_STYLE_CAPABILITIES, C as CLUSTER_COLUMN, P as POS_ATTRIBUTE, K as KEY_ATTRIBUTE, L as LABEL_ATTRIBUTE, R as RANK_COLUMN, a as RANK_SET_COLUMN, e as DOT_META_KEY, i as isHtmlString, f as PARENT_COLUMN, T as TARGET_PORT_COLUMN, S as SOURCE_PORT_COLUMN, h as dotStyles, j as DOT_ORIGIN, k as PIN_ATTRIBUTE, l as POSITION_COLUMN } from "./styles-boK6Ln54.js";
10
10
  import { i as importChosenGraph } from "./choose-CczZJaX_.js";
11
11
  import { r as readText, b as throwIfAborted, c as canonicalEncoding } from "./input-CAxt_aV-.js";
12
12
  import { m as median } from "./convert-Ct2zelbQ.js";
13
+ const RANK_COLUMNS = /* @__PURE__ */ new Set([RANK_COLUMN, RANK_SET_COLUMN]);
13
14
  const DOT_LOSS = Object.freeze({
14
15
  /**
15
16
  * An id, name or text with a backslash before a quote or a line break, or at its end, cannot be written as a DOT
@@ -392,7 +393,7 @@ class ExportPlan {
392
393
  */
393
394
  writesNodeColumn(column) {
394
395
  const { name, role } = column.meta;
395
- if (!isTextualDtype(column) || name === CLUSTER_COLUMN) {
396
+ if (!isTextualDtype(column) || name === CLUSTER_COLUMN || RANK_COLUMNS.has(name)) {
396
397
  return false;
397
398
  }
398
399
  if (role !== null && SKIPPED_NODE_ROLES.has(role)) {
@@ -648,9 +649,39 @@ class ExportPlan {
648
649
  for (let i = 0; i < snapshot.nodeCount; i++) {
649
650
  yield* this.writeNode(i, 1, emitted);
650
651
  }
652
+ yield* this.writeRanks();
651
653
  yield* this.writeEdges();
652
654
  yield "}\n";
653
655
  }
656
+ /**
657
+ * Write each rank subgraph the `graphty.rank` and `graphty.rankSet` columns hold, after the nodes: a named one
658
+ * under its name, an anonymous one (`%<n>`) as an anonymous subgraph.
659
+ * @yields the statements
660
+ * @returns nothing
661
+ */
662
+ *writeRanks() {
663
+ const { nodes } = this.snapshot;
664
+ const rank = nodes.get(RANK_COLUMN);
665
+ const set = nodes.get(RANK_SET_COLUMN);
666
+ if (rank === null || set === null) {
667
+ return;
668
+ }
669
+ const sets = /* @__PURE__ */ new Map();
670
+ for (let i = 0; i < this.snapshot.nodeCount; i++) {
671
+ if (rank.isSet(i) && set.isSet(i)) {
672
+ const key = String(set.value(i));
673
+ const entry = sets.get(key) ?? { rank: String(rank.value(i)), members: [] };
674
+ entry.members.push(writeId(this.idText(i)));
675
+ sets.set(key, entry);
676
+ }
677
+ }
678
+ const order = (key) => key.startsWith("%") ? Number(key.slice(1)) : -1;
679
+ for (const [key, entry] of [...sets].sort(([a], [b]) => order(a) - order(b))) {
680
+ const head = key.startsWith("%") ? "" : `subgraph ${quoteDotId(key)} `;
681
+ yield `${this.indent}${head}{ rank=${quoteDotId(entry.rank)}; ${entry.members.join("; ")}; }
682
+ `;
683
+ }
684
+ }
654
685
  /**
655
686
  * Write one node (and, for a container, its cluster block with the members nested).
656
687
  * @param i - the node index
@@ -1399,8 +1430,13 @@ const DOT_ISSUE = Object.freeze({
1399
1430
  AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
1400
1431
  /** A badly delimited numeral (`1e3`) split into two tokens, as Graphviz does with a warning. */
1401
1432
  NUMERAL_AMBIGUITY: "W_DOT_NUMERAL_AMBIGUITY",
1402
- /** Attributes of a subgraph that is not a cluster (rank=same and the like) cannot be represented. */
1433
+ /**
1434
+ * Attributes of a subgraph that is not a cluster cannot be represented, except its `rank`, which is kept in the
1435
+ * `graphty.rank` and `graphty.rankSet` node columns.
1436
+ */
1403
1437
  SUBGRAPH_ATTRIBUTES_DROPPED: "W_DOT_SUBGRAPH_ATTRIBUTES_DROPPED",
1438
+ /** A node in two rank subgraphs (`rank=same` and the like) keeps the first one's rank. */
1439
+ RANK_CONFLICT: "W_DOT_RANK_CONFLICT",
1404
1440
  /** A port on a node statement has no meaning and was dropped. */
1405
1441
  NODE_PORT_DROPPED: "W_DOT_NODE_PORT_DROPPED",
1406
1442
  /** A plain node and a cluster share a name and were merged into one container node. */
@@ -1478,6 +1514,8 @@ const EXTENSIONS = Object.freeze([".dot", ".gv"]);
1478
1514
  const MIME_TYPES = Object.freeze(["text/vnd.graphviz"]);
1479
1515
  const CLUSTER_PREFIX = "cluster";
1480
1516
  const CLUSTER_ATTRIBUTE = "cluster";
1517
+ const RANK_ATTRIBUTE = "rank";
1518
+ const RANK_KINDS = /* @__PURE__ */ new Set(["same", "min", "max", "source", "sink"]);
1481
1519
  const STATEMENTS_PER_ABORT_CHECK = 64;
1482
1520
  const MAX_ANCESTOR_WALK = 4096;
1483
1521
  const INHERITED_GRAPH_ATTRIBUTES = /* @__PURE__ */ new Set([
@@ -1800,6 +1838,10 @@ class DotParser {
1800
1838
  this.keyHandle = INVALID_INDEX;
1801
1839
  this.sourcePortHandle = INVALID_INDEX;
1802
1840
  this.targetPortHandle = INVALID_INDEX;
1841
+ this.rankHandle = INVALID_INDEX;
1842
+ this.rankSetHandle = INVALID_INDEX;
1843
+ this.rankSetOf = /* @__PURE__ */ new Map();
1844
+ this.anonymousRanks = 0;
1803
1845
  this.depth = 0;
1804
1846
  this.posDims = 0;
1805
1847
  this.headCharset = void 0;
@@ -2164,14 +2206,16 @@ class DotParser {
2164
2206
  }
2165
2207
  }
2166
2208
  /**
2167
- * Finish a subgraph that is not a cluster: report its attributes dropped, and hand its nested
2209
+ * Finish a subgraph that is not a cluster: keep its rank, report its other attributes dropped, and hand its nested
2168
2210
  * containers to the enclosing scope.
2169
2211
  * @param scope - the closed scope
2170
2212
  * @param parent - the enclosing scope
2171
2213
  */
2172
2214
  closePlainSubgraph(scope, parent) {
2173
- if (scope.attributes.size > 0) {
2174
- const names = [...scope.attributes.keys()].join(", ");
2215
+ const ranked = this.recordRank(scope);
2216
+ const dropped = [...scope.attributes.keys()].filter((name) => !(ranked && name === RANK_ATTRIBUTE));
2217
+ if (dropped.length > 0) {
2218
+ const names = dropped.join(", ");
2175
2219
  this.report.warning(
2176
2220
  "unsupported",
2177
2221
  DOT_ISSUE.SUBGRAPH_ATTRIBUTES_DROPPED,
@@ -2891,6 +2935,52 @@ class DotParser {
2891
2935
  }
2892
2936
  return this.positionHandle;
2893
2937
  }
2938
+ /**
2939
+ * Keep a non-cluster subgraph's `rank` on its nodes: each gets the rank and the subgraph's name (`%<n>` for an
2940
+ * anonymous one). A node already in another rank subgraph keeps that one, with a warning.
2941
+ * @param scope - the closed subgraph
2942
+ * @returns true when the subgraph has a rank Graphviz knows
2943
+ */
2944
+ recordRank(scope) {
2945
+ const rank = scope.attributes.get(RANK_ATTRIBUTE)?.value.trim();
2946
+ if (rank === void 0 || !RANK_KINDS.has(rank)) {
2947
+ return false;
2948
+ }
2949
+ const set = scope.name ?? `%${this.anonymousRanks++}`;
2950
+ for (const member of scope.members) {
2951
+ const held = this.rankSetOf.get(member);
2952
+ const id = String(this.idOf.get(member) ?? member);
2953
+ if (held === void 0) {
2954
+ this.rankSetOf.set(member, set);
2955
+ try {
2956
+ this.sink.setNodeValue(this.stringColumn("rankHandle", RANK_COLUMN), member, rank);
2957
+ this.sink.setNodeValue(this.stringColumn("rankSetHandle", RANK_SET_COLUMN), member, set);
2958
+ } catch (err) {
2959
+ this.report.recordError(err, { element: id });
2960
+ }
2961
+ } else if (held !== set) {
2962
+ this.report.warning(
2963
+ "coercion",
2964
+ DOT_ISSUE.RANK_CONFLICT,
2965
+ `node ${JSON.stringify(id)} is in rank subgraph ${JSON.stringify(held)} and in rank subgraph ${JSON.stringify(set)}; the first is kept`,
2966
+ { line: scope.line, element: id }
2967
+ );
2968
+ }
2969
+ }
2970
+ return true;
2971
+ }
2972
+ /**
2973
+ * One of the importer's string node columns, declared on first use.
2974
+ * @param field - the field holding its handle
2975
+ * @param name - the column name
2976
+ * @returns the handle
2977
+ */
2978
+ stringColumn(field, name) {
2979
+ if (this[field] === INVALID_INDEX) {
2980
+ this[field] = this.declare("node", { name, dtype: "string", nullable: true, origin: DOT_ORIGIN });
2981
+ }
2982
+ return this[field];
2983
+ }
2894
2984
  /**
2895
2985
  * The cluster marker column (bool).
2896
2986
  * @returns the handle
@@ -3007,4 +3097,4 @@ export {
3007
3097
  dotImporter as b,
3008
3098
  dotExporter as d
3009
3099
  };
3010
- //# sourceMappingURL=importer-DaLTjbds.js.map
3100
+ //# sourceMappingURL=importer-ClCsdgTJ.js.map