@graphty/graph-io 0.3.18 → 0.3.20

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 (235) hide show
  1. package/README.md +80 -2
  2. package/dist/chunks/{escape-D-gZWO26.js → escape-B-tkk_Cl.js} +74 -16
  3. package/dist/chunks/escape-B-tkk_Cl.js.map +1 -0
  4. package/dist/chunks/{writer-GAdltGmC.js → export-Bh60Dv-n.js} +7 -252
  5. package/dist/chunks/export-Bh60Dv-n.js.map +1 -0
  6. package/dist/chunks/exporter-BGrsWamJ.js +736 -0
  7. package/dist/chunks/exporter-BGrsWamJ.js.map +1 -0
  8. package/dist/chunks/exporter-DIeGJXAZ.js +834 -0
  9. package/dist/chunks/exporter-DIeGJXAZ.js.map +1 -0
  10. package/dist/chunks/{importer-Br_QeAeE.js → importer-BWY2FFc5.js} +28 -6
  11. package/dist/chunks/importer-BWY2FFc5.js.map +1 -0
  12. package/dist/chunks/importer-BaQpCEcJ.js +2194 -0
  13. package/dist/chunks/importer-BaQpCEcJ.js.map +1 -0
  14. package/dist/chunks/importer-DD-xv9_Y.js +1471 -0
  15. package/dist/chunks/importer-DD-xv9_Y.js.map +1 -0
  16. package/dist/chunks/{importer-aNJfe0qu.js → importer-DEcKhsmQ.js} +2 -2
  17. package/dist/chunks/{importer-aNJfe0qu.js.map → importer-DEcKhsmQ.js.map} +1 -1
  18. package/dist/chunks/{importer-DHagxvDD.js → importer-DIrbbnAf.js} +6 -4
  19. package/dist/chunks/{importer-DHagxvDD.js.map → importer-DIrbbnAf.js.map} +1 -1
  20. package/dist/chunks/importer-D_e7e7LX.js +3055 -0
  21. package/dist/chunks/importer-D_e7e7LX.js.map +1 -0
  22. package/dist/chunks/{importer-Du5crN9l.js → importer-Wv4C4gaF.js} +278 -82
  23. package/dist/chunks/importer-Wv4C4gaF.js.map +1 -0
  24. package/dist/chunks/importer-vi3bSdtw.js +1713 -0
  25. package/dist/chunks/importer-vi3bSdtw.js.map +1 -0
  26. package/dist/chunks/{importer-d0uQxFp6.js → importer-zGLo8Dg_.js} +6 -4
  27. package/dist/chunks/{importer-d0uQxFp6.js.map → importer-zGLo8Dg_.js.map} +1 -1
  28. package/dist/chunks/json-elements-DDS8N2Dd.js +779 -0
  29. package/dist/chunks/json-elements-DDS8N2Dd.js.map +1 -0
  30. package/dist/chunks/{records-Bk9jgodz.js → records-dbRkxwaq.js} +2 -2
  31. package/dist/chunks/{records-Bk9jgodz.js.map → records-dbRkxwaq.js.map} +1 -1
  32. package/dist/chunks/{report-BOk0p5y8.js → report-B1z4WT9e.js} +143 -111
  33. package/dist/chunks/{report-BOk0p5y8.js.map → report-B1z4WT9e.js.map} +1 -1
  34. package/dist/chunks/weights-Dzba96G3.js +176 -0
  35. package/dist/chunks/weights-Dzba96G3.js.map +1 -0
  36. package/dist/chunks/writer-C7flM-Ih.js +77 -0
  37. package/dist/chunks/writer-C7flM-Ih.js.map +1 -0
  38. package/dist/csv.js +6 -4
  39. package/dist/csv.js.map +1 -1
  40. package/dist/cx.d.ts +1 -0
  41. package/dist/cx.js +6 -0
  42. package/dist/cx.js.map +1 -0
  43. package/dist/cx2.d.ts +1 -0
  44. package/dist/cx2.js +10 -0
  45. package/dist/cx2.js.map +1 -0
  46. package/dist/cys.d.ts +1 -0
  47. package/dist/cys.js +6 -0
  48. package/dist/cys.js.map +1 -0
  49. package/dist/dot.js +1 -1
  50. package/dist/gexf.js +16 -4
  51. package/dist/gexf.js.map +1 -1
  52. package/dist/gml.js +13 -14
  53. package/dist/gml.js.map +1 -1
  54. package/dist/graph-io.js +302 -399
  55. package/dist/graph-io.js.map +1 -1
  56. package/dist/graphml.js +1 -1
  57. package/dist/json.js +1 -1
  58. package/dist/neo4j.js +6 -4
  59. package/dist/neo4j.js.map +1 -1
  60. package/dist/obo.js +1 -1
  61. package/dist/pajek.js +1 -1
  62. package/dist/src/common/cell-budget.d.ts +84 -0
  63. package/dist/src/common/cell-budget.d.ts.map +1 -0
  64. package/dist/src/common/cell-budget.js +138 -0
  65. package/dist/src/common/cell-budget.js.map +1 -0
  66. package/dist/src/common/input.d.ts +10 -0
  67. package/dist/src/common/input.d.ts.map +1 -1
  68. package/dist/src/common/input.js +39 -0
  69. package/dist/src/common/input.js.map +1 -1
  70. package/dist/src/common/json-elements.d.ts +286 -0
  71. package/dist/src/common/json-elements.d.ts.map +1 -0
  72. package/dist/src/common/json-elements.js +926 -0
  73. package/dist/src/common/json-elements.js.map +1 -0
  74. package/dist/src/common/options.d.ts +6 -0
  75. package/dist/src/common/options.d.ts.map +1 -1
  76. package/dist/src/common/options.js +1 -1
  77. package/dist/src/common/options.js.map +1 -1
  78. package/dist/src/common/xml.d.ts +37 -3
  79. package/dist/src/common/xml.d.ts.map +1 -1
  80. package/dist/src/common/xml.js +86 -6
  81. package/dist/src/common/xml.js.map +1 -1
  82. package/dist/src/common/zip.d.ts +92 -0
  83. package/dist/src/common/zip.d.ts.map +1 -0
  84. package/dist/src/common/zip.js +399 -0
  85. package/dist/src/common/zip.js.map +1 -0
  86. package/dist/src/formats/cx/importer.d.ts +133 -0
  87. package/dist/src/formats/cx/importer.d.ts.map +1 -0
  88. package/dist/src/formats/cx/importer.js +2220 -0
  89. package/dist/src/formats/cx/importer.js.map +1 -0
  90. package/dist/src/formats/cx/index.d.ts +7 -0
  91. package/dist/src/formats/cx/index.d.ts.map +1 -0
  92. package/dist/src/formats/cx/index.js +7 -0
  93. package/dist/src/formats/cx/index.js.map +1 -0
  94. package/dist/src/formats/cx2/exporter.d.ts +59 -0
  95. package/dist/src/formats/cx2/exporter.d.ts.map +1 -0
  96. package/dist/src/formats/cx2/exporter.js +864 -0
  97. package/dist/src/formats/cx2/exporter.js.map +1 -0
  98. package/dist/src/formats/cx2/importer.d.ts +169 -0
  99. package/dist/src/formats/cx2/importer.d.ts.map +1 -0
  100. package/dist/src/formats/cx2/importer.js +1652 -0
  101. package/dist/src/formats/cx2/importer.js.map +1 -0
  102. package/dist/src/formats/cx2/index.d.ts +7 -0
  103. package/dist/src/formats/cx2/index.d.ts.map +1 -0
  104. package/dist/src/formats/cx2/index.js +7 -0
  105. package/dist/src/formats/cx2/index.js.map +1 -0
  106. package/dist/src/formats/cys/constants.d.ts +68 -0
  107. package/dist/src/formats/cys/constants.d.ts.map +1 -0
  108. package/dist/src/formats/cys/constants.js +76 -0
  109. package/dist/src/formats/cys/constants.js.map +1 -0
  110. package/dist/src/formats/cys/importer.d.ts +38 -0
  111. package/dist/src/formats/cys/importer.d.ts.map +1 -0
  112. package/dist/src/formats/cys/importer.js +830 -0
  113. package/dist/src/formats/cys/importer.js.map +1 -0
  114. package/dist/src/formats/cys/index.d.ts +7 -0
  115. package/dist/src/formats/cys/index.d.ts.map +1 -0
  116. package/dist/src/formats/cys/index.js +7 -0
  117. package/dist/src/formats/cys/index.js.map +1 -0
  118. package/dist/src/formats/cys/session.d.ts +132 -0
  119. package/dist/src/formats/cys/session.d.ts.map +1 -0
  120. package/dist/src/formats/cys/session.js +315 -0
  121. package/dist/src/formats/cys/session.js.map +1 -0
  122. package/dist/src/formats/cys/tables.d.ts +90 -0
  123. package/dist/src/formats/cys/tables.d.ts.map +1 -0
  124. package/dist/src/formats/cys/tables.js +293 -0
  125. package/dist/src/formats/cys/tables.js.map +1 -0
  126. package/dist/src/formats/dot/exporter.d.ts +2 -0
  127. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  128. package/dist/src/formats/dot/exporter.js +12 -0
  129. package/dist/src/formats/dot/exporter.js.map +1 -1
  130. package/dist/src/formats/dot/importer.js +6 -2
  131. package/dist/src/formats/dot/importer.js.map +1 -1
  132. package/dist/src/formats/gexf/exporter.d.ts +2 -0
  133. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  134. package/dist/src/formats/gexf/exporter.js +6 -0
  135. package/dist/src/formats/gexf/exporter.js.map +1 -1
  136. package/dist/src/formats/gexf/importer.d.ts +0 -2
  137. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  138. package/dist/src/formats/gexf/importer.js +1 -3
  139. package/dist/src/formats/gexf/importer.js.map +1 -1
  140. package/dist/src/formats/gexf/index.d.ts +1 -1
  141. package/dist/src/formats/gexf/index.js +2 -2
  142. package/dist/src/formats/gexf/index.js.map +1 -1
  143. package/dist/src/formats/gml/exporter.d.ts +0 -8
  144. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  145. package/dist/src/formats/gml/exporter.js +8 -18
  146. package/dist/src/formats/gml/exporter.js.map +1 -1
  147. package/dist/src/formats/json/importer.d.ts.map +1 -1
  148. package/dist/src/formats/json/importer.js +6 -104
  149. package/dist/src/formats/json/importer.js.map +1 -1
  150. package/dist/src/formats/xgmml/columns.d.ts +152 -0
  151. package/dist/src/formats/xgmml/columns.d.ts.map +1 -0
  152. package/dist/src/formats/xgmml/columns.js +593 -0
  153. package/dist/src/formats/xgmml/columns.js.map +1 -0
  154. package/dist/src/formats/xgmml/constants.d.ts +196 -0
  155. package/dist/src/formats/xgmml/constants.d.ts.map +1 -0
  156. package/dist/src/formats/xgmml/constants.js +197 -0
  157. package/dist/src/formats/xgmml/constants.js.map +1 -0
  158. package/dist/src/formats/xgmml/document.d.ts +361 -0
  159. package/dist/src/formats/xgmml/document.d.ts.map +1 -0
  160. package/dist/src/formats/xgmml/document.js +695 -0
  161. package/dist/src/formats/xgmml/document.js.map +1 -0
  162. package/dist/src/formats/xgmml/emit.d.ts +341 -0
  163. package/dist/src/formats/xgmml/emit.d.ts.map +1 -0
  164. package/dist/src/formats/xgmml/emit.js +1275 -0
  165. package/dist/src/formats/xgmml/emit.js.map +1 -0
  166. package/dist/src/formats/xgmml/exporter.d.ts +24 -0
  167. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -0
  168. package/dist/src/formats/xgmml/exporter.js +999 -0
  169. package/dist/src/formats/xgmml/exporter.js.map +1 -0
  170. package/dist/src/formats/xgmml/importer.d.ts +58 -0
  171. package/dist/src/formats/xgmml/importer.d.ts.map +1 -0
  172. package/dist/src/formats/xgmml/importer.js +360 -0
  173. package/dist/src/formats/xgmml/importer.js.map +1 -0
  174. package/dist/src/formats/xgmml/index.d.ts +8 -0
  175. package/dist/src/formats/xgmml/index.d.ts.map +1 -0
  176. package/dist/src/formats/xgmml/index.js +8 -0
  177. package/dist/src/formats/xgmml/index.js.map +1 -0
  178. package/dist/src/formats/xgmml/values.d.ts +60 -0
  179. package/dist/src/formats/xgmml/values.d.ts.map +1 -0
  180. package/dist/src/formats/xgmml/values.js +148 -0
  181. package/dist/src/formats/xgmml/values.js.map +1 -0
  182. package/dist/src/index.d.ts +4 -0
  183. package/dist/src/index.d.ts.map +1 -1
  184. package/dist/src/index.js +4 -0
  185. package/dist/src/index.js.map +1 -1
  186. package/dist/src/registry.d.ts +7 -0
  187. package/dist/src/registry.d.ts.map +1 -1
  188. package/dist/src/registry.js +30 -10
  189. package/dist/src/registry.js.map +1 -1
  190. package/dist/src/sniff.d.ts +1 -1
  191. package/dist/src/sniff.d.ts.map +1 -1
  192. package/dist/src/sniff.js +4 -0
  193. package/dist/src/sniff.js.map +1 -1
  194. package/dist/xgmml.d.ts +1 -0
  195. package/dist/xgmml.js +9 -0
  196. package/dist/xgmml.js.map +1 -0
  197. package/package.json +25 -2
  198. package/src/common/cell-budget.ts +170 -0
  199. package/src/common/input.ts +40 -0
  200. package/src/common/json-elements.ts +1147 -0
  201. package/src/common/options.ts +1 -1
  202. package/src/common/xml.ts +127 -6
  203. package/src/common/zip.ts +472 -0
  204. package/src/formats/cx/importer.ts +2733 -0
  205. package/src/formats/cx/index.ts +7 -0
  206. package/src/formats/cx2/exporter.ts +1036 -0
  207. package/src/formats/cx2/importer.ts +2187 -0
  208. package/src/formats/cx2/index.ts +7 -0
  209. package/src/formats/cys/constants.ts +99 -0
  210. package/src/formats/cys/importer.ts +1100 -0
  211. package/src/formats/cys/index.ts +7 -0
  212. package/src/formats/cys/session.ts +428 -0
  213. package/src/formats/cys/tables.ts +400 -0
  214. package/src/formats/dot/exporter.ts +17 -0
  215. package/src/formats/dot/importer.ts +6 -2
  216. package/src/formats/gexf/exporter.ts +11 -0
  217. package/src/formats/gexf/importer.ts +1 -2
  218. package/src/formats/gexf/index.ts +1 -1
  219. package/src/formats/gml/exporter.ts +8 -19
  220. package/src/formats/json/importer.ts +6 -105
  221. package/src/formats/xgmml/columns.ts +747 -0
  222. package/src/formats/xgmml/constants.ts +265 -0
  223. package/src/formats/xgmml/document.ts +975 -0
  224. package/src/formats/xgmml/emit.ts +1563 -0
  225. package/src/formats/xgmml/exporter.ts +1215 -0
  226. package/src/formats/xgmml/importer.ts +491 -0
  227. package/src/formats/xgmml/index.ts +8 -0
  228. package/src/formats/xgmml/values.ts +183 -0
  229. package/src/index.ts +19 -0
  230. package/src/registry.ts +46 -15
  231. package/src/sniff.ts +18 -1
  232. package/dist/chunks/escape-D-gZWO26.js.map +0 -1
  233. package/dist/chunks/importer-Br_QeAeE.js.map +0 -1
  234. package/dist/chunks/importer-Du5crN9l.js.map +0 -1
  235. package/dist/chunks/writer-GAdltGmC.js.map +0 -1
@@ -0,0 +1,1713 @@
1
+ import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
2
+ import { T as TOO_LARGE_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, D as DIRECTION_FORCED_CODE, a as DIRECTION_REFUSED_CODE, R as ROLE_TAKEN_CODE, C as COLUMN_RENAMED_CODE, Y as UNKNOWN_ELEMENT_CODE, I as ID_MERGED_CODE, P as PRECISION_CODE, b as ID_TEXT_TYPE_CODE, c as DUPLICATE_EDGE_ID_CODE, d as DUPLICATE_NODE_CODE, e as MISSING_ENDPOINT_CODE, f as MISSING_ID_CODE, i as DUPLICATE_ATTRIBUTE_CODE, j as DANGLING_REFERENCE_CODE, k as UNKNOWN_ATTR_TYPE_CODE, Z as BAD_DEFAULT_CODE, B as BAD_VALUE_CODE, l as COUNT_MISMATCH_CODE, m as ASPECT_ORDER_CODE, n as BAD_ASPECT_BLOCK_CODE, g as STYLES_NOT_IMPORTED_CODE, o as STATUS_WARNING_CODE, p as STATUS_FAILED_CODE, q as UNKNOWN_ENCODING_CODE, E as ENCODING_FALLBACK_CODE, r as INVALID_ENCODING_CODE, s as INVALID_UTF8_CODE, t as SYNTAX_CODE, u as EMPTY_INPUT_CODE, v as resolveImportOptions, w as ImportReportBuilder, F as reportSinkOptions, z as reportUnusedOptions, y as throwIfAborted, x as textChunks, J as DirectionResolver, K as IdCoercer, L as declareResolved, Q as uniqueColumnName } from "./report-B1z4WT9e.js";
3
+ import { s as scanAspects, r as reportTooDeep, C as CxStructure, i as isRecord, J as JsonScanError, p as plainJson, c as cxId, z as zDecl, a as positionDecl, f as flipY, d as declareFresh, E as ExactInteger, b as inexactLiteral } from "./json-elements-DDS8N2Dd.js";
4
+ import { w as weightFromValue } from "./weights-Dzba96G3.js";
5
+ const CX2_FORMAT = "cx2";
6
+ const ORIGINAL_ID_ATTRIBUTE = "graphty:originalId";
7
+ const BYPASS_NAMESPACE = "cx2.bypass";
8
+ const CX2_ISSUE = Object.freeze({
9
+ /** The input is empty (fatal). */
10
+ EMPTY_INPUT: EMPTY_INPUT_CODE,
11
+ /** The text is not JSON (fatal). */
12
+ SYNTAX: SYNTAX_CODE,
13
+ /** Invalid UTF-8 (fatal). */
14
+ INVALID_UTF8: INVALID_UTF8_CODE,
15
+ /** Invalid bytes in the encoding a BOM or the encoding option chose (fatal). */
16
+ INVALID_ENCODING: INVALID_ENCODING_CODE,
17
+ /** Bytes that are not UTF-8 were read as windows-1252. */
18
+ ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
19
+ /** An encoding the platform cannot decode was ignored. */
20
+ UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
21
+ /** The document is not an array or does not start with a descriptor holding CXVersion (fatal). */
22
+ NO_DESCRIPTOR: "E_CX2_NO_DESCRIPTOR",
23
+ /** CXVersion names a major version other than 2 (fatal); "1.x" says the file is CX1. */
24
+ VERSION: "E_CX2_VERSION",
25
+ /** CXVersion is a 2.x other than "2.0" (or not a string); the document is read. */
26
+ MINOR_VERSION: "W_CX2_MINOR_VERSION",
27
+ /** The document has no status block, or a malformed one. */
28
+ NO_STATUS: "E_CX2_NO_STATUS",
29
+ /** The producer marked the document as failed (fatal). */
30
+ STATUS_FAILED: STATUS_FAILED_CODE,
31
+ /** The producer marked the document as successful with an error text. */
32
+ STATUS_WARNING: STATUS_WARNING_CODE,
33
+ /** An aspect appears in several blocks although the descriptor does not declare fragments. */
34
+ UNDECLARED_FRAGMENTS: "W_CX2_UNDECLARED_FRAGMENTS",
35
+ /** An attribute declared twice with two different types; the first declaration wins. */
36
+ DECLARATION_CONFLICT: "E_CX2_DECLARATION_CONFLICT",
37
+ /** An attribute value whose name no declaration covers; its column is inferred. */
38
+ UNDECLARED_ATTRIBUTE: "W_CX2_UNDECLARED_ATTRIBUTE",
39
+ /** A node or edge attribute named `id`, which the specification reserves. */
40
+ RESERVED_KEY: "W_CX2_RESERVED_KEY",
41
+ /** An alias that is another attribute's name or another attribute's alias; the alias is ignored. */
42
+ ALIAS_CONFLICT: "E_CX2_ALIAS_CONFLICT",
43
+ /** A network attribute declaration with an alias or a default, which CX2 forbids; both ignored. */
44
+ NETWORK_DECLARATION: "W_CX2_NETWORK_DECLARATION",
45
+ /** An aspect that holds one element holds several; the first is read. */
46
+ EXTRA_ELEMENTS: "W_CX2_EXTRA_ELEMENTS",
47
+ /** Some nodes have coordinates and others none, or a node has x without y. */
48
+ PARTIAL_LAYOUT: "W_CX2_PARTIAL_LAYOUT",
49
+ /** A full attribute name used where its alias is declared; read as the same attribute. */
50
+ ALIAS_BYPASSED: "W_CX2_ALIAS_BYPASSED",
51
+ /** A CX1 cartesianLayout aspect next to node coordinates; kept, not applied. */
52
+ LEGACY_LAYOUT: "W_CX2_LEGACY_LAYOUT",
53
+ /** The file's style rules are not applied (issue #706). */
54
+ STYLES_NOT_IMPORTED: STYLES_NOT_IMPORTED_CODE,
55
+ /** A member of the top-level array that is not a one-key aspect block, or an element that is not an object. */
56
+ BAD_ASPECT_BLOCK: BAD_ASPECT_BLOCK_CODE,
57
+ /** An aspect out of its place (after the post-metadata or the status, a third metaData, late declarations). */
58
+ ASPECT_ORDER: ASPECT_ORDER_CODE,
59
+ /** A metaData element count disagrees with what was read. */
60
+ COUNT_MISMATCH: COUNT_MISMATCH_CODE,
61
+ /** A value that does not match its declared type; the cell is unset. */
62
+ BAD_VALUE: BAD_VALUE_CODE,
63
+ /** A declared default that does not match its type. */
64
+ BAD_DEFAULT: BAD_DEFAULT_CODE,
65
+ /** A declared type CX2 does not define; the column is inferred from the values. */
66
+ UNKNOWN_ATTR_TYPE: UNKNOWN_ATTR_TYPE_CODE,
67
+ /** A bypass or a layout entry naming no node or edge. */
68
+ DANGLING_REFERENCE: DANGLING_REFERENCE_CODE,
69
+ /** The same attribute twice on one element (its alias and its full name). */
70
+ DUPLICATE_ATTRIBUTE: DUPLICATE_ATTRIBUTE_CODE,
71
+ /** A node without an id. */
72
+ MISSING_ID: MISSING_ID_CODE,
73
+ /** An edge without s or t. */
74
+ MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
75
+ /** A node id declared twice; the second merges into the first. */
76
+ DUPLICATE_NODE: DUPLICATE_NODE_CODE,
77
+ /** An edge id declared twice; the second edge is skipped. */
78
+ DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,
79
+ /** An id that is not an integer. */
80
+ INVALID_ID: "E_INVALID_ID",
81
+ /** An edge endpoint naming no node (addMissingNodes false, the default). */
82
+ UNKNOWN_NODE: "E_UNKNOWN_NODE",
83
+ /** A weight that is not a number. */
84
+ INVALID_WEIGHT: "E_INVALID_WEIGHT",
85
+ /** An id spelled as a string or a non-integer literal; read as the integer. */
86
+ ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
87
+ /** An integer beyond 2^53: an id kept as its digits, a value stored as the nearest f64. */
88
+ PRECISION: PRECISION_CODE,
89
+ /** Two id texts merged under `ids: "number"`. */
90
+ ID_MERGED: ID_MERGED_CODE,
91
+ /** An element key CX2 does not define. */
92
+ UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
93
+ /** A column renamed because its name was taken. */
94
+ COLUMN_RENAMED: COLUMN_RENAMED_CODE,
95
+ /** A column that lost its role because another column holds it. */
96
+ ROLE_TAKEN: ROLE_TAKEN_CODE,
97
+ /** The sink refused the direction. */
98
+ DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
99
+ /** Edges forced to the policy's direction. */
100
+ DIRECTION_FORCED: DIRECTION_FORCED_CODE,
101
+ /** A common option CX2 has no use for. */
102
+ OPTION_IGNORED: OPTION_IGNORED_CODE,
103
+ /** A builder option the caller's sink does not honour. */
104
+ SINK_OPTION: SINK_OPTION_CODE,
105
+ /** The input is beyond a size limit (fatal). */
106
+ TOO_LARGE: TOO_LARGE_CODE
107
+ });
108
+ const USED_OPTIONS = /* @__PURE__ */ new Set([
109
+ "ids",
110
+ "addMissingNodes",
111
+ "duplicateEdges",
112
+ "selfLoops",
113
+ "onMixedDirection",
114
+ "weightFrom",
115
+ "weightDtype",
116
+ "long",
117
+ "restoreMangledIds",
118
+ "errorLimit",
119
+ "signal",
120
+ "onProgress"
121
+ ]);
122
+ const FORMAT_DEFAULTS = {
123
+ ids: "keep",
124
+ defaultDirected: true,
125
+ weightFrom: "weight",
126
+ addMissingNodes: false
127
+ };
128
+ const ABORT_CHECK_INTERVAL = 64;
129
+ const NODE_KEYS = /* @__PURE__ */ new Set(["id", "x", "y", "z", "v"]);
130
+ const EDGE_KEYS = /* @__PURE__ */ new Set(["id", "s", "t", "v"]);
131
+ const SINGLE_ELEMENT_ASPECTS = ["networkAttributes", "visualProperties", "visualEditorProperties"];
132
+ const STYLE_ASPECTS = [
133
+ "visualProperties",
134
+ "visualEditorProperties",
135
+ "cyVisualProperties",
136
+ "tableVisualProperties",
137
+ "cyTableVisualProperties"
138
+ ];
139
+ const STRUCTURE_ASPECTS = /* @__PURE__ */ new Set(["metaData", "status"]);
140
+ const SCALARS = {
141
+ string: "string",
142
+ long: "long",
143
+ integer: "integer",
144
+ double: "double",
145
+ boolean: "boolean",
146
+ str: "string",
147
+ int: "integer",
148
+ bool: "boolean",
149
+ float: "double"
150
+ };
151
+ function cx2Type(d) {
152
+ const list = d.startsWith("list_of_");
153
+ const scalar = SCALARS[list ? d.slice("list_of_".length) : d];
154
+ if (scalar === void 0) {
155
+ return null;
156
+ }
157
+ return { d: list ? `list_of_${scalar}` : scalar, scalar, list };
158
+ }
159
+ function scalarDtype(scalar, long) {
160
+ switch (scalar) {
161
+ case "string":
162
+ return "string";
163
+ case "long":
164
+ return long === "string" ? "string" : "f64";
165
+ case "integer":
166
+ return "i32";
167
+ case "double":
168
+ return "f64";
169
+ case "boolean":
170
+ return "bool";
171
+ default: {
172
+ const name = scalar;
173
+ return name;
174
+ }
175
+ }
176
+ }
177
+ const BAD = /* @__PURE__ */ Symbol("bad");
178
+ const I32_MIN = -2147483648;
179
+ const I32_MAX = 2147483647;
180
+ function convertScalar(value, scalar, long, onPrecision) {
181
+ switch (scalar) {
182
+ case "string":
183
+ return typeof value === "string" ? value : BAD;
184
+ case "boolean":
185
+ return typeof value === "boolean" ? value : BAD;
186
+ case "double":
187
+ if (value instanceof ExactInteger) {
188
+ return Number(value.digits);
189
+ }
190
+ return typeof value === "number" ? value : BAD;
191
+ case "integer":
192
+ return typeof value === "number" && Number.isInteger(value) && value >= I32_MIN && value <= I32_MAX ? value : BAD;
193
+ case "long":
194
+ if (value instanceof ExactInteger) {
195
+ if (long === "string") {
196
+ return value.digits;
197
+ }
198
+ onPrecision(value.digits);
199
+ return Number(value.digits);
200
+ }
201
+ if (typeof value === "number" && Number.isInteger(value)) {
202
+ return long === "string" ? String(value) : value;
203
+ }
204
+ return BAD;
205
+ default:
206
+ return BAD;
207
+ }
208
+ }
209
+ function convertValue(value, type, long, onPrecision) {
210
+ if (!type.list) {
211
+ return convertScalar(value, type.scalar, long, onPrecision);
212
+ }
213
+ if (!Array.isArray(value)) {
214
+ return BAD;
215
+ }
216
+ const out = [];
217
+ for (const item of value) {
218
+ const converted = item === null ? BAD : convertScalar(item, type.scalar, long, onPrecision);
219
+ if (converted === BAD) {
220
+ return BAD;
221
+ }
222
+ out.push(converted);
223
+ }
224
+ return out;
225
+ }
226
+ function shown(value) {
227
+ if (value instanceof ExactInteger) {
228
+ return value.digits;
229
+ }
230
+ let text;
231
+ try {
232
+ text = JSON.stringify(value) ?? String(value);
233
+ } catch {
234
+ text = String(value);
235
+ }
236
+ return text.length > 60 ? `${text.slice(0, 57)}...` : text;
237
+ }
238
+ function inexactBits(text, keys) {
239
+ if (!/[0-9][.eE]/.test(text)) {
240
+ return 0;
241
+ }
242
+ let bits = 0;
243
+ keys.forEach((key, i) => {
244
+ if (inexactLiteral(text, key)) {
245
+ bits |= 1 << i;
246
+ }
247
+ });
248
+ return bits;
249
+ }
250
+ async function readDocument(input, report, options) {
251
+ const structure = new CxStructure(report);
252
+ const doc = {
253
+ version: "",
254
+ hasFragments: false,
255
+ nodes: [],
256
+ edges: [],
257
+ declarations: [],
258
+ networkAttributes: [],
259
+ nodeBypasses: [],
260
+ edgeBypasses: [],
261
+ cartesianLayout: [],
262
+ opaque: /* @__PURE__ */ new Map(),
263
+ structure
264
+ };
265
+ let descriptor = false;
266
+ let elementsSeen = false;
267
+ let sinceCheck = 0;
268
+ const collect = (aspect, value, text, line, exact) => {
269
+ structure.element(aspect, value, line);
270
+ switch (aspect) {
271
+ case "nodes":
272
+ elementsSeen = true;
273
+ doc.nodes.push({ value, line, inexact: inexactBits(text, ["id"]) });
274
+ return;
275
+ case "edges":
276
+ elementsSeen = true;
277
+ doc.edges.push({ value, line, inexact: inexactBits(text, ["id", "s", "t"]) });
278
+ return;
279
+ case "attributeDeclarations":
280
+ if (elementsSeen) {
281
+ report.warnOnce(
282
+ "validation-error",
283
+ ASPECT_ORDER_CODE,
284
+ "attributeDeclarations comes after the elements it types; the declarations are applied to them",
285
+ { line, element: aspect },
286
+ `${ASPECT_ORDER_CODE}:declarations`
287
+ );
288
+ }
289
+ doc.declarations.push({ value: plainJson(value), line, inexact: 0 });
290
+ return;
291
+ case "networkAttributes":
292
+ elementsSeen = true;
293
+ doc.networkAttributes.push({ value, line, inexact: 0 });
294
+ return;
295
+ case "nodeBypasses":
296
+ doc.nodeBypasses.push({ value, line, inexact: 0 });
297
+ return;
298
+ case "edgeBypasses":
299
+ doc.edgeBypasses.push({ value, line, inexact: 0 });
300
+ return;
301
+ case "cartesianLayout":
302
+ doc.cartesianLayout.push({ value, line, inexact: 0 });
303
+ return;
304
+ default:
305
+ if (STRUCTURE_ASPECTS.has(aspect)) {
306
+ return;
307
+ }
308
+ if (!doc.opaque.has(aspect)) {
309
+ doc.opaque.set(aspect, []);
310
+ }
311
+ doc.opaque.get(aspect)?.push(exact ? plainJson(value, () => void 0) : value);
312
+ }
313
+ };
314
+ try {
315
+ for await (const event of scanAspects(textChunks(input, report, options))) {
316
+ if (++sinceCheck >= ABORT_CHECK_INTERVAL) {
317
+ sinceCheck = 0;
318
+ throwIfAborted(options.signal);
319
+ }
320
+ switch (event.kind) {
321
+ case "root":
322
+ report.fail(
323
+ CX2_ISSUE.NO_DESCRIPTOR,
324
+ `a CX2 document is a JSON array; found ${isRecord(event.value) ? "an object" : shown(event.value)}`,
325
+ { line: event.line }
326
+ );
327
+ break;
328
+ case "member":
329
+ if (!descriptor) {
330
+ readDescriptor(event.value, event.line, doc, report);
331
+ descriptor = true;
332
+ break;
333
+ }
334
+ memberAspect(event.value, event.line, structure, report, (aspect, value) => {
335
+ collect(aspect, value, JSON.stringify(value) ?? "", event.line, event.exact);
336
+ });
337
+ break;
338
+ case "block":
339
+ if (!descriptor) {
340
+ report.fail(
341
+ CX2_ISSUE.NO_DESCRIPTOR,
342
+ `the document starts with a "${event.aspect}" block, not a descriptor with CXVersion${event.aspect === "numberVerification" || event.aspect === "metaData" ? "; this looks like CX version 1 (read it with the cx importer)" : ""}`,
343
+ { line: event.line }
344
+ );
345
+ }
346
+ structure.block(event.aspect, event.line);
347
+ break;
348
+ case "element":
349
+ collect(event.aspect, event.value, event.text, event.line, event.exact);
350
+ break;
351
+ case "deep":
352
+ reportTooDeep(report, event);
353
+ if (!descriptor) {
354
+ readDescriptor(null, event.line, doc, report);
355
+ }
356
+ break;
357
+ case "extraKeys":
358
+ report.error(
359
+ "parse-error",
360
+ BAD_ASPECT_BLOCK_CODE,
361
+ `the "${event.aspect}" block holds more keys (${event.keys.join(", ")}); an aspect block has one key, the others are skipped`,
362
+ { line: event.line, element: event.aspect }
363
+ );
364
+ break;
365
+ default:
366
+ break;
367
+ }
368
+ }
369
+ } catch (err) {
370
+ if (err instanceof JsonScanError) {
371
+ report.fail(err.empty ? EMPTY_INPUT_CODE : SYNTAX_CODE, err.message, { line: err.line });
372
+ }
373
+ throw err;
374
+ }
375
+ if (!descriptor) {
376
+ report.fail(CX2_ISSUE.NO_DESCRIPTOR, "the document is an empty array; a CX2 document starts with a descriptor");
377
+ }
378
+ return doc;
379
+ }
380
+ function readDescriptor(value, line, doc, report) {
381
+ if (!isRecord(value) || !("CXVersion" in value)) {
382
+ const keys = isRecord(value) ? Object.keys(value) : [];
383
+ report.fail(
384
+ CX2_ISSUE.NO_DESCRIPTOR,
385
+ `the first member of the array is not a descriptor with CXVersion${keys.length > 0 ? ` (keys: ${keys.join(", ")})` : ""}`,
386
+ { line }
387
+ );
388
+ }
389
+ const raw = value.CXVersion;
390
+ const text = typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
391
+ const match = /^([0-9]+)(?:\.([0-9]+))?$/.exec(text);
392
+ const major = match === null ? NaN : Number(match[1]);
393
+ if (major === 1) {
394
+ report.fail(
395
+ CX2_ISSUE.VERSION,
396
+ `CXVersion ${shown(raw)} is CX version 1, a different document shape; read it with the cx importer`,
397
+ { line, element: "CXVersion" }
398
+ );
399
+ }
400
+ if (major !== 2) {
401
+ report.fail(CX2_ISSUE.VERSION, `CXVersion ${shown(raw)} is not a CX2 version`, { line, element: "CXVersion" });
402
+ }
403
+ doc.version = text;
404
+ if (raw !== "2.0") {
405
+ report.warning(
406
+ "validation-error",
407
+ CX2_ISSUE.MINOR_VERSION,
408
+ `CXVersion ${shown(raw)} is not "2.0"; the document is read as CX2 2.0`,
409
+ { line, element: "CXVersion" }
410
+ );
411
+ }
412
+ for (const key of Object.keys(value)) {
413
+ if (key === "hasFragments") {
414
+ if (typeof value.hasFragments === "boolean") {
415
+ doc.hasFragments = value.hasFragments;
416
+ } else {
417
+ report.error(
418
+ "validation-error",
419
+ BAD_VALUE_CODE,
420
+ `hasFragments is ${shown(value.hasFragments)}, not a boolean; read as false`,
421
+ { line, element: "hasFragments" }
422
+ );
423
+ }
424
+ } else if (key !== "CXVersion") {
425
+ report.warning("validation-error", UNKNOWN_ELEMENT_CODE, `the descriptor key "${key}" is not defined`, {
426
+ line,
427
+ element: key
428
+ });
429
+ }
430
+ }
431
+ }
432
+ function memberAspect(value, line, structure, report, collect) {
433
+ const keys = isRecord(value) ? Object.keys(value) : [];
434
+ if (isRecord(value) && keys.length === 1 && isRecord(value[keys[0]])) {
435
+ structure.block(keys[0], line);
436
+ collect(keys[0], value[keys[0]]);
437
+ return;
438
+ }
439
+ report.error(
440
+ "parse-error",
441
+ BAD_ASPECT_BLOCK_CODE,
442
+ `a member of the document is ${keys.length > 1 ? `an object with ${keys.length} keys (${keys.join(", ")})` : shown(value)}, not a one-key aspect block; skipped`,
443
+ { line, element: keys[0] ?? null }
444
+ );
445
+ }
446
+ function scalarKind(value) {
447
+ switch (typeof value) {
448
+ case "boolean":
449
+ return "boolean";
450
+ case "string":
451
+ return "string";
452
+ case "number":
453
+ return Number.isInteger(value) && value >= I32_MIN && value <= I32_MAX ? "integer" : "double";
454
+ default:
455
+ return value instanceof ExactInteger ? "double" : null;
456
+ }
457
+ }
458
+ function widenKind(a, b) {
459
+ if (a === void 0 || a === b) {
460
+ return b;
461
+ }
462
+ if (a === null || b === null) {
463
+ return null;
464
+ }
465
+ return a === "integer" && b === "double" || a === "double" && b === "integer" ? "double" : null;
466
+ }
467
+ function observe(seen, value) {
468
+ const list = Array.isArray(value);
469
+ if (seen.list !== void 0 && seen.list !== list) {
470
+ seen.kind = null;
471
+ return;
472
+ }
473
+ seen.list = list;
474
+ if (!list) {
475
+ seen.kind = widenKind(seen.kind, scalarKind(value));
476
+ return;
477
+ }
478
+ for (const item of value) {
479
+ seen.kind = widenKind(seen.kind, item === null || Array.isArray(item) ? null : scalarKind(item));
480
+ }
481
+ }
482
+ function inferredType(seen) {
483
+ if (seen.kind === null) {
484
+ return null;
485
+ }
486
+ const scalar = seen.kind ?? "string";
487
+ return cx2Type(seen.list === true ? `list_of_${scalar}` : scalar);
488
+ }
489
+ class DeclarationTable {
490
+ constructor() {
491
+ this.byName = /* @__PURE__ */ new Map();
492
+ this.byAlias = /* @__PURE__ */ new Map();
493
+ }
494
+ /**
495
+ * The declaration a key of an element's `v` names.
496
+ * @param key - the key
497
+ * @returns the declaration and whether the key was its alias, or null
498
+ */
499
+ resolve(key) {
500
+ const viaAlias = this.byAlias.get(key);
501
+ if (viaAlias !== void 0) {
502
+ return { decl: viaAlias, viaAlias: true };
503
+ }
504
+ const decl = this.byName.get(key);
505
+ return decl === void 0 ? null : { decl, viaAlias: false };
506
+ }
507
+ }
508
+ const DECLARATION_KEYS = { nodes: "node", edges: "edge", networkAttributes: "network" };
509
+ class Cx2Reader {
510
+ /**
511
+ * Create the reader.
512
+ * @param sink - the sink
513
+ * @param report - the report
514
+ * @param options - the resolved options
515
+ * @param zAs - where z goes
516
+ * @param doc - the collected document
517
+ */
518
+ constructor(sink, report, options, zAs, doc) {
519
+ this.decls = {
520
+ node: new DeclarationTable(),
521
+ edge: new DeclarationTable(),
522
+ network: new DeclarationTable(),
523
+ other: {}
524
+ };
525
+ this.nodeRows = /* @__PURE__ */ new Map();
526
+ this.sinkIds = /* @__PURE__ */ new Map();
527
+ this.edgeRows = /* @__PURE__ */ new Map();
528
+ this.positionHandle = INVALID_INDEX;
529
+ this.zHandle = INVALID_INDEX;
530
+ this.edgeIdHandle = INVALID_INDEX;
531
+ this.positioned = /* @__PURE__ */ new Set();
532
+ this.sinceCheck = 0;
533
+ this.idType = "integer";
534
+ this.weighted = false;
535
+ this.dangling = /* @__PURE__ */ new Map();
536
+ this.point = [0, 0, 0];
537
+ this.sink = sink;
538
+ this.report = report;
539
+ this.options = options;
540
+ this.zAs = zAs;
541
+ this.doc = doc;
542
+ this.direction = new DirectionResolver(sink, report, options.onMixedDirection);
543
+ this.coercer = new IdCoercer(options.ids);
544
+ }
545
+ /** Build the graph. */
546
+ build() {
547
+ const { doc, report } = this;
548
+ this.direction.setHeader(true);
549
+ this.readDeclarations();
550
+ this.declareColumns();
551
+ this.sink.reserve(doc.nodes.length, doc.edges.length);
552
+ for (const held of doc.nodes) {
553
+ this.readNode(held);
554
+ }
555
+ this.readLegacyLayout();
556
+ if (this.positioned.size > 0 && this.positioned.size < this.nodeRows.size) {
557
+ report.warnOnce(
558
+ "missing-value",
559
+ CX2_ISSUE.PARTIAL_LAYOUT,
560
+ `${this.nodeRows.size - this.positioned.size} of ${this.nodeRows.size} node(s) have no coordinates; their positions are unset`,
561
+ { element: "nodes" }
562
+ );
563
+ }
564
+ for (const held of doc.edges) {
565
+ this.readEdge(held);
566
+ }
567
+ this.readNetworkAttributes();
568
+ this.readBypasses("node", doc.nodeBypasses);
569
+ this.readBypasses("edge", doc.edgeBypasses);
570
+ for (const [kind, count] of this.dangling) {
571
+ report.warning(
572
+ "validation-error",
573
+ DANGLING_REFERENCE_CODE,
574
+ `${count} ${kind} name(s) no element; ignored`,
575
+ { element: kind }
576
+ );
577
+ }
578
+ this.checkAspects();
579
+ this.setMeta();
580
+ }
581
+ // ------------------------------------------------------------ declarations
582
+ /** Merge every attributeDeclarations element (fragments in order) into the tables. */
583
+ readDeclarations() {
584
+ const { report } = this;
585
+ for (const held of this.doc.declarations) {
586
+ if (!isRecord(held.value)) {
587
+ report.error(
588
+ "parse-error",
589
+ BAD_ASPECT_BLOCK_CODE,
590
+ "an attributeDeclarations element is not an object",
591
+ {
592
+ line: held.line,
593
+ element: "attributeDeclarations"
594
+ }
595
+ );
596
+ continue;
597
+ }
598
+ for (const [key, table] of Object.entries(held.value)) {
599
+ const domain = DECLARATION_KEYS[key];
600
+ if (domain === void 0) {
601
+ this.decls.other[key] = table;
602
+ continue;
603
+ }
604
+ if (!isRecord(table)) {
605
+ report.error(
606
+ "validation-error",
607
+ BAD_VALUE_CODE,
608
+ `the ${key} declarations are not an object; ignored`,
609
+ { line: held.line, element: key }
610
+ );
611
+ continue;
612
+ }
613
+ for (const [name, decl] of Object.entries(table)) {
614
+ this.declare(domain, name, decl, held.line);
615
+ }
616
+ }
617
+ }
618
+ for (const domain of ["node", "edge"]) {
619
+ this.checkAliases(domain);
620
+ }
621
+ this.inferUndeclared("node", this.doc.nodes);
622
+ this.inferUndeclared("edge", this.doc.edges);
623
+ }
624
+ /**
625
+ * Type the attributes no declaration types (undeclared, or declared with a type CX2 does not
626
+ * define) from their values, so every attribute column is declared with its CX2 type.
627
+ * @param domain - node or edge
628
+ * @param elements - the elements
629
+ */
630
+ inferUndeclared(domain, elements) {
631
+ const table = this.decls[domain];
632
+ const seen = /* @__PURE__ */ new Map();
633
+ for (const { value } of elements) {
634
+ if (!isRecord(value) || !isRecord(value.v)) {
635
+ continue;
636
+ }
637
+ for (const [key, raw] of Object.entries(value.v)) {
638
+ const resolved = table.resolve(key);
639
+ if (resolved !== null && resolved.decl.type !== null || raw === null || raw === void 0) {
640
+ continue;
641
+ }
642
+ const name = resolved?.decl.name ?? key;
643
+ let state = seen.get(name);
644
+ if (state === void 0) {
645
+ state = { kind: void 0, list: void 0 };
646
+ seen.set(name, state);
647
+ }
648
+ observe(state, raw);
649
+ }
650
+ }
651
+ for (const [name, state] of seen) {
652
+ const type = inferredType(state);
653
+ const decl = table.byName.get(name);
654
+ if (decl !== void 0) {
655
+ decl.type = type;
656
+ if (decl.hasDefault && type !== null) {
657
+ decl.defaultValue = this.convertDefault(name, decl.rawDefault, type, { element: name });
658
+ decl.hasDefault = decl.defaultValue !== BAD;
659
+ }
660
+ continue;
661
+ }
662
+ table.byName.set(name, {
663
+ name,
664
+ type,
665
+ alias: null,
666
+ hasDefault: false,
667
+ defaultValue: void 0,
668
+ rawDefault: void 0,
669
+ handle: INVALID_INDEX,
670
+ undeclared: true
671
+ });
672
+ }
673
+ }
674
+ /**
675
+ * Add one declaration to a table.
676
+ * @param domain - the table
677
+ * @param name - the attribute name
678
+ * @param raw - the declaration object
679
+ * @param line - its line
680
+ */
681
+ declare(domain, name, raw, line) {
682
+ const { report } = this;
683
+ const table = this.decls[domain];
684
+ const where = { line, element: name };
685
+ if (!isRecord(raw)) {
686
+ report.error(
687
+ "validation-error",
688
+ BAD_VALUE_CODE,
689
+ `the declaration of "${name}" is not an object; ignored`,
690
+ where
691
+ );
692
+ return;
693
+ }
694
+ let type;
695
+ if (raw.d === void 0) {
696
+ type = cx2Type("string");
697
+ report.warning(
698
+ "unsupported",
699
+ UNKNOWN_ATTR_TYPE_CODE,
700
+ `the declaration of "${name}" has no type; read as string (the Java reader's rule)`,
701
+ where
702
+ );
703
+ } else {
704
+ type = typeof raw.d === "string" ? cx2Type(raw.d) : null;
705
+ if (type === null) {
706
+ report.warning(
707
+ "unsupported",
708
+ UNKNOWN_ATTR_TYPE_CODE,
709
+ `the type ${shown(raw.d)} of "${name}" is not a CX2 type; the column is inferred from its values`,
710
+ where
711
+ );
712
+ }
713
+ }
714
+ const existing = table.byName.get(name);
715
+ if (existing !== void 0) {
716
+ if (existing.type?.d !== type?.d) {
717
+ report.error(
718
+ "validation-error",
719
+ CX2_ISSUE.DECLARATION_CONFLICT,
720
+ `"${name}" is declared ${existing.type?.d ?? "untyped"} and again ${type?.d ?? "untyped"}; the first declaration wins`,
721
+ where
722
+ );
723
+ }
724
+ return;
725
+ }
726
+ let alias = typeof raw.a === "string" ? raw.a : null;
727
+ let hasDefault = raw.v !== void 0 && raw.v !== null;
728
+ if (domain === "network" && (raw.a !== void 0 || raw.v !== void 0)) {
729
+ report.warning(
730
+ "validation-error",
731
+ CX2_ISSUE.NETWORK_DECLARATION,
732
+ `the network attribute "${name}" declares an alias or a default, which CX2 forbids; ignored`,
733
+ where
734
+ );
735
+ alias = null;
736
+ hasDefault = false;
737
+ }
738
+ let defaultValue = void 0;
739
+ if (hasDefault && type !== null) {
740
+ defaultValue = this.convertDefault(name, raw.v, type, where);
741
+ hasDefault = defaultValue !== BAD;
742
+ }
743
+ table.byName.set(name, {
744
+ name,
745
+ type,
746
+ alias,
747
+ hasDefault,
748
+ defaultValue,
749
+ rawDefault: hasDefault && type === null ? plainJson(raw.v) : void 0,
750
+ handle: INVALID_INDEX,
751
+ undeclared: false
752
+ });
753
+ }
754
+ /**
755
+ * Convert a declared default; a numeric string for a numeric type (the specification's own
756
+ * `"v": "0.0"`) is coerced, anything else that does not match drops the default.
757
+ * @param name - the attribute
758
+ * @param value - the default
759
+ * @param type - its type
760
+ * @param where - the location
761
+ * @param where.line - the line
762
+ * @param where.element - the attribute
763
+ * @returns the default, or BAD when dropped
764
+ */
765
+ convertDefault(name, value, type, where) {
766
+ const converted = convertValue(value, type, this.options.long, () => void 0);
767
+ if (converted !== BAD) {
768
+ return converted;
769
+ }
770
+ const numeric = type.scalar === "double" || type.scalar === "integer" || type.scalar === "long";
771
+ if (!type.list && numeric && typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) {
772
+ const coerced = convertValue(Number(value), type, this.options.long, () => void 0);
773
+ if (coerced !== BAD) {
774
+ this.report.warning(
775
+ "validation-error",
776
+ BAD_DEFAULT_CODE,
777
+ `the default ${shown(value)} of "${name}" is a string, not a ${type.d}; read as ${shown(coerced)}`,
778
+ where
779
+ );
780
+ return coerced;
781
+ }
782
+ }
783
+ this.report.warning(
784
+ "validation-error",
785
+ BAD_DEFAULT_CODE,
786
+ `the default ${shown(value)} of "${name}" is not a ${type.d}; the column has no default`,
787
+ where
788
+ );
789
+ return BAD;
790
+ }
791
+ /**
792
+ * Check the aliases of a table in declaration order: an alias that is another attribute's
793
+ * name or another attribute's alias is E_CX2_ALIAS_CONFLICT and ignored.
794
+ * @param domain - the table
795
+ */
796
+ checkAliases(domain) {
797
+ const table = this.decls[domain];
798
+ for (const decl of table.byName.values()) {
799
+ const { alias } = decl;
800
+ if (alias === null || alias === decl.name) {
801
+ if (alias !== null) {
802
+ table.byAlias.set(alias, decl);
803
+ }
804
+ continue;
805
+ }
806
+ const other = table.byName.get(alias) ?? table.byAlias.get(alias);
807
+ if (other !== void 0) {
808
+ this.report.error(
809
+ "validation-error",
810
+ CX2_ISSUE.ALIAS_CONFLICT,
811
+ `the alias "${alias}" of the ${domain} attribute "${decl.name}" is also ${other.name === alias ? "an attribute's name" : `the alias of "${other.name}"`}; the alias is ignored`,
812
+ { element: decl.name }
813
+ );
814
+ decl.alias = null;
815
+ continue;
816
+ }
817
+ table.byAlias.set(alias, decl);
818
+ }
819
+ }
820
+ /** Declare the declared node and edge columns. */
821
+ declareColumns() {
822
+ const restore = this.options.restoreMangledIds;
823
+ for (const domain of ["node", "edge"]) {
824
+ for (const decl of this.decls[domain].byName.values()) {
825
+ if (domain === "node" && restore && decl.name === ORIGINAL_ID_ATTRIBUTE) {
826
+ continue;
827
+ }
828
+ if (domain === "edge" && decl.name === this.options.weightFrom) {
829
+ continue;
830
+ }
831
+ const column = decl.type === null ? {
832
+ name: decl.name,
833
+ dtype: "json",
834
+ nullable: true,
835
+ origin: { format: CX2_FORMAT, id: decl.alias }
836
+ } : {
837
+ ...this.columnDecl(decl.type),
838
+ name: decl.name,
839
+ origin: { format: CX2_FORMAT, id: decl.alias, type: decl.type.d }
840
+ };
841
+ if (decl.hasDefault) {
842
+ column.default = decl.type === null ? decl.rawDefault : decl.defaultValue;
843
+ }
844
+ if (domain === "node" && decl.name === "name") {
845
+ column.role = "label";
846
+ }
847
+ decl.handle = declareResolved(this.sink, domain, column, this.report).handle;
848
+ }
849
+ }
850
+ }
851
+ /**
852
+ * The edge id column, declared with the first edge.
853
+ * @returns the handle
854
+ */
855
+ edgeIdColumn() {
856
+ if (this.edgeIdHandle === INVALID_INDEX) {
857
+ const taken = (name) => this.sink.edgeColumn(name) !== INVALID_INDEX;
858
+ this.edgeIdHandle = declareResolved(
859
+ this.sink,
860
+ "edge",
861
+ { name: uniqueColumnName("id", "cx2", taken), dtype: "f64", role: "id", nullable: true },
862
+ this.report
863
+ ).handle;
864
+ }
865
+ return this.edgeIdHandle;
866
+ }
867
+ /**
868
+ * The dtype part of a declared column.
869
+ * @param type - the CX2 type
870
+ * @returns the dtype and item dtype
871
+ */
872
+ columnDecl(type) {
873
+ const scalar = scalarDtype(type.scalar, this.options.long);
874
+ return type.list ? { name: "", dtype: "list", itemDtype: scalar, nullable: true } : { name: "", dtype: scalar, nullable: true };
875
+ }
876
+ // ------------------------------------------------------------ ids
877
+ /**
878
+ * The id of an element, by the CX id rule and the `ids` option.
879
+ * @param raw - the parsed id
880
+ * @param inexact - whether the literal was not a plain integer
881
+ * @param element - the element name for issues
882
+ * @param edgeId - whether this is an edge's own id (stored in the f64 id column, not kept as digits)
883
+ * @returns the id, or null when it was reported
884
+ */
885
+ idOf(raw, inexact, element, edgeId = false) {
886
+ let id;
887
+ try {
888
+ const parsed = cxId(raw, inexact);
889
+ if (parsed.note === "text") {
890
+ this.report.warnOnce(
891
+ "coercion",
892
+ ID_TEXT_TYPE_CODE,
893
+ `${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`,
894
+ { element }
895
+ );
896
+ } else if (parsed.note === "precision" && edgeId) {
897
+ this.report.warnOnce(
898
+ "precision",
899
+ PRECISION_CODE,
900
+ `${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`,
901
+ { element },
902
+ `${PRECISION_CODE}:edge`
903
+ );
904
+ } else if (parsed.note === "precision") {
905
+ this.idType = "mixed";
906
+ this.report.warnOnce(
907
+ "precision",
908
+ PRECISION_CODE,
909
+ `${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`,
910
+ { element }
911
+ );
912
+ }
913
+ id = this.options.ids === "keep" ? parsed.id : this.coercer.value(parsed.id);
914
+ } catch (err) {
915
+ this.report.recordError(err, { element });
916
+ return null;
917
+ }
918
+ return id;
919
+ }
920
+ /**
921
+ * Check the cancellation signal every ABORT_CHECK_INTERVAL elements.
922
+ */
923
+ checkAbort() {
924
+ if (++this.sinceCheck >= ABORT_CHECK_INTERVAL) {
925
+ this.sinceCheck = 0;
926
+ throwIfAborted(this.options.signal);
927
+ }
928
+ }
929
+ /**
930
+ * Report the keys of an element CX2 does not define, once per key.
931
+ * @param record - the element
932
+ * @param known - the keys it defines
933
+ * @param what - nodes or edges
934
+ * @param line - its line
935
+ */
936
+ unknownKeys(record, known, what, line) {
937
+ for (const key of Object.keys(record)) {
938
+ if (!known.has(key)) {
939
+ this.report.warnOnce(
940
+ "unsupported",
941
+ UNKNOWN_ELEMENT_CODE,
942
+ `the ${what} key "${key}" is not defined by CX2; its values are not read`,
943
+ { line, element: key },
944
+ `${UNKNOWN_ELEMENT_CODE}:${what}:${key}`
945
+ );
946
+ }
947
+ }
948
+ }
949
+ // ------------------------------------------------------------ nodes
950
+ /**
951
+ * Read one node element.
952
+ * @param held - the element
953
+ */
954
+ readNode(held) {
955
+ this.checkAbort();
956
+ const { report, sink } = this;
957
+ const { value, line } = held;
958
+ if (!isRecord(value)) {
959
+ report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `a nodes element is ${shown(value)}, not an object`, {
960
+ line,
961
+ element: "nodes"
962
+ });
963
+ report.counts.skippedNodes++;
964
+ return;
965
+ }
966
+ this.unknownKeys(value, NODE_KEYS, "node", line);
967
+ if (value.id === void 0 || value.id === null) {
968
+ report.error("missing-value", MISSING_ID_CODE, "a node has no id", { line, element: "nodes" });
969
+ report.counts.skippedNodes++;
970
+ return;
971
+ }
972
+ const element = `node ${shown(value.id)}`;
973
+ const cx = this.idOf(value.id, (held.inexact & 1) !== 0, element);
974
+ if (cx === null) {
975
+ report.counts.skippedNodes++;
976
+ return;
977
+ }
978
+ const attrs = isRecord(value.v) ? value.v : null;
979
+ if (value.v !== void 0 && value.v !== null && attrs === null) {
980
+ report.error("validation-error", BAD_VALUE_CODE, `${element}: v is ${shown(value.v)}, not an object`, {
981
+ line,
982
+ element
983
+ });
984
+ }
985
+ let row = this.nodeRows.get(cx);
986
+ if (row !== void 0) {
987
+ report.warning(
988
+ "merged",
989
+ DUPLICATE_NODE_CODE,
990
+ `${element} is declared more than once; its attributes are merged (the later values win)`,
991
+ { line, element }
992
+ );
993
+ } else {
994
+ const original = this.originalIdOf(attrs);
995
+ this.idTypeOf(original ?? cx);
996
+ try {
997
+ row = sink.addNode(original ?? cx);
998
+ } catch (err) {
999
+ report.recordError(err, { line, element });
1000
+ report.counts.skippedNodes++;
1001
+ return;
1002
+ }
1003
+ this.nodeRows.set(cx, row);
1004
+ this.sinkIds.set(cx, original ?? cx);
1005
+ report.counts.nodes++;
1006
+ }
1007
+ this.readCoordinates(value, row, element, line);
1008
+ if (attrs !== null) {
1009
+ this.writeAttributes("node", row, attrs, element, line);
1010
+ }
1011
+ }
1012
+ /**
1013
+ * The original id the exporter's `sanitizeIds: "mangle"` kept, when it is to be restored.
1014
+ * @param attrs - the node's v
1015
+ * @returns the original id, or null
1016
+ */
1017
+ originalIdOf(attrs) {
1018
+ if (attrs === null || !this.options.restoreMangledIds) {
1019
+ return null;
1020
+ }
1021
+ const alias = this.decls.node.byName.get(ORIGINAL_ID_ATTRIBUTE)?.alias ?? null;
1022
+ const raw = attrs[ORIGINAL_ID_ATTRIBUTE] ?? (alias === null ? void 0 : attrs[alias]);
1023
+ return typeof raw === "string" ? raw : null;
1024
+ }
1025
+ /**
1026
+ * Track the idType of the metadata.
1027
+ * @param id - a node id
1028
+ */
1029
+ idTypeOf(id) {
1030
+ if (typeof id === "string" && this.idType === "integer") {
1031
+ this.idType = "mixed";
1032
+ }
1033
+ }
1034
+ /**
1035
+ * Read a node's x / y / z.
1036
+ * @param value - the node element
1037
+ * @param row - the node
1038
+ * @param element - the element name
1039
+ * @param line - its line
1040
+ */
1041
+ readCoordinates(value, row, element, line) {
1042
+ const coordinate = (key) => {
1043
+ const raw = value[key];
1044
+ if (raw === void 0 || raw === null) {
1045
+ return null;
1046
+ }
1047
+ if (typeof raw === "number") {
1048
+ return raw;
1049
+ }
1050
+ this.report.error("validation-error", BAD_VALUE_CODE, `${element}: ${key} is ${shown(raw)}, not a number`, {
1051
+ line,
1052
+ element
1053
+ });
1054
+ return null;
1055
+ };
1056
+ const x = coordinate("x");
1057
+ const y = coordinate("y");
1058
+ const z = coordinate("z");
1059
+ if (x === null !== (y === null)) {
1060
+ this.report.warnOnce(
1061
+ "missing-value",
1062
+ CX2_ISSUE.PARTIAL_LAYOUT,
1063
+ `${element} has ${x === null ? "y without x" : "x without y"}; its coordinates are dropped`,
1064
+ { line, element },
1065
+ `${CX2_ISSUE.PARTIAL_LAYOUT}:half`
1066
+ );
1067
+ }
1068
+ if (z !== null && x === null && y === null) {
1069
+ this.report.warnOnce(
1070
+ "missing-value",
1071
+ CX2_ISSUE.PARTIAL_LAYOUT,
1072
+ `${element} has z without x and y; ${this.zAs === "column" ? "z is kept in the z column" : "with no position, z is dropped"}`,
1073
+ { line, element },
1074
+ `${CX2_ISSUE.PARTIAL_LAYOUT}:z`
1075
+ );
1076
+ }
1077
+ if (x !== null && y !== null) {
1078
+ this.setPosition(row, x, y, z);
1079
+ }
1080
+ if (z !== null && this.zAs === "column") {
1081
+ if (this.zHandle === INVALID_INDEX) {
1082
+ this.zHandle = declareResolved(this.sink, "node", zDecl(CX2_FORMAT), this.report).handle;
1083
+ }
1084
+ this.sink.setNodeValue(this.zHandle, row, z);
1085
+ }
1086
+ }
1087
+ /**
1088
+ * Write a node's position, y flipped to y-up.
1089
+ * @param row - the node
1090
+ * @param x - screen x
1091
+ * @param y - screen y
1092
+ * @param z - z, used only under zAs "position"
1093
+ */
1094
+ setPosition(row, x, y, z) {
1095
+ if (this.positionHandle === INVALID_INDEX) {
1096
+ this.positionHandle = declareResolved(
1097
+ this.sink,
1098
+ "node",
1099
+ positionDecl(CX2_FORMAT, this.zAs === "position" ? 3 : 2),
1100
+ this.report
1101
+ ).handle;
1102
+ }
1103
+ this.point[0] = x;
1104
+ this.point[1] = flipY(y);
1105
+ this.point[2] = this.zAs === "position" && z !== null ? z : 0;
1106
+ this.sink.setNodeValue(this.positionHandle, row, this.point);
1107
+ this.positioned.add(row);
1108
+ }
1109
+ /** Apply a CX1 cartesianLayout aspect when no node carries coordinates; keep and warn otherwise. */
1110
+ readLegacyLayout() {
1111
+ const { cartesianLayout } = this.doc;
1112
+ if (cartesianLayout.length === 0) {
1113
+ return;
1114
+ }
1115
+ if (this.positioned.size > 0) {
1116
+ this.doc.opaque.set(
1117
+ "cartesianLayout",
1118
+ cartesianLayout.map((h) => plainJson(h.value))
1119
+ );
1120
+ this.report.warning(
1121
+ "unsupported",
1122
+ CX2_ISSUE.LEGACY_LAYOUT,
1123
+ `a CX1 cartesianLayout aspect (${cartesianLayout.length} element(s)) next to node coordinates; the node coordinates are used, the aspect is kept in meta.extra.cx2`,
1124
+ { element: "cartesianLayout" }
1125
+ );
1126
+ return;
1127
+ }
1128
+ for (const held of cartesianLayout) {
1129
+ const entry = held.value;
1130
+ if (!isRecord(entry) || typeof entry.x !== "number" || typeof entry.y !== "number") {
1131
+ this.report.error(
1132
+ "validation-error",
1133
+ BAD_VALUE_CODE,
1134
+ "a cartesianLayout element has no numeric x and y; skipped",
1135
+ { line: held.line, element: "cartesianLayout" }
1136
+ );
1137
+ continue;
1138
+ }
1139
+ const row = this.rowOf(this.nodeRows, entry.node);
1140
+ if (row === void 0) {
1141
+ this.dangle("cartesianLayout entry");
1142
+ continue;
1143
+ }
1144
+ const z = typeof entry.z === "number" ? entry.z : null;
1145
+ this.setPosition(row, entry.x, entry.y, z);
1146
+ if (z !== null && this.zAs === "column") {
1147
+ if (this.zHandle === INVALID_INDEX) {
1148
+ this.zHandle = declareResolved(this.sink, "node", zDecl(CX2_FORMAT), this.report).handle;
1149
+ }
1150
+ this.sink.setNodeValue(this.zHandle, row, z);
1151
+ }
1152
+ }
1153
+ }
1154
+ /**
1155
+ * The sink row of a referenced id.
1156
+ * @param rows - the id map
1157
+ * @param raw - the parsed reference
1158
+ * @returns the row, or undefined
1159
+ */
1160
+ rowOf(rows, raw) {
1161
+ try {
1162
+ const { id } = cxId(raw);
1163
+ return rows.get(this.options.ids === "keep" ? id : this.coercer.value(id));
1164
+ } catch {
1165
+ return void 0;
1166
+ }
1167
+ }
1168
+ /**
1169
+ * Count a reference that names nothing.
1170
+ * @param kind - what referred
1171
+ */
1172
+ dangle(kind) {
1173
+ this.dangling.set(kind, (this.dangling.get(kind) ?? 0) + 1);
1174
+ }
1175
+ // ------------------------------------------------------------ attributes
1176
+ /**
1177
+ * Write the attributes of an element's v.
1178
+ * @param domain - node or edge
1179
+ * @param row - the row
1180
+ * @param attrs - the v object
1181
+ * @param element - the element name
1182
+ * @param line - its line
1183
+ */
1184
+ writeAttributes(domain, row, attrs, element, line) {
1185
+ const { report } = this;
1186
+ const table = this.decls[domain];
1187
+ for (const key of Object.keys(attrs)) {
1188
+ const value = attrs[key];
1189
+ const resolved = table.resolve(key);
1190
+ const name = resolved?.decl.name ?? key;
1191
+ if (domain === "node" && this.options.restoreMangledIds && name === ORIGINAL_ID_ATTRIBUTE) {
1192
+ continue;
1193
+ }
1194
+ if (domain === "edge" && name === this.options.weightFrom) {
1195
+ continue;
1196
+ }
1197
+ if (key === "id") {
1198
+ report.warnOnce(
1199
+ "validation-error",
1200
+ CX2_ISSUE.RESERVED_KEY,
1201
+ `${element}: the attribute name "id" is reserved in v; read as an ordinary attribute`,
1202
+ { line, element },
1203
+ `${CX2_ISSUE.RESERVED_KEY}:${domain}`
1204
+ );
1205
+ }
1206
+ if (resolved !== null && !resolved.viaAlias && resolved.decl.alias !== null && resolved.decl.alias !== key) {
1207
+ if (resolved.decl.alias in attrs) {
1208
+ report.warning(
1209
+ "validation-error",
1210
+ DUPLICATE_ATTRIBUTE_CODE,
1211
+ `${element}: "${name}" is given under its alias and its full name; the alias wins`,
1212
+ { line, element }
1213
+ );
1214
+ continue;
1215
+ }
1216
+ report.warnOnce(
1217
+ "coercion",
1218
+ CX2_ISSUE.ALIAS_BYPASSED,
1219
+ `${element}: the full name "${name}" is used although its alias "${resolved.decl.alias}" is declared; read as the same attribute`,
1220
+ { line, element },
1221
+ `${CX2_ISSUE.ALIAS_BYPASSED}:${domain}:${name}`
1222
+ );
1223
+ }
1224
+ if (value === null || value === void 0) {
1225
+ continue;
1226
+ }
1227
+ if (resolved === null) {
1228
+ continue;
1229
+ }
1230
+ const { decl } = resolved;
1231
+ if (decl.undeclared) {
1232
+ report.warnOnce(
1233
+ "validation-error",
1234
+ CX2_ISSUE.UNDECLARED_ATTRIBUTE,
1235
+ `the ${domain} attribute "${key}" is not declared; its type is inferred from the values`,
1236
+ { line, element: key },
1237
+ `${CX2_ISSUE.UNDECLARED_ATTRIBUTE}:${domain}:${key}`
1238
+ );
1239
+ }
1240
+ const converted = decl.type === null ? plainJson(value, (digits) => {
1241
+ this.precision(name, digits);
1242
+ }) : convertValue(value, decl.type, this.options.long, (digits) => {
1243
+ this.precision(name, digits);
1244
+ });
1245
+ if (converted === BAD) {
1246
+ report.error(
1247
+ "validation-error",
1248
+ BAD_VALUE_CODE,
1249
+ `${element}: "${name}" is ${shown(value)}, not a ${decl.type?.d ?? "value"}; the cell is unset`,
1250
+ { line, element }
1251
+ );
1252
+ continue;
1253
+ }
1254
+ try {
1255
+ if (domain === "node") {
1256
+ this.sink.setNodeValue(decl.handle, row, converted);
1257
+ } else {
1258
+ this.sink.setEdgeValue(decl.handle, row, converted);
1259
+ }
1260
+ } catch (err) {
1261
+ report.recordError(err, { line, element });
1262
+ }
1263
+ }
1264
+ }
1265
+ /**
1266
+ * Record a long value beyond 2^53 (once).
1267
+ * @param name - the attribute
1268
+ * @param digits - the value
1269
+ */
1270
+ precision(name, digits) {
1271
+ this.report.warnOnce(
1272
+ "precision",
1273
+ PRECISION_CODE,
1274
+ `"${name}": ${digits} is beyond 2^53; stored as the nearest double`,
1275
+ { element: name },
1276
+ `${PRECISION_CODE}:value`
1277
+ );
1278
+ }
1279
+ // ------------------------------------------------------------ edges
1280
+ /**
1281
+ * Read one edge element.
1282
+ * @param held - the element
1283
+ */
1284
+ readEdge(held) {
1285
+ this.checkAbort();
1286
+ const { report, sink } = this;
1287
+ const { value, line } = held;
1288
+ if (!isRecord(value)) {
1289
+ report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `an edges element is ${shown(value)}, not an object`, {
1290
+ line,
1291
+ element: "edges"
1292
+ });
1293
+ report.counts.skippedEdges++;
1294
+ return;
1295
+ }
1296
+ this.unknownKeys(value, EDGE_KEYS, "edge", line);
1297
+ if (value.id === void 0 || value.id === null) {
1298
+ report.error("missing-value", MISSING_ID_CODE, "an edge has no id", { line, element: "edges" });
1299
+ report.counts.skippedEdges++;
1300
+ return;
1301
+ }
1302
+ const element = `edge ${shown(value.id)}`;
1303
+ for (const key of ["s", "t"]) {
1304
+ if (value[key] === void 0 || value[key] === null) {
1305
+ report.error("missing-value", MISSING_ENDPOINT_CODE, `${element} has no ${key}`, { line, element });
1306
+ report.counts.skippedEdges++;
1307
+ return;
1308
+ }
1309
+ }
1310
+ const id = this.idOf(value.id, (held.inexact & 1) !== 0, element, true);
1311
+ const s = id === null ? null : this.idOf(value.s, (held.inexact & 2) !== 0, element);
1312
+ const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element);
1313
+ if (id === null || s === null || t === null) {
1314
+ report.counts.skippedEdges++;
1315
+ return;
1316
+ }
1317
+ if (this.edgeRows.has(id)) {
1318
+ report.error(
1319
+ "validation-error",
1320
+ DUPLICATE_EDGE_ID_CODE,
1321
+ `${element} is declared more than once; the later edge is skipped`,
1322
+ { line, element }
1323
+ );
1324
+ report.counts.skippedEdges++;
1325
+ return;
1326
+ }
1327
+ const source = this.endpoint(s, element, line);
1328
+ const target = source === null ? null : this.endpoint(t, element, line);
1329
+ if (source === null || target === null) {
1330
+ report.counts.skippedEdges++;
1331
+ return;
1332
+ }
1333
+ const attrs = isRecord(value.v) ? value.v : null;
1334
+ let weight;
1335
+ const weightKey = this.weightKey(attrs);
1336
+ try {
1337
+ weight = weightKey === null || attrs === null ? void 0 : weightFromValue(plainJson(attrs[weightKey]));
1338
+ } catch (err) {
1339
+ report.recordError(err, { line, element });
1340
+ report.counts.skippedEdges++;
1341
+ return;
1342
+ }
1343
+ if (weight !== void 0) {
1344
+ this.weighted = true;
1345
+ }
1346
+ const before = sink.edgeCount;
1347
+ let edge;
1348
+ try {
1349
+ edge = this.direction.addEdge(source, target, "directed", weight, { line, element });
1350
+ } catch (err) {
1351
+ report.recordError(err, { line, element });
1352
+ report.counts.skippedEdges++;
1353
+ return;
1354
+ }
1355
+ report.counts.edges += sink.edgeCount - before;
1356
+ this.edgeRows.set(id, edge);
1357
+ sink.setEdgeValue(this.edgeIdColumn(), edge, typeof id === "number" ? id : Number(id));
1358
+ if (attrs !== null) {
1359
+ this.writeAttributes("edge", edge, attrs, element, line);
1360
+ }
1361
+ }
1362
+ /**
1363
+ * The key of an edge's v that holds the weight: weightFrom, or its declared alias.
1364
+ * @param attrs - the edge's v
1365
+ * @returns the key present in attrs, or null
1366
+ */
1367
+ weightKey(attrs) {
1368
+ const { weightFrom } = this.options;
1369
+ if (weightFrom === null || attrs === null) {
1370
+ return null;
1371
+ }
1372
+ const alias = this.decls.edge.byName.get(weightFrom)?.alias ?? null;
1373
+ if (alias !== null && alias in attrs) {
1374
+ return alias;
1375
+ }
1376
+ return weightFrom in attrs ? weightFrom : null;
1377
+ }
1378
+ /**
1379
+ * The sink id of an edge endpoint: a known node, or a node created under addMissingNodes.
1380
+ * @param cx - the endpoint's CX id
1381
+ * @param element - the edge name
1382
+ * @param line - its line
1383
+ * @returns the sink id, or null when the endpoint was reported
1384
+ */
1385
+ endpoint(cx, element, line) {
1386
+ const known = this.sinkIds.get(cx);
1387
+ if (known !== void 0) {
1388
+ return known;
1389
+ }
1390
+ if (!this.options.addMissingNodes) {
1391
+ this.report.error(
1392
+ "missing-value",
1393
+ CX2_ISSUE.UNKNOWN_NODE,
1394
+ `${element}: the endpoint ${String(cx)} is not a node; the edge is skipped`,
1395
+ { line, element }
1396
+ );
1397
+ return null;
1398
+ }
1399
+ try {
1400
+ this.nodeRows.set(cx, this.sink.addNode(cx));
1401
+ } catch (err) {
1402
+ this.report.recordError(err, { line, element });
1403
+ return null;
1404
+ }
1405
+ this.sinkIds.set(cx, cx);
1406
+ this.report.counts.nodes++;
1407
+ return cx;
1408
+ }
1409
+ // ------------------------------------------------------------ network attributes, bypasses
1410
+ /** Write the network attributes (the first element; others are reported). */
1411
+ readNetworkAttributes() {
1412
+ const { networkAttributes } = this.doc;
1413
+ if (networkAttributes.length === 0) {
1414
+ return;
1415
+ }
1416
+ if (networkAttributes.length > 1) {
1417
+ this.report.warning(
1418
+ "validation-error",
1419
+ CX2_ISSUE.EXTRA_ELEMENTS,
1420
+ `networkAttributes holds ${networkAttributes.length} elements; the first is read`,
1421
+ { line: networkAttributes[1].line, element: "networkAttributes" }
1422
+ );
1423
+ }
1424
+ const { value, line } = networkAttributes[0];
1425
+ if (!isRecord(value)) {
1426
+ this.report.error("parse-error", BAD_ASPECT_BLOCK_CODE, "the networkAttributes element is not an object", {
1427
+ line,
1428
+ element: "networkAttributes"
1429
+ });
1430
+ return;
1431
+ }
1432
+ for (const [name, raw] of Object.entries(value)) {
1433
+ if (raw === null || raw === void 0) {
1434
+ continue;
1435
+ }
1436
+ const decl = this.decls.network.byName.get(name);
1437
+ const element = `networkAttributes.${name}`;
1438
+ if (decl === void 0) {
1439
+ this.report.warnOnce(
1440
+ "validation-error",
1441
+ CX2_ISSUE.UNDECLARED_ATTRIBUTE,
1442
+ `the network attribute "${name}" is not declared; its column is inferred from the value`,
1443
+ { line, element: name },
1444
+ `${CX2_ISSUE.UNDECLARED_ATTRIBUTE}:network:${name}`
1445
+ );
1446
+ }
1447
+ if ((name === "name" || name === "description") && typeof raw === "string" && decl?.type?.d !== "list_of_string") {
1448
+ continue;
1449
+ }
1450
+ try {
1451
+ const seen = { kind: void 0, list: void 0 };
1452
+ observe(seen, raw);
1453
+ const type = decl?.type ?? inferredType(seen);
1454
+ if (type === null) {
1455
+ this.sink.setGraphValue(
1456
+ name,
1457
+ plainJson(raw, (digits) => {
1458
+ this.precision(name, digits);
1459
+ }),
1460
+ {
1461
+ dtype: "json",
1462
+ origin: { format: CX2_FORMAT, id: null }
1463
+ }
1464
+ );
1465
+ continue;
1466
+ }
1467
+ const converted = convertValue(raw, type, this.options.long, (digits) => {
1468
+ this.precision(name, digits);
1469
+ });
1470
+ if (converted === BAD) {
1471
+ this.report.error(
1472
+ "validation-error",
1473
+ BAD_VALUE_CODE,
1474
+ `${element} is ${shown(raw)}, not a ${type.d}; unset`,
1475
+ { line, element }
1476
+ );
1477
+ continue;
1478
+ }
1479
+ const scalar = scalarDtype(type.scalar, this.options.long);
1480
+ this.sink.setGraphValue(name, converted, {
1481
+ dtype: type.list ? "list" : scalar,
1482
+ ...type.list ? { itemDtype: scalar } : {},
1483
+ origin: { format: CX2_FORMAT, id: null, type: type.d }
1484
+ });
1485
+ } catch (err) {
1486
+ this.report.recordError(err, { line, element });
1487
+ }
1488
+ }
1489
+ }
1490
+ /**
1491
+ * Write the per-element visual property values: one column per property, typed by its values.
1492
+ * @param domain - node or edge
1493
+ * @param entries - the bypass elements
1494
+ */
1495
+ readBypasses(domain, entries) {
1496
+ const rows = domain === "node" ? this.nodeRows : this.edgeRows;
1497
+ const columns = /* @__PURE__ */ new Map();
1498
+ for (const held of entries) {
1499
+ this.checkAbort();
1500
+ const { value, line } = held;
1501
+ if (!isRecord(value) || !isRecord(value.v)) {
1502
+ this.report.error(
1503
+ "parse-error",
1504
+ BAD_ASPECT_BLOCK_CODE,
1505
+ `a ${domain}Bypasses element is not an object with an id and a v object; skipped`,
1506
+ { line, element: `${domain}Bypasses` }
1507
+ );
1508
+ continue;
1509
+ }
1510
+ const row = this.rowOf(rows, value.id);
1511
+ if (row === void 0) {
1512
+ this.dangle(`${domain} bypass`);
1513
+ continue;
1514
+ }
1515
+ for (const [property, raw] of Object.entries(value.v)) {
1516
+ if (raw === null || raw === void 0) {
1517
+ continue;
1518
+ }
1519
+ let column = columns.get(property);
1520
+ if (column === void 0) {
1521
+ column = { rows: [], values: [] };
1522
+ columns.set(property, column);
1523
+ }
1524
+ column.rows.push(row);
1525
+ column.values.push(plainJson(raw));
1526
+ }
1527
+ }
1528
+ for (const [property, { rows: targets, values }] of columns) {
1529
+ const decl = {
1530
+ name: property,
1531
+ dtype: bypassDtype(values),
1532
+ nullable: true,
1533
+ // origin.id keeps the property's name: a column renamed for a clash is written back under it
1534
+ origin: { format: CX2_FORMAT, id: property, namespace: BYPASS_NAMESPACE }
1535
+ };
1536
+ const handle = declareFresh(this.sink, domain, decl, this.report);
1537
+ for (let i = 0; i < targets.length; i++) {
1538
+ if (domain === "node") {
1539
+ this.sink.setNodeValue(handle, targets[i], values[i]);
1540
+ } else {
1541
+ this.sink.setEdgeValue(handle, targets[i], values[i]);
1542
+ }
1543
+ }
1544
+ }
1545
+ }
1546
+ // ------------------------------------------------------------ aspects, status, metadata
1547
+ /** The style-rule loss, the single-element aspects, the status and the metadata counts. */
1548
+ checkAspects() {
1549
+ const { doc, report } = this;
1550
+ const { structure } = doc;
1551
+ const styles = [];
1552
+ for (const aspect of STYLE_ASPECTS) {
1553
+ const elements = doc.opaque.get(aspect);
1554
+ if (elements !== void 0) {
1555
+ styles.push(describeStyle(aspect, elements));
1556
+ }
1557
+ }
1558
+ if (styles.length > 0) {
1559
+ report.warning(
1560
+ "unsupported",
1561
+ STYLES_NOT_IMPORTED_CODE,
1562
+ `the file's style rules are not applied (${styles.join("; ")}); they are kept in meta.extra.cx2.opaque (style import is issue #706)`,
1563
+ { element: STYLE_ASPECTS.filter((a) => doc.opaque.has(a)).join(",") }
1564
+ );
1565
+ }
1566
+ for (const aspect of SINGLE_ELEMENT_ASPECTS.slice(1)) {
1567
+ const count = doc.opaque.get(aspect)?.length ?? 0;
1568
+ if (count > 1) {
1569
+ report.warning(
1570
+ "validation-error",
1571
+ CX2_ISSUE.EXTRA_ELEMENTS,
1572
+ `${aspect} holds ${count} elements where CX2 allows one; all are kept, a reader uses the first`,
1573
+ { element: aspect }
1574
+ );
1575
+ }
1576
+ }
1577
+ if (!doc.hasFragments) {
1578
+ for (const [aspect, blocks] of structure.blocks) {
1579
+ if (blocks > 1 && !STRUCTURE_ASPECTS.has(aspect)) {
1580
+ report.warnOnce(
1581
+ "validation-error",
1582
+ CX2_ISSUE.UNDECLARED_FRAGMENTS,
1583
+ `"${aspect}" comes in ${blocks} blocks although the descriptor does not declare hasFragments; the blocks are joined in order`,
1584
+ { element: aspect }
1585
+ );
1586
+ }
1587
+ }
1588
+ }
1589
+ if (!structure.hasStatus) {
1590
+ report.error("validation-error", CX2_ISSUE.NO_STATUS, "the document ends without a status block", {
1591
+ element: "status"
1592
+ });
1593
+ } else if (!structure.statusWellFormed() || structure.counts.get("status") !== 1) {
1594
+ const count = structure.counts.get("status") ?? 0;
1595
+ report.error(
1596
+ "validation-error",
1597
+ CX2_ISSUE.NO_STATUS,
1598
+ `the status is malformed (${count === 1 ? shown(structure.status) : `${count} elements`}): a document ends with one status block holding one object with a boolean success`,
1599
+ { element: "status" }
1600
+ );
1601
+ }
1602
+ structure.checkCounts();
1603
+ }
1604
+ /** Record the metadata: source format and version, name, description, id type, the kept aspects. */
1605
+ setMeta() {
1606
+ const { doc } = this;
1607
+ const attrs = doc.networkAttributes[0]?.value;
1608
+ const named = isRecord(attrs) ? attrs : {};
1609
+ const extra = {};
1610
+ if (doc.opaque.size > 0) {
1611
+ extra.opaque = Object.fromEntries(doc.opaque);
1612
+ }
1613
+ if (Object.keys(this.decls.other).length > 0) {
1614
+ extra.declarations = this.decls.other;
1615
+ }
1616
+ const { weightFrom } = this.options;
1617
+ const weightDecl = weightFrom === null ? void 0 : this.decls.edge.byName.get(weightFrom);
1618
+ const patch = {
1619
+ sourceFormat: CX2_FORMAT,
1620
+ sourceVersion: doc.version,
1621
+ idType: this.idType,
1622
+ ...typeof named.name === "string" ? { name: named.name } : {},
1623
+ ...typeof named.description === "string" ? { description: named.description } : {},
1624
+ ...this.weighted && weightFrom !== null ? {
1625
+ weightOrigin: {
1626
+ format: CX2_FORMAT,
1627
+ id: weightDecl?.alias ?? null,
1628
+ title: weightFrom,
1629
+ type: weightDecl?.type?.d ?? null,
1630
+ namespace: null
1631
+ }
1632
+ } : {},
1633
+ extra: { cx2: extra }
1634
+ };
1635
+ this.sink.setMeta(patch);
1636
+ }
1637
+ }
1638
+ function bypassDtype(values) {
1639
+ if (values.every((v) => typeof v === "number")) {
1640
+ return "f64";
1641
+ }
1642
+ if (values.every((v) => typeof v === "boolean")) {
1643
+ return "bool";
1644
+ }
1645
+ if (values.every((v) => typeof v === "string")) {
1646
+ return "string";
1647
+ }
1648
+ return "json";
1649
+ }
1650
+ function describeStyle(aspect, elements) {
1651
+ const first = elements[0];
1652
+ if (aspect === "visualProperties" && isRecord(first)) {
1653
+ const count = (key) => isRecord(first[key]) ? Object.keys(first[key]).length : 0;
1654
+ const defaults = isRecord(first.default) ? Object.values(first.default).reduce((n, v) => n + (isRecord(v) ? Object.keys(v).length : 0), 0) : 0;
1655
+ return `visualProperties: ${defaults} default(s), ${count("nodeMapping")} node mapping(s), ${count("edgeMapping")} edge mapping(s)`;
1656
+ }
1657
+ return `${aspect}: ${elements.length} element(s)`;
1658
+ }
1659
+ function zAsOption(value) {
1660
+ if (value === void 0) {
1661
+ return "column";
1662
+ }
1663
+ if (value === "column" || value === "position") {
1664
+ return value;
1665
+ }
1666
+ throw new GraphFormatError("E_UNSUPPORTED", `option zAs: ${shown(value)} is not one of "column", "position"`, {
1667
+ option: "zAs",
1668
+ found: typeof value === "string" ? value : typeof value
1669
+ });
1670
+ }
1671
+ const cx2Importer = Object.freeze({
1672
+ format: CX2_FORMAT,
1673
+ extensions: Object.freeze([".cx2"]),
1674
+ mimeTypes: Object.freeze(["application/json"]),
1675
+ /**
1676
+ * Confidence that the head is CX2: 0.97 when the first member of a top-level array is an
1677
+ * object with a CXVersion key, in any key order.
1678
+ * @param head - the first bytes
1679
+ * @returns the confidence
1680
+ */
1681
+ sniff(head) {
1682
+ const text = new TextDecoder("utf-8").decode(head.subarray(0, 1024)).replace(/^\uFEFF/, "");
1683
+ return /^\s*\[\s*\{(?:\s*"[^"]*"\s*:\s*(?:true|false|"[^"]*"|[0-9.]+)\s*,)*\s*"CXVersion"\s*:/.test(text) ? 0.97 : 0;
1684
+ },
1685
+ /**
1686
+ * Read a CX2 document into the sink.
1687
+ * @param input - the text, bytes or stream
1688
+ * @param sink - the sink
1689
+ * @param options - format-specific and common options
1690
+ * @returns the import report; ImportError on a fatal error or beyond the error limit
1691
+ */
1692
+ async import(input, sink, options) {
1693
+ const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
1694
+ const zAs = zAsOption(options?.zAs);
1695
+ const report = new ImportReportBuilder(CX2_FORMAT, resolved.errorLimit);
1696
+ reportSinkOptions(sink, options, report, true);
1697
+ reportUnusedOptions(options, report, USED_OPTIONS);
1698
+ const doc = await readDocument(input, report, resolved);
1699
+ new Cx2Reader(sink, report, resolved, zAs, doc).build();
1700
+ throwIfAborted(resolved.signal);
1701
+ return report.finish();
1702
+ }
1703
+ });
1704
+ export {
1705
+ BYPASS_NAMESPACE as B,
1706
+ CX2_ISSUE as C,
1707
+ ORIGINAL_ID_ATTRIBUTE as O,
1708
+ CX2_FORMAT as a,
1709
+ cx2Type as b,
1710
+ cx2Importer as c,
1711
+ zAsOption as z
1712
+ };
1713
+ //# sourceMappingURL=importer-vi3bSdtw.js.map