@graphty/graph-io 0.3.24 → 0.3.26

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 (581) hide show
  1. package/README.md +49 -414
  2. package/dist/chunks/children-CL3Cy0ez.js.map +1 -1
  3. package/dist/chunks/choose-BbJI55dx.js +56 -0
  4. package/dist/chunks/choose-BbJI55dx.js.map +1 -0
  5. package/dist/chunks/constants-BCsFsMj4.js +231 -0
  6. package/dist/chunks/constants-BCsFsMj4.js.map +1 -0
  7. package/dist/chunks/{escape-B-tkk_Cl.js → escape-D0g6NNUy.js} +382 -92
  8. package/dist/chunks/escape-D0g6NNUy.js.map +1 -0
  9. package/dist/chunks/export-BTI4yQgy.js +4333 -0
  10. package/dist/chunks/export-BTI4yQgy.js.map +1 -0
  11. package/dist/chunks/exporter-BMqk5oxW.js +3465 -0
  12. package/dist/chunks/exporter-BMqk5oxW.js.map +1 -0
  13. package/dist/chunks/{importer-eUREhBGj.js → exporter-Cra2GYcN.js} +4969 -3201
  14. package/dist/chunks/exporter-Cra2GYcN.js.map +1 -0
  15. package/dist/chunks/{exporter-BGrsWamJ.js → exporter-Dxh231ZF.js} +38 -229
  16. package/dist/chunks/exporter-Dxh231ZF.js.map +1 -0
  17. package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-tne475KK.js} +22 -20
  18. package/dist/chunks/exporter-tne475KK.js.map +1 -0
  19. package/dist/chunks/importer-90YWhJoF.js +2811 -0
  20. package/dist/chunks/importer-90YWhJoF.js.map +1 -0
  21. package/dist/chunks/{importer-D_e7e7LX.js → importer-Bcl1d4xH.js} +398 -145
  22. package/dist/chunks/importer-Bcl1d4xH.js.map +1 -0
  23. package/dist/chunks/{importer-BWY2FFc5.js → importer-BfIsp1f9.js} +255 -77
  24. package/dist/chunks/importer-BfIsp1f9.js.map +1 -0
  25. package/dist/chunks/{importer-zGLo8Dg_.js → importer-C_TEA1zJ.js} +484 -157
  26. package/dist/chunks/importer-C_TEA1zJ.js.map +1 -0
  27. package/dist/chunks/{importer-DET1tg6s.js → importer-DCjOvO7U.js} +1687 -310
  28. package/dist/chunks/importer-DCjOvO7U.js.map +1 -0
  29. package/dist/chunks/{importer-vi3bSdtw.js → importer-DfVu6j59.js} +509 -76
  30. package/dist/chunks/importer-DfVu6j59.js.map +1 -0
  31. package/dist/chunks/{importer-DIrbbnAf.js → importer-Dku_VAIY.js} +585 -226
  32. package/dist/chunks/importer-Dku_VAIY.js.map +1 -0
  33. package/dist/chunks/json-elements-3tnyt5uZ.js +1225 -0
  34. package/dist/chunks/json-elements-3tnyt5uZ.js.map +1 -0
  35. package/dist/chunks/ontology-export-B9Tcf37D.js +357 -0
  36. package/dist/chunks/ontology-export-B9Tcf37D.js.map +1 -0
  37. package/dist/chunks/{records-dbRkxwaq.js → records-CQXT__mY.js} +298 -114
  38. package/dist/chunks/records-CQXT__mY.js.map +1 -0
  39. package/dist/chunks/{text-Dr0Ifpag.js → text-DmcUs5_Q.js} +29 -11
  40. package/dist/chunks/text-DmcUs5_Q.js.map +1 -0
  41. package/dist/chunks/{writer-C7flM-Ih.js → writer-DxjgHwSe.js} +19 -1
  42. package/dist/chunks/writer-DxjgHwSe.js.map +1 -0
  43. package/dist/csv.js +951 -261
  44. package/dist/csv.js.map +1 -1
  45. package/dist/cx.js +6 -3
  46. package/dist/cx2.js +2 -2
  47. package/dist/cys.js +6 -3
  48. package/dist/dot.js +1 -1
  49. package/dist/gexf.js +554 -122
  50. package/dist/gexf.js.map +1 -1
  51. package/dist/gml.js +342 -85
  52. package/dist/gml.js.map +1 -1
  53. package/dist/graph-io.js +1105 -213
  54. package/dist/graph-io.js.map +1 -1
  55. package/dist/graphml.js +3 -2
  56. package/dist/graphml.js.map +1 -1
  57. package/dist/json.js +3 -2
  58. package/dist/neo4j.js +564 -157
  59. package/dist/neo4j.js.map +1 -1
  60. package/dist/obo.js +6 -3
  61. package/dist/pajek.js +1 -1
  62. package/dist/src/children.d.ts +18 -6
  63. package/dist/src/children.d.ts.map +1 -1
  64. package/dist/src/children.js +6 -3
  65. package/dist/src/children.js.map +1 -1
  66. package/dist/src/common/attributes.d.ts +29 -11
  67. package/dist/src/common/attributes.d.ts.map +1 -1
  68. package/dist/src/common/attributes.js +25 -7
  69. package/dist/src/common/attributes.js.map +1 -1
  70. package/dist/src/common/cell-budget.d.ts +9 -2
  71. package/dist/src/common/cell-budget.d.ts.map +1 -1
  72. package/dist/src/common/cell-budget.js +11 -3
  73. package/dist/src/common/cell-budget.js.map +1 -1
  74. package/dist/src/common/choose.d.ts +19 -0
  75. package/dist/src/common/choose.d.ts.map +1 -0
  76. package/dist/src/common/choose.js +72 -0
  77. package/dist/src/common/choose.js.map +1 -0
  78. package/dist/src/common/codes.d.ts +456 -66
  79. package/dist/src/common/codes.d.ts.map +1 -1
  80. package/dist/src/common/codes.js +458 -66
  81. package/dist/src/common/codes.js.map +1 -1
  82. package/dist/src/common/compare.d.ts +81 -0
  83. package/dist/src/common/compare.d.ts.map +1 -0
  84. package/dist/src/common/compare.js +394 -0
  85. package/dist/src/common/compare.js.map +1 -0
  86. package/dist/src/common/cx-export.d.ts +123 -0
  87. package/dist/src/common/cx-export.d.ts.map +1 -0
  88. package/dist/src/common/cx-export.js +334 -0
  89. package/dist/src/common/cx-export.js.map +1 -0
  90. package/dist/src/common/declared-types.d.ts +22 -2
  91. package/dist/src/common/declared-types.d.ts.map +1 -1
  92. package/dist/src/common/declared-types.js +19 -1
  93. package/dist/src/common/declared-types.js.map +1 -1
  94. package/dist/src/common/direction.d.ts +74 -50
  95. package/dist/src/common/direction.d.ts.map +1 -1
  96. package/dist/src/common/direction.js +47 -27
  97. package/dist/src/common/direction.js.map +1 -1
  98. package/dist/src/common/escape.d.ts +21 -9
  99. package/dist/src/common/escape.d.ts.map +1 -1
  100. package/dist/src/common/escape.js +33 -17
  101. package/dist/src/common/escape.js.map +1 -1
  102. package/dist/src/common/export.d.ts +144 -32
  103. package/dist/src/common/export.d.ts.map +1 -1
  104. package/dist/src/common/export.js +216 -42
  105. package/dist/src/common/export.js.map +1 -1
  106. package/dist/src/common/format.d.ts +23 -1
  107. package/dist/src/common/format.d.ts.map +1 -1
  108. package/dist/src/common/format.js +26 -1
  109. package/dist/src/common/format.js.map +1 -1
  110. package/dist/src/common/ids.d.ts +9 -2
  111. package/dist/src/common/ids.d.ts.map +1 -1
  112. package/dist/src/common/ids.js +6 -2
  113. package/dist/src/common/ids.js.map +1 -1
  114. package/dist/src/common/input.d.ts +122 -28
  115. package/dist/src/common/input.d.ts.map +1 -1
  116. package/dist/src/common/input.js +890 -124
  117. package/dist/src/common/input.js.map +1 -1
  118. package/dist/src/common/json-elements.d.ts +144 -20
  119. package/dist/src/common/json-elements.d.ts.map +1 -1
  120. package/dist/src/common/json-elements.js +868 -188
  121. package/dist/src/common/json-elements.js.map +1 -1
  122. package/dist/src/common/line-format.d.ts +90 -0
  123. package/dist/src/common/line-format.d.ts.map +1 -0
  124. package/dist/src/common/line-format.js +185 -0
  125. package/dist/src/common/line-format.js.map +1 -0
  126. package/dist/src/common/lists.d.ts +6 -1
  127. package/dist/src/common/lists.d.ts.map +1 -1
  128. package/dist/src/common/lists.js +2 -0
  129. package/dist/src/common/lists.js.map +1 -1
  130. package/dist/src/common/ontology-export.d.ts +167 -0
  131. package/dist/src/common/ontology-export.d.ts.map +1 -0
  132. package/dist/src/common/ontology-export.js +359 -0
  133. package/dist/src/common/ontology-export.js.map +1 -0
  134. package/dist/src/common/ontology.d.ts +25 -3
  135. package/dist/src/common/ontology.d.ts.map +1 -1
  136. package/dist/src/common/ontology.js +30 -9
  137. package/dist/src/common/ontology.js.map +1 -1
  138. package/dist/src/common/options.d.ts +62 -12
  139. package/dist/src/common/options.d.ts.map +1 -1
  140. package/dist/src/common/options.js +89 -11
  141. package/dist/src/common/options.js.map +1 -1
  142. package/dist/src/common/plural.d.ts +31 -0
  143. package/dist/src/common/plural.d.ts.map +1 -0
  144. package/dist/src/common/plural.js +46 -0
  145. package/dist/src/common/plural.js.map +1 -0
  146. package/dist/src/common/report.d.ts +72 -14
  147. package/dist/src/common/report.d.ts.map +1 -1
  148. package/dist/src/common/report.js +146 -22
  149. package/dist/src/common/report.js.map +1 -1
  150. package/dist/src/common/temporal.d.ts +22 -5
  151. package/dist/src/common/temporal.d.ts.map +1 -1
  152. package/dist/src/common/temporal.js +13 -2
  153. package/dist/src/common/temporal.js.map +1 -1
  154. package/dist/src/common/text.d.ts +39 -23
  155. package/dist/src/common/text.d.ts.map +1 -1
  156. package/dist/src/common/text.js +51 -21
  157. package/dist/src/common/text.js.map +1 -1
  158. package/dist/src/common/weights.d.ts +37 -4
  159. package/dist/src/common/weights.d.ts.map +1 -1
  160. package/dist/src/common/weights.js +61 -2
  161. package/dist/src/common/weights.js.map +1 -1
  162. package/dist/src/common/writer.d.ts +19 -3
  163. package/dist/src/common/writer.d.ts.map +1 -1
  164. package/dist/src/common/writer.js +30 -2
  165. package/dist/src/common/writer.js.map +1 -1
  166. package/dist/src/common/xml.d.ts +125 -9
  167. package/dist/src/common/xml.d.ts.map +1 -1
  168. package/dist/src/common/xml.js +400 -83
  169. package/dist/src/common/xml.js.map +1 -1
  170. package/dist/src/common/zip.d.ts +44 -4
  171. package/dist/src/common/zip.d.ts.map +1 -1
  172. package/dist/src/common/zip.js +134 -5
  173. package/dist/src/common/zip.js.map +1 -1
  174. package/dist/src/formats/csv/exporter.d.ts +80 -24
  175. package/dist/src/formats/csv/exporter.d.ts.map +1 -1
  176. package/dist/src/formats/csv/exporter.js +145 -32
  177. package/dist/src/formats/csv/exporter.js.map +1 -1
  178. package/dist/src/formats/csv/header.d.ts +38 -9
  179. package/dist/src/formats/csv/header.d.ts.map +1 -1
  180. package/dist/src/formats/csv/header.js +46 -10
  181. package/dist/src/formats/csv/header.js.map +1 -1
  182. package/dist/src/formats/csv/importer.d.ts +187 -37
  183. package/dist/src/formats/csv/importer.d.ts.map +1 -1
  184. package/dist/src/formats/csv/importer.js +835 -204
  185. package/dist/src/formats/csv/importer.js.map +1 -1
  186. package/dist/src/formats/csv/index.d.ts +100 -19
  187. package/dist/src/formats/csv/index.d.ts.map +1 -1
  188. package/dist/src/formats/csv/index.js +94 -21
  189. package/dist/src/formats/csv/index.js.map +1 -1
  190. package/dist/src/formats/csv/records.d.ts +95 -10
  191. package/dist/src/formats/csv/records.d.ts.map +1 -1
  192. package/dist/src/formats/csv/records.js +333 -122
  193. package/dist/src/formats/csv/records.js.map +1 -1
  194. package/dist/src/formats/csv/values.d.ts +1 -0
  195. package/dist/src/formats/csv/values.d.ts.map +1 -1
  196. package/dist/src/formats/csv/values.js +1 -0
  197. package/dist/src/formats/csv/values.js.map +1 -1
  198. package/dist/src/formats/cx/exporter.d.ts +102 -0
  199. package/dist/src/formats/cx/exporter.d.ts.map +1 -0
  200. package/dist/src/formats/cx/exporter.js +1217 -0
  201. package/dist/src/formats/cx/exporter.js.map +1 -0
  202. package/dist/src/formats/cx/importer.d.ts +79 -32
  203. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  204. package/dist/src/formats/cx/importer.js +680 -285
  205. package/dist/src/formats/cx/importer.js.map +1 -1
  206. package/dist/src/formats/cx/index.d.ts +4 -3
  207. package/dist/src/formats/cx/index.d.ts.map +1 -1
  208. package/dist/src/formats/cx/index.js +4 -3
  209. package/dist/src/formats/cx/index.js.map +1 -1
  210. package/dist/src/formats/cx2/exporter.d.ts +29 -11
  211. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  212. package/dist/src/formats/cx2/exporter.js +39 -268
  213. package/dist/src/formats/cx2/exporter.js.map +1 -1
  214. package/dist/src/formats/cx2/importer.d.ts +78 -28
  215. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  216. package/dist/src/formats/cx2/importer.js +226 -79
  217. package/dist/src/formats/cx2/importer.js.map +1 -1
  218. package/dist/src/formats/cx2/index.d.ts +2 -1
  219. package/dist/src/formats/cx2/index.d.ts.map +1 -1
  220. package/dist/src/formats/cx2/index.js +2 -1
  221. package/dist/src/formats/cx2/index.js.map +1 -1
  222. package/dist/src/formats/cys/constants.d.ts +66 -17
  223. package/dist/src/formats/cys/constants.d.ts.map +1 -1
  224. package/dist/src/formats/cys/constants.js +59 -18
  225. package/dist/src/formats/cys/constants.js.map +1 -1
  226. package/dist/src/formats/cys/exporter.d.ts +118 -0
  227. package/dist/src/formats/cys/exporter.d.ts.map +1 -0
  228. package/dist/src/formats/cys/exporter.js +1072 -0
  229. package/dist/src/formats/cys/exporter.js.map +1 -0
  230. package/dist/src/formats/cys/importer.d.ts +22 -8
  231. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  232. package/dist/src/formats/cys/importer.js +510 -149
  233. package/dist/src/formats/cys/importer.js.map +1 -1
  234. package/dist/src/formats/cys/index.d.ts +4 -2
  235. package/dist/src/formats/cys/index.d.ts.map +1 -1
  236. package/dist/src/formats/cys/index.js +4 -2
  237. package/dist/src/formats/cys/index.js.map +1 -1
  238. package/dist/src/formats/cys/session.d.ts +46 -7
  239. package/dist/src/formats/cys/session.d.ts.map +1 -1
  240. package/dist/src/formats/cys/session.js +98 -24
  241. package/dist/src/formats/cys/session.js.map +1 -1
  242. package/dist/src/formats/cys/tables.d.ts +9 -2
  243. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  244. package/dist/src/formats/cys/tables.js +29 -7
  245. package/dist/src/formats/cys/tables.js.map +1 -1
  246. package/dist/src/formats/dot/exporter.d.ts +42 -14
  247. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  248. package/dist/src/formats/dot/exporter.js +56 -29
  249. package/dist/src/formats/dot/exporter.js.map +1 -1
  250. package/dist/src/formats/dot/importer.d.ts +91 -29
  251. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  252. package/dist/src/formats/dot/importer.js +190 -39
  253. package/dist/src/formats/dot/importer.js.map +1 -1
  254. package/dist/src/formats/dot/index.d.ts +2 -1
  255. package/dist/src/formats/dot/index.d.ts.map +1 -1
  256. package/dist/src/formats/dot/index.js +2 -1
  257. package/dist/src/formats/dot/index.js.map +1 -1
  258. package/dist/src/formats/dot/names.d.ts +44 -11
  259. package/dist/src/formats/dot/names.d.ts.map +1 -1
  260. package/dist/src/formats/dot/names.js +44 -11
  261. package/dist/src/formats/dot/names.js.map +1 -1
  262. package/dist/src/formats/dot/tokenizer.d.ts +9 -2
  263. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
  264. package/dist/src/formats/dot/tokenizer.js +20 -5
  265. package/dist/src/formats/dot/tokenizer.js.map +1 -1
  266. package/dist/src/formats/gexf/exporter.d.ts +46 -15
  267. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  268. package/dist/src/formats/gexf/exporter.js +45 -15
  269. package/dist/src/formats/gexf/exporter.js.map +1 -1
  270. package/dist/src/formats/gexf/importer.d.ts +87 -23
  271. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  272. package/dist/src/formats/gexf/importer.js +471 -90
  273. package/dist/src/formats/gexf/importer.js.map +1 -1
  274. package/dist/src/formats/gexf/index.d.ts +84 -21
  275. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  276. package/dist/src/formats/gexf/index.js +80 -23
  277. package/dist/src/formats/gexf/index.js.map +1 -1
  278. package/dist/src/formats/gexf/schema.d.ts +67 -15
  279. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  280. package/dist/src/formats/gexf/schema.js +59 -13
  281. package/dist/src/formats/gexf/schema.js.map +1 -1
  282. package/dist/src/formats/gml/exporter.d.ts +69 -21
  283. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  284. package/dist/src/formats/gml/exporter.js +74 -29
  285. package/dist/src/formats/gml/exporter.js.map +1 -1
  286. package/dist/src/formats/gml/importer.d.ts +116 -25
  287. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  288. package/dist/src/formats/gml/importer.js +290 -61
  289. package/dist/src/formats/gml/importer.js.map +1 -1
  290. package/dist/src/formats/gml/index.d.ts +87 -24
  291. package/dist/src/formats/gml/index.d.ts.map +1 -1
  292. package/dist/src/formats/gml/index.js +79 -26
  293. package/dist/src/formats/gml/index.js.map +1 -1
  294. package/dist/src/formats/gml/syntax.d.ts +67 -18
  295. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  296. package/dist/src/formats/gml/syntax.js +108 -26
  297. package/dist/src/formats/gml/syntax.js.map +1 -1
  298. package/dist/src/formats/graphml/constants.d.ts +174 -44
  299. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  300. package/dist/src/formats/graphml/constants.js +165 -44
  301. package/dist/src/formats/graphml/constants.js.map +1 -1
  302. package/dist/src/formats/graphml/exporter.d.ts +17 -9
  303. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  304. package/dist/src/formats/graphml/exporter.js +33 -12
  305. package/dist/src/formats/graphml/exporter.js.map +1 -1
  306. package/dist/src/formats/graphml/importer.d.ts +19 -7
  307. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  308. package/dist/src/formats/graphml/importer.js +466 -39
  309. package/dist/src/formats/graphml/importer.js.map +1 -1
  310. package/dist/src/formats/graphml/index.d.ts +2 -1
  311. package/dist/src/formats/graphml/index.d.ts.map +1 -1
  312. package/dist/src/formats/graphml/index.js +2 -1
  313. package/dist/src/formats/graphml/index.js.map +1 -1
  314. package/dist/src/formats/graphml/tree.d.ts +6 -2
  315. package/dist/src/formats/graphml/tree.d.ts.map +1 -1
  316. package/dist/src/formats/graphml/tree.js +6 -2
  317. package/dist/src/formats/graphml/tree.js.map +1 -1
  318. package/dist/src/formats/graphml/yfiles.d.ts +8 -3
  319. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  320. package/dist/src/formats/graphml/yfiles.js +36 -15
  321. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  322. package/dist/src/formats/json/dialect.d.ts +88 -24
  323. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  324. package/dist/src/formats/json/dialect.js +109 -19
  325. package/dist/src/formats/json/dialect.js.map +1 -1
  326. package/dist/src/formats/json/exporter.d.ts +134 -22
  327. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  328. package/dist/src/formats/json/exporter.js +135 -54
  329. package/dist/src/formats/json/exporter.js.map +1 -1
  330. package/dist/src/formats/json/importer.d.ts +173 -52
  331. package/dist/src/formats/json/importer.d.ts.map +1 -1
  332. package/dist/src/formats/json/importer.js +899 -220
  333. package/dist/src/formats/json/importer.js.map +1 -1
  334. package/dist/src/formats/json/index.d.ts +3 -2
  335. package/dist/src/formats/json/index.d.ts.map +1 -1
  336. package/dist/src/formats/json/index.js +3 -2
  337. package/dist/src/formats/json/index.js.map +1 -1
  338. package/dist/src/formats/json/obographs-export.d.ts +68 -0
  339. package/dist/src/formats/json/obographs-export.d.ts.map +1 -0
  340. package/dist/src/formats/json/obographs-export.js +941 -0
  341. package/dist/src/formats/json/obographs-export.js.map +1 -0
  342. package/dist/src/formats/json/obographs.d.ts +26 -0
  343. package/dist/src/formats/json/obographs.d.ts.map +1 -1
  344. package/dist/src/formats/json/obographs.js +297 -80
  345. package/dist/src/formats/json/obographs.js.map +1 -1
  346. package/dist/src/formats/neo4j/exporter.d.ts +109 -21
  347. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  348. package/dist/src/formats/neo4j/exporter.js +238 -85
  349. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  350. package/dist/src/formats/neo4j/header.d.ts +17 -4
  351. package/dist/src/formats/neo4j/header.d.ts.map +1 -1
  352. package/dist/src/formats/neo4j/header.js +36 -4
  353. package/dist/src/formats/neo4j/header.js.map +1 -1
  354. package/dist/src/formats/neo4j/importer.d.ts +107 -27
  355. package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
  356. package/dist/src/formats/neo4j/importer.js +415 -63
  357. package/dist/src/formats/neo4j/importer.js.map +1 -1
  358. package/dist/src/formats/neo4j/index.d.ts +73 -23
  359. package/dist/src/formats/neo4j/index.d.ts.map +1 -1
  360. package/dist/src/formats/neo4j/index.js +65 -25
  361. package/dist/src/formats/neo4j/index.js.map +1 -1
  362. package/dist/src/formats/obo/exporter.d.ts +150 -0
  363. package/dist/src/formats/obo/exporter.d.ts.map +1 -0
  364. package/dist/src/formats/obo/exporter.js +1736 -0
  365. package/dist/src/formats/obo/exporter.js.map +1 -0
  366. package/dist/src/formats/obo/importer.d.ts +95 -19
  367. package/dist/src/formats/obo/importer.d.ts.map +1 -1
  368. package/dist/src/formats/obo/importer.js +475 -104
  369. package/dist/src/formats/obo/importer.js.map +1 -1
  370. package/dist/src/formats/obo/index.d.ts +6 -3
  371. package/dist/src/formats/obo/index.d.ts.map +1 -1
  372. package/dist/src/formats/obo/index.js +6 -3
  373. package/dist/src/formats/obo/index.js.map +1 -1
  374. package/dist/src/formats/obo/syntax.d.ts +108 -4
  375. package/dist/src/formats/obo/syntax.d.ts.map +1 -1
  376. package/dist/src/formats/obo/syntax.js +211 -34
  377. package/dist/src/formats/obo/syntax.js.map +1 -1
  378. package/dist/src/formats/pajek/exporter.d.ts +54 -16
  379. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  380. package/dist/src/formats/pajek/exporter.js +66 -34
  381. package/dist/src/formats/pajek/exporter.js.map +1 -1
  382. package/dist/src/formats/pajek/importer.d.ts +84 -22
  383. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  384. package/dist/src/formats/pajek/importer.js +303 -83
  385. package/dist/src/formats/pajek/importer.js.map +1 -1
  386. package/dist/src/formats/pajek/index.d.ts +2 -1
  387. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  388. package/dist/src/formats/pajek/index.js +2 -1
  389. package/dist/src/formats/pajek/index.js.map +1 -1
  390. package/dist/src/formats/pajek/syntax.d.ts +77 -14
  391. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  392. package/dist/src/formats/pajek/syntax.js +183 -51
  393. package/dist/src/formats/pajek/syntax.js.map +1 -1
  394. package/dist/src/formats/xgmml/columns.d.ts +1 -0
  395. package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
  396. package/dist/src/formats/xgmml/columns.js +4 -0
  397. package/dist/src/formats/xgmml/columns.js.map +1 -1
  398. package/dist/src/formats/xgmml/constants.d.ts +186 -56
  399. package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
  400. package/dist/src/formats/xgmml/constants.js +181 -57
  401. package/dist/src/formats/xgmml/constants.js.map +1 -1
  402. package/dist/src/formats/xgmml/document.d.ts +65 -11
  403. package/dist/src/formats/xgmml/document.d.ts.map +1 -1
  404. package/dist/src/formats/xgmml/document.js +157 -28
  405. package/dist/src/formats/xgmml/document.js.map +1 -1
  406. package/dist/src/formats/xgmml/emit.d.ts +61 -3
  407. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  408. package/dist/src/formats/xgmml/emit.js +129 -26
  409. package/dist/src/formats/xgmml/emit.js.map +1 -1
  410. package/dist/src/formats/xgmml/exporter.d.ts +13 -6
  411. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  412. package/dist/src/formats/xgmml/exporter.js +20 -21
  413. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  414. package/dist/src/formats/xgmml/importer.d.ts +33 -12
  415. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  416. package/dist/src/formats/xgmml/importer.js +35 -72
  417. package/dist/src/formats/xgmml/importer.js.map +1 -1
  418. package/dist/src/formats/xgmml/index.d.ts +1 -0
  419. package/dist/src/formats/xgmml/index.d.ts.map +1 -1
  420. package/dist/src/formats/xgmml/index.js +1 -0
  421. package/dist/src/formats/xgmml/index.js.map +1 -1
  422. package/dist/src/formats/xgmml/values.d.ts +8 -1
  423. package/dist/src/formats/xgmml/values.d.ts.map +1 -1
  424. package/dist/src/formats/xgmml/values.js +7 -2
  425. package/dist/src/formats/xgmml/values.js.map +1 -1
  426. package/dist/src/index.d.ts +23 -18
  427. package/dist/src/index.d.ts.map +1 -1
  428. package/dist/src/index.js +28 -16
  429. package/dist/src/index.js.map +1 -1
  430. package/dist/src/registry.d.ts +478 -92
  431. package/dist/src/registry.d.ts.map +1 -1
  432. package/dist/src/registry.js +807 -88
  433. package/dist/src/registry.js.map +1 -1
  434. package/dist/src/sniff.d.ts +61 -19
  435. package/dist/src/sniff.d.ts.map +1 -1
  436. package/dist/src/sniff.js +121 -29
  437. package/dist/src/sniff.js.map +1 -1
  438. package/dist/src/types.d.ts +362 -112
  439. package/dist/src/types.d.ts.map +1 -1
  440. package/dist/src/types.js +28 -7
  441. package/dist/src/types.js.map +1 -1
  442. package/dist/xgmml.js +2 -2
  443. package/docs/samples/README.md +25 -0
  444. package/docs/samples/got-edges.csv +353 -0
  445. package/docs/samples/got-network.graphml +1384 -0
  446. package/docs/samples/got-nodes.csv +108 -0
  447. package/docs/samples/got.cx +930 -0
  448. package/docs/samples/got.cx2 +465 -0
  449. package/docs/samples/got.gexf +471 -0
  450. package/docs/samples/got.gml +2650 -0
  451. package/docs/samples/got.json +2546 -0
  452. package/docs/samples/got.net +461 -0
  453. package/docs/samples/movies-nodes.csv +9 -0
  454. package/docs/samples/movies-rels.csv +8 -0
  455. package/docs/samples/networks.cys +0 -0
  456. package/docs/samples/proteins.xgmml +9 -0
  457. package/docs/samples/teams.gv +24 -0
  458. package/docs/samples/vehicles.obo +25 -0
  459. package/package.json +9 -4
  460. package/src/children.ts +18 -6
  461. package/src/common/attributes.ts +29 -11
  462. package/src/common/cell-budget.ts +11 -3
  463. package/src/common/choose.ts +89 -0
  464. package/src/common/codes.ts +490 -66
  465. package/src/common/compare.ts +472 -0
  466. package/src/common/cx-export.ts +432 -0
  467. package/src/common/declared-types.ts +38 -4
  468. package/src/common/direction.ts +78 -51
  469. package/src/common/escape.ts +33 -17
  470. package/src/common/export.ts +284 -47
  471. package/src/common/format.ts +30 -1
  472. package/src/common/ids.ts +14 -3
  473. package/src/common/input.ts +1057 -134
  474. package/src/common/json-elements.ts +1034 -198
  475. package/src/common/line-format.ts +249 -0
  476. package/src/common/lists.ts +6 -1
  477. package/src/common/ontology-export.ts +461 -0
  478. package/src/common/ontology.ts +31 -9
  479. package/src/common/options.ts +110 -15
  480. package/src/common/plural.ts +49 -0
  481. package/src/common/report.ts +168 -25
  482. package/src/common/temporal.ts +22 -5
  483. package/src/common/text.ts +62 -23
  484. package/src/common/weights.ts +74 -4
  485. package/src/common/writer.ts +37 -3
  486. package/src/common/xml.ts +477 -88
  487. package/src/common/zip.ts +169 -7
  488. package/src/formats/csv/exporter.ts +201 -46
  489. package/src/formats/csv/header.ts +50 -11
  490. package/src/formats/csv/importer.ts +1133 -245
  491. package/src/formats/csv/index.ts +106 -19
  492. package/src/formats/csv/records.ts +396 -122
  493. package/src/formats/csv/values.ts +1 -0
  494. package/src/formats/cx/exporter.ts +1483 -0
  495. package/src/formats/cx/importer.ts +888 -300
  496. package/src/formats/cx/index.ts +4 -3
  497. package/src/formats/cx2/exporter.ts +61 -338
  498. package/src/formats/cx2/importer.ts +317 -86
  499. package/src/formats/cx2/index.ts +2 -1
  500. package/src/formats/cys/constants.ts +62 -17
  501. package/src/formats/cys/exporter.ts +1368 -0
  502. package/src/formats/cys/importer.ts +659 -157
  503. package/src/formats/cys/index.ts +4 -2
  504. package/src/formats/cys/session.ts +139 -31
  505. package/src/formats/cys/tables.ts +47 -9
  506. package/src/formats/dot/exporter.ts +83 -37
  507. package/src/formats/dot/importer.ts +248 -54
  508. package/src/formats/dot/index.ts +2 -1
  509. package/src/formats/dot/names.ts +44 -11
  510. package/src/formats/dot/tokenizer.ts +25 -6
  511. package/src/formats/gexf/exporter.ts +57 -18
  512. package/src/formats/gexf/importer.ts +636 -105
  513. package/src/formats/gexf/index.ts +90 -21
  514. package/src/formats/gexf/schema.ts +67 -15
  515. package/src/formats/gml/exporter.ts +90 -36
  516. package/src/formats/gml/importer.ts +388 -67
  517. package/src/formats/gml/index.ts +85 -24
  518. package/src/formats/gml/syntax.ts +111 -26
  519. package/src/formats/graphml/constants.ts +178 -44
  520. package/src/formats/graphml/exporter.ts +52 -18
  521. package/src/formats/graphml/importer.ts +640 -60
  522. package/src/formats/graphml/index.ts +2 -1
  523. package/src/formats/graphml/tree.ts +6 -2
  524. package/src/formats/graphml/yfiles.ts +37 -15
  525. package/src/formats/json/dialect.ts +128 -29
  526. package/src/formats/json/exporter.ts +205 -68
  527. package/src/formats/json/importer.ts +1163 -253
  528. package/src/formats/json/index.ts +3 -1
  529. package/src/formats/json/obographs-export.ts +1197 -0
  530. package/src/formats/json/obographs.ts +353 -92
  531. package/src/formats/neo4j/exporter.ts +320 -101
  532. package/src/formats/neo4j/header.ts +44 -6
  533. package/src/formats/neo4j/importer.ts +512 -73
  534. package/src/formats/neo4j/index.ts +75 -23
  535. package/src/formats/obo/exporter.ts +2210 -0
  536. package/src/formats/obo/importer.ts +622 -109
  537. package/src/formats/obo/index.ts +6 -3
  538. package/src/formats/obo/syntax.ts +232 -37
  539. package/src/formats/pajek/exporter.ts +89 -41
  540. package/src/formats/pajek/importer.ts +407 -93
  541. package/src/formats/pajek/index.ts +2 -1
  542. package/src/formats/pajek/syntax.ts +213 -52
  543. package/src/formats/xgmml/columns.ts +10 -0
  544. package/src/formats/xgmml/constants.ts +183 -56
  545. package/src/formats/xgmml/document.ts +212 -34
  546. package/src/formats/xgmml/emit.ts +175 -34
  547. package/src/formats/xgmml/exporter.ts +35 -27
  548. package/src/formats/xgmml/importer.ts +64 -86
  549. package/src/formats/xgmml/index.ts +1 -0
  550. package/src/formats/xgmml/values.ts +11 -3
  551. package/src/index.ts +81 -11
  552. package/src/registry.ts +1121 -135
  553. package/src/sniff.ts +163 -32
  554. package/src/types.ts +385 -114
  555. package/dist/chunks/escape-B-tkk_Cl.js.map +0 -1
  556. package/dist/chunks/export-Bh60Dv-n.js +0 -582
  557. package/dist/chunks/export-Bh60Dv-n.js.map +0 -1
  558. package/dist/chunks/exporter-BGrsWamJ.js.map +0 -1
  559. package/dist/chunks/exporter-DIeGJXAZ.js.map +0 -1
  560. package/dist/chunks/importer-BWY2FFc5.js.map +0 -1
  561. package/dist/chunks/importer-DD-xv9_Y.js +0 -1471
  562. package/dist/chunks/importer-DD-xv9_Y.js.map +0 -1
  563. package/dist/chunks/importer-DET1tg6s.js.map +0 -1
  564. package/dist/chunks/importer-DEcKhsmQ.js +0 -1614
  565. package/dist/chunks/importer-DEcKhsmQ.js.map +0 -1
  566. package/dist/chunks/importer-DIrbbnAf.js.map +0 -1
  567. package/dist/chunks/importer-D_e7e7LX.js.map +0 -1
  568. package/dist/chunks/importer-eUREhBGj.js.map +0 -1
  569. package/dist/chunks/importer-vi3bSdtw.js.map +0 -1
  570. package/dist/chunks/importer-zGLo8Dg_.js.map +0 -1
  571. package/dist/chunks/json-elements-DDS8N2Dd.js +0 -779
  572. package/dist/chunks/json-elements-DDS8N2Dd.js.map +0 -1
  573. package/dist/chunks/ontology-BnrJ4I98.js +0 -113
  574. package/dist/chunks/ontology-BnrJ4I98.js.map +0 -1
  575. package/dist/chunks/records-dbRkxwaq.js.map +0 -1
  576. package/dist/chunks/report-B1z4WT9e.js +0 -2372
  577. package/dist/chunks/report-B1z4WT9e.js.map +0 -1
  578. package/dist/chunks/text-Dr0Ifpag.js.map +0 -1
  579. package/dist/chunks/weights-Dzba96G3.js +0 -176
  580. package/dist/chunks/weights-Dzba96G3.js.map +0 -1
  581. package/dist/chunks/writer-C7flM-Ih.js.map +0 -1
@@ -20,47 +20,66 @@
20
20
  */
21
21
  import { GraphFormatError, INVALID_INDEX, } from "@graphty/graph-format";
22
22
  import { declareResolved, uniqueColumnName } from "../../common/attributes.js";
23
- import { ASPECT_ORDER_CODE, BAD_ASPECT_BLOCK_CODE, BAD_DEFAULT_CODE, BAD_VALUE_CODE, COLUMN_RENAMED_CODE, COUNT_MISMATCH_CODE, DANGLING_REFERENCE_CODE, DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_EDGE_ID_CODE, DUPLICATE_NODE_CODE, EMPTY_INPUT_CODE, ENCODING_FALLBACK_CODE, ID_MERGED_CODE, ID_TEXT_TYPE_CODE, INVALID_ENCODING_CODE, INVALID_UTF8_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, OPTION_IGNORED_CODE, PRECISION_CODE, ROLE_TAKEN_CODE, SINK_OPTION_CODE, STATUS_FAILED_CODE, STATUS_WARNING_CODE, STYLES_NOT_IMPORTED_CODE, SYNTAX_CODE, TOO_LARGE_CODE, UNKNOWN_ATTR_TYPE_CODE, UNKNOWN_ELEMENT_CODE, UNKNOWN_ENCODING_CODE, } from "../../common/codes.js";
23
+ import { ASPECT_ORDER_CODE, BAD_ASPECT_BLOCK_CODE, BAD_DEFAULT_CODE, BAD_VALUE_CODE, COLUMN_RENAMED_CODE, COUNT_MISMATCH_CODE, DANGLING_REFERENCE_CODE, DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_EDGE_ID_CODE, DUPLICATE_NODE_CODE, ELEMENT_ISSUE, EMPTY_INPUT_CODE, ENCODING_FALLBACK_CODE, ID_MERGED_CODE, ID_TEXT_TYPE_CODE, INPUT_ISSUE, INVALID_ENCODING_CODE, INVALID_UTF8_CODE, JSON_NONSTANDARD_NUMBER_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, MULTI_ASPECT_FRAGMENT_CODE, OPTION_IGNORED_CODE, PRECISION_CODE, ROLE_TAKEN_CODE, SINGLE_OBJECT_ASPECT_CODE, SINK_OPTION_CODE, STATUS_FAILED_CODE, STATUS_WARNING_CODE, STYLES_NOT_IMPORTED_CODE, SYNTAX_CODE, TOO_LARGE_CODE, UNKNOWN_ATTR_TYPE_CODE, UNKNOWN_ELEMENT_CODE, UNKNOWN_ENCODING_CODE, } from "../../common/codes.js";
24
24
  import { DirectionResolver } from "../../common/direction.js";
25
25
  import { IdCoercer } from "../../common/ids.js";
26
26
  import { textChunks, throwIfAborted } from "../../common/input.js";
27
- import { cxId, CxStructure, declareFresh, ExactInteger, flipY, inexactLiteral, isRecord, JsonScanError, plainJson, positionDecl, reportTooDeep, scanAspects, zDecl, } from "../../common/json-elements.js";
27
+ import { cxId, CxStructure, declareFresh, ExactInteger, fitsF32, flipY, headText, inexactLiteral, isRecord, JsonScanError, keptPrecision, plainJson, positionDecl, reportSharedBlock, reportTooDeep, scanAspects, setOwn, zDecl, } from "../../common/json-elements.js";
28
28
  import { reportSinkOptions, reportUnusedOptions, resolveImportOptions, } from "../../common/options.js";
29
+ import { agree, plural } from "../../common/plural.js";
29
30
  import { ImportReportBuilder } from "../../common/report.js";
30
31
  import { weightFromValue } from "../../common/weights.js";
31
- /** The format name. */
32
+ /**
33
+ * The format name.
34
+ * @category Plugin helpers
35
+ */
32
36
  export const CX2_FORMAT = "cx2";
33
- /** The node attribute the exporter's `sanitizeIds: "mangle"` keeps an original id in. */
37
+ /**
38
+ * The node attribute the exporter's `sanitizeIds: "mangle"` keeps an original id in.
39
+ * @category Plugin helpers
40
+ */
34
41
  export const ORIGINAL_ID_ATTRIBUTE = "graphty:originalId";
35
- /** The origin namespace of the per-element visual property columns. */
42
+ /**
43
+ * The origin namespace of the per-element visual property columns.
44
+ * @category Plugin helpers
45
+ */
36
46
  export const BYPASS_NAMESPACE = "cx2.bypass";
37
47
  /**
38
- * The issue codes the CX2 importer records (design section 1.3), by name: the codes shared with
39
- * the other importers (src/common/codes.ts) and the CX2-specific ones. A key is the code without
48
+ * The issue codes the CX2 importer records, by name: the codes shared with
49
+ * the other importers and the CX2-specific ones. A key is the code without
40
50
  * its severity and format prefixes.
51
+ * @category Built-in formats
41
52
  */
42
53
  export const CX2_ISSUE = Object.freeze({
43
- /** The input is empty (fatal). */
54
+ ...INPUT_ISSUE,
55
+ ...ELEMENT_ISSUE,
56
+ /** The input is empty. The import stops. */
44
57
  EMPTY_INPUT: EMPTY_INPUT_CODE,
45
- /** The text is not JSON (fatal). */
58
+ /** The text is not valid JSON. The import stops. */
46
59
  SYNTAX: SYNTAX_CODE,
47
- /** Invalid UTF-8 (fatal). */
60
+ /** The input is not valid UTF-8. The import stops. */
48
61
  INVALID_UTF8: INVALID_UTF8_CODE,
49
- /** Invalid bytes in the encoding a BOM or the encoding option chose (fatal). */
62
+ /**
63
+ * Some bytes are not valid in the encoding that was chosen (by a byte order mark or the `encoding` option). The
64
+ * import stops.
65
+ */
50
66
  INVALID_ENCODING: INVALID_ENCODING_CODE,
51
67
  /** Bytes that are not UTF-8 were read as windows-1252. */
52
68
  ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
53
69
  /** An encoding the platform cannot decode was ignored. */
54
70
  UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
55
- /** The document is not an array or does not start with a descriptor holding CXVersion (fatal). */
71
+ /**
72
+ * The document is not a JSON array, or its first element is not the CX2 descriptor with `CXVersion`. The import
73
+ * stops.
74
+ */
56
75
  NO_DESCRIPTOR: "E_CX2_NO_DESCRIPTOR",
57
- /** CXVersion names a major version other than 2 (fatal); "1.x" says the file is CX1. */
76
+ /** `CXVersion` names a version other than 2 ("1.x" means the file is CX1; read it as `cx`). The import stops. */
58
77
  VERSION: "E_CX2_VERSION",
59
78
  /** CXVersion is a 2.x other than "2.0" (or not a string); the document is read. */
60
79
  MINOR_VERSION: "W_CX2_MINOR_VERSION",
61
80
  /** The document has no status block, or a malformed one. */
62
81
  NO_STATUS: "E_CX2_NO_STATUS",
63
- /** The producer marked the document as failed (fatal). */
82
+ /** The program that wrote the file marked it as failed, so it is incomplete. The import stops. */
64
83
  STATUS_FAILED: STATUS_FAILED_CODE,
65
84
  /** The producer marked the document as successful with an error text. */
66
85
  STATUS_WARNING: STATUS_WARNING_CODE,
@@ -84,9 +103,18 @@ export const CX2_ISSUE = Object.freeze({
84
103
  ALIAS_BYPASSED: "W_CX2_ALIAS_BYPASSED",
85
104
  /** A CX1 cartesianLayout aspect next to node coordinates; kept, not applied. */
86
105
  LEGACY_LAYOUT: "W_CX2_LEGACY_LAYOUT",
87
- /** The file's style rules are not applied (issue #706). */
106
+ /** An aspect CX2 defines as an array of elements written as one object; read as one element. */
107
+ SINGLE_OBJECT_ASPECT: SINGLE_OBJECT_ASPECT_CODE,
108
+ /** A member holding several aspects; each array-valued key is read as its own block. */
109
+ MULTI_ASPECT_FRAGMENT: MULTI_ASPECT_FRAGMENT_CODE,
110
+ /** The bare tokens NaN / Infinity / -Infinity (Python's json writes them), read as numbers. */
111
+ JSON_NONSTANDARD_NUMBER: JSON_NONSTANDARD_NUMBER_CODE,
112
+ /** The file's style rules are not applied; they are kept so a CX2 export writes them back. */
88
113
  STYLES_NOT_IMPORTED: STYLES_NOT_IMPORTED_CODE,
89
- /** A member of the top-level array that is not a one-key aspect block, or an element that is not an object. */
114
+ /**
115
+ * A member of the file's top-level array that is not a block with one key (`{"nodes": [...]}`), or an element of a
116
+ * block that is not an object. It is skipped; the rest of the file is read.
117
+ */
90
118
  BAD_ASPECT_BLOCK: BAD_ASPECT_BLOCK_CODE,
91
119
  /** An aspect out of its place (after the post-metadata or the status, a third metaData, late declarations). */
92
120
  ASPECT_ORDER: ASPECT_ORDER_CODE,
@@ -118,25 +146,37 @@ export const CX2_ISSUE = Object.freeze({
118
146
  INVALID_WEIGHT: "E_INVALID_WEIGHT",
119
147
  /** An id spelled as a string or a non-integer literal; read as the integer. */
120
148
  ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
121
- /** An integer beyond 2^53: an id kept as its digits, a value stored as the nearest f64. */
149
+ /** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
122
150
  PRECISION: PRECISION_CODE,
123
151
  /** Two id texts merged under `ids: "number"`. */
124
152
  ID_MERGED: ID_MERGED_CODE,
125
153
  /** An element key CX2 does not define. */
126
154
  UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
127
- /** A column renamed because its name was taken. */
155
+ /**
156
+ * An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example
157
+ * two attributes declared with the same name.
158
+ */
128
159
  COLUMN_RENAMED: COLUMN_RENAMED_CODE,
129
- /** A column that lost its role because another column holds it. */
160
+ /**
161
+ * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
162
+ * a plain attribute.
163
+ */
130
164
  ROLE_TAKEN: ROLE_TAKEN_CODE,
131
- /** The sink refused the direction. */
165
+ /**
166
+ * You read into a graph builder whose direction is already set, or which already holds edges, so the file is read
167
+ * with the builder's direction instead of its own.
168
+ */
132
169
  DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
133
- /** Edges forced to the policy's direction. */
170
+ /** Edges of the other direction were read with the direction `onMixedDirection` chose. */
134
171
  DIRECTION_FORCED: DIRECTION_FORCED_CODE,
135
- /** A common option CX2 has no use for. */
172
+ /** You set an option this format does not use; it had no effect. The message names the option. */
136
173
  OPTION_IGNORED: OPTION_IGNORED_CODE,
137
- /** A builder option the caller's sink does not honour. */
174
+ /**
175
+ * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
176
+ * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
177
+ */
138
178
  SINK_OPTION: SINK_OPTION_CODE,
139
- /** The input is beyond a size limit (fatal). */
179
+ /** The input is larger than graph-io's size limit. The import stops. */
140
180
  TOO_LARGE: TOO_LARGE_CODE,
141
181
  });
142
182
  const USED_OPTIONS = new Set([
@@ -177,26 +217,38 @@ const STYLE_ASPECTS = [
177
217
  ];
178
218
  /** The aspects the stream structure owns; never kept as opaque. */
179
219
  const STRUCTURE_ASPECTS = new Set(["metaData", "status"]);
220
+ /** The aspects CX2 defines as an array of elements, which a reader expects in that form. */
221
+ const ARRAY_ASPECTS = new Set([
222
+ "nodes",
223
+ "edges",
224
+ "attributeDeclarations",
225
+ "networkAttributes",
226
+ "nodeBypasses",
227
+ "edgeBypasses",
228
+ "status",
229
+ "metaData",
230
+ ]);
180
231
  /** The type spellings CX2 defines, plus the Python spellings ndex2 writes. */
181
- const SCALARS = {
182
- string: "string",
183
- long: "long",
184
- integer: "integer",
185
- double: "double",
186
- boolean: "boolean",
187
- str: "string",
188
- int: "integer",
189
- bool: "boolean",
190
- float: "double",
191
- };
232
+ const SCALARS = new Map([
233
+ ["string", "string"],
234
+ ["long", "long"],
235
+ ["integer", "integer"],
236
+ ["double", "double"],
237
+ ["boolean", "boolean"],
238
+ ["str", "string"],
239
+ ["int", "integer"],
240
+ ["bool", "boolean"],
241
+ ["float", "double"],
242
+ ]);
192
243
  /**
193
244
  * Resolve a declared type text.
194
245
  * @param d - the `d` value
195
246
  * @returns the type, or null when CX2 does not define it
247
+ * @category Plugin helpers
196
248
  */
197
249
  export function cx2Type(d) {
198
250
  const list = d.startsWith("list_of_");
199
- const scalar = SCALARS[list ? d.slice("list_of_".length) : d];
251
+ const scalar = SCALARS.get(list ? d.slice("list_of_".length) : d);
200
252
  if (scalar === undefined) {
201
253
  return null;
202
254
  }
@@ -246,6 +298,7 @@ function convertScalar(value, scalar, long, onPrecision) {
246
298
  return typeof value === "boolean" ? value : BAD;
247
299
  case "double":
248
300
  if (value instanceof ExactInteger) {
301
+ onPrecision(value.digits);
249
302
  return Number(value.digits);
250
303
  }
251
304
  return typeof value === "number" ? value : BAD;
@@ -316,15 +369,16 @@ function shown(value) {
316
369
  * The inexact-literal bits of an element.
317
370
  * @param text - the element's JSON text
318
371
  * @param keys - the id keys to check, bit 1, 2, 4 in order
372
+ * @param depth - the depth of the element's own keys in the text (2: a member's single object)
319
373
  * @returns the bits
320
374
  */
321
- function inexactBits(text, keys) {
375
+ function inexactBits(text, keys, depth = 1) {
322
376
  if (!/[0-9][.eE]/.test(text)) {
323
377
  return 0;
324
378
  }
325
379
  let bits = 0;
326
380
  keys.forEach((key, i) => {
327
- if (inexactLiteral(text, key)) {
381
+ if (inexactLiteral(text, key, depth)) {
328
382
  bits |= 1 << i;
329
383
  }
330
384
  });
@@ -355,22 +409,24 @@ async function readDocument(input, report, options) {
355
409
  let descriptor = false;
356
410
  let elementsSeen = false;
357
411
  let sinceCheck = 0;
358
- const collect = (aspect, value, text, line, exact) => {
412
+ const opaquePrecision = keptPrecision(report, "meta.extra.cx2.opaque");
413
+ const collect = (aspect, value, text, line, exact, depth = 1) => {
359
414
  structure.element(aspect, value, line);
360
415
  switch (aspect) {
361
416
  case "nodes":
362
417
  elementsSeen = true;
363
- doc.nodes.push({ value, line, inexact: inexactBits(text, ["id"]) });
418
+ doc.nodes.push({ value, line, inexact: inexactBits(text, ["id"], depth) });
364
419
  return;
365
420
  case "edges":
366
421
  elementsSeen = true;
367
- doc.edges.push({ value, line, inexact: inexactBits(text, ["id", "s", "t"]) });
422
+ doc.edges.push({ value, line, inexact: inexactBits(text, ["id", "s", "t"], depth) });
368
423
  return;
369
424
  case "attributeDeclarations":
370
425
  if (elementsSeen) {
371
426
  report.warnOnce("validation-error", ASPECT_ORDER_CODE, "attributeDeclarations comes after the elements it types; the declarations are applied to them", { line, element: aspect }, `${ASPECT_ORDER_CODE}:declarations`);
372
427
  }
373
- doc.declarations.push({ value: plainJson(value), line, inexact: 0 });
428
+ // not plainJson(): a long default beyond 2^53 keeps its digits for long: "string"
429
+ doc.declarations.push({ value, line, inexact: 0 });
374
430
  return;
375
431
  case "networkAttributes":
376
432
  elementsSeen = true;
@@ -392,18 +448,18 @@ async function readDocument(input, report, options) {
392
448
  if (!doc.opaque.has(aspect)) {
393
449
  doc.opaque.set(aspect, []);
394
450
  }
395
- doc.opaque.get(aspect)?.push(exact ? plainJson(value, () => undefined) : value);
451
+ doc.opaque.get(aspect)?.push(exact ? plainJson(value, opaquePrecision) : value);
396
452
  }
397
453
  };
398
454
  try {
399
- for await (const event of scanAspects(textChunks(input, report, options))) {
455
+ for await (const event of scanAspects(textChunks(input, report, options), report)) {
400
456
  if (++sinceCheck >= ABORT_CHECK_INTERVAL) {
401
457
  sinceCheck = 0;
402
458
  throwIfAborted(options.signal);
403
459
  }
404
460
  switch (event.kind) {
405
461
  case "root":
406
- report.fail(CX2_ISSUE.NO_DESCRIPTOR, `a CX2 document is a JSON array; found ${isRecord(event.value) ? "an object" : shown(event.value)}`, { line: event.line });
462
+ report.fail(CX2_ISSUE.NO_DESCRIPTOR, `a CX2 document is a JSON array; found ${event.object ? "an object" : shown(event.value)}`, { line: event.line });
407
463
  break;
408
464
  case "member":
409
465
  if (!descriptor) {
@@ -412,10 +468,11 @@ async function readDocument(input, report, options) {
412
468
  break;
413
469
  }
414
470
  memberAspect(event.value, event.line, structure, report, (aspect, value) => {
415
- collect(aspect, value, JSON.stringify(value) ?? "", event.line, event.exact);
471
+ collect(aspect, value, event.text, event.line, event.exact, 2);
416
472
  });
417
473
  break;
418
474
  case "block":
475
+ reportSharedBlock(report, event);
419
476
  if (!descriptor) {
420
477
  report.fail(CX2_ISSUE.NO_DESCRIPTOR, `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)" : ""}`, { line: event.line });
421
478
  }
@@ -431,7 +488,7 @@ async function readDocument(input, report, options) {
431
488
  }
432
489
  break;
433
490
  case "extraKeys":
434
- report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `the "${event.aspect}" block holds more keys (${event.keys.join(", ")}); an aspect block has one key, the others are skipped`, { line: event.line, element: event.aspect });
491
+ report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `the "${event.aspect}" block holds more keys (${event.keys.join(", ")}) whose values are not arrays; an aspect block has one key, the others are skipped`, { line: event.line, element: event.aspect });
435
492
  break;
436
493
  default:
437
494
  break;
@@ -463,7 +520,8 @@ function readDescriptor(value, line, doc, report) {
463
520
  }
464
521
  const raw = value.CXVersion;
465
522
  const text = typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
466
- const match = /^([0-9]+)(?:\.([0-9]+))?$/.exec(text);
523
+ // the major version: "2.0", "2.1", "2.0.1", "2.1-beta", " 2.0" are all CX2
524
+ const match = /^\s*(\d+)(?=$|[.\s-])/.exec(text);
467
525
  const major = match === null ? NaN : Number(match[1]);
468
526
  if (major === 1) {
469
527
  report.fail(CX2_ISSUE.VERSION, `CXVersion ${shown(raw)} is CX version 1, a different document shape; read it with the cx importer`, { line, element: "CXVersion" });
@@ -504,6 +562,9 @@ function readDescriptor(value, line, doc, report) {
504
562
  function memberAspect(value, line, structure, report, collect) {
505
563
  const keys = isRecord(value) ? Object.keys(value) : [];
506
564
  if (isRecord(value) && keys.length === 1 && isRecord(value[keys[0]])) {
565
+ if (ARRAY_ASPECTS.has(keys[0])) {
566
+ report.warning("validation-error", CX2_ISSUE.SINGLE_OBJECT_ASPECT, `the "${keys[0]}" aspect is one object, not an array of elements; read as one element`, { line, element: keys[0] });
567
+ }
507
568
  structure.block(keys[0], line);
508
569
  collect(keys[0], value[keys[0]]);
509
570
  return;
@@ -596,7 +657,20 @@ class DeclarationTable {
596
657
  }
597
658
  }
598
659
  /** The domain key of attributeDeclarations for each table. */
599
- const DECLARATION_KEYS = { nodes: "node", edges: "edge", networkAttributes: "network" };
660
+ const DECLARATION_KEYS = new Map([
661
+ ["nodes", "node"],
662
+ ["edges", "edge"],
663
+ ["networkAttributes", "network"],
664
+ ]);
665
+ /**
666
+ * Whether an object has an own property (never one of Object.prototype, as `in` would find).
667
+ * @param record - the object
668
+ * @param key - the key
669
+ * @returns true for an own property
670
+ */
671
+ function hasOwn(record, key) {
672
+ return Object.hasOwn(record, key);
673
+ }
600
674
  // ============================================================ the build
601
675
  /** Reads a collected document into a sink. */
602
676
  class Cx2Reader {
@@ -619,6 +693,8 @@ class Cx2Reader {
619
693
  this.nodeRows = new Map();
620
694
  /** CX node id -> the id the sink holds it under (differs when a mangled id is restored). */
621
695
  this.sinkIds = new Map();
696
+ /** The original ids restored so far: a second node with one keeps its CX id. */
697
+ this.restored = new Set();
622
698
  /** CX edge id -> sink edge index. */
623
699
  this.edgeRows = new Map();
624
700
  this.positionHandle = INVALID_INDEX;
@@ -651,7 +727,7 @@ class Cx2Reader {
651
727
  }
652
728
  this.readLegacyLayout();
653
729
  if (this.positioned.size > 0 && this.positioned.size < this.nodeRows.size) {
654
- report.warnOnce("missing-value", CX2_ISSUE.PARTIAL_LAYOUT, `${this.nodeRows.size - this.positioned.size} of ${this.nodeRows.size} node(s) have no coordinates; their positions are unset`, { element: "nodes" });
730
+ report.warnOnce("missing-value", CX2_ISSUE.PARTIAL_LAYOUT, `${this.nodeRows.size - this.positioned.size} of ${this.nodeRows.size} node${plural(this.nodeRows.size)} ${agree(this.nodeRows.size - this.positioned.size, "has", "have")} no coordinates; their positions are unset`, { element: "nodes" });
655
731
  }
656
732
  for (const held of doc.edges) {
657
733
  this.readEdge(held);
@@ -660,7 +736,7 @@ class Cx2Reader {
660
736
  this.readBypasses("node", doc.nodeBypasses);
661
737
  this.readBypasses("edge", doc.edgeBypasses);
662
738
  for (const [kind, count] of this.dangling) {
663
- report.warning("validation-error", DANGLING_REFERENCE_CODE, `${count} ${kind} name(s) no element; ignored`, { element: kind });
739
+ report.warning("validation-error", DANGLING_REFERENCE_CODE, `${count} ${kind}${plural(count)} ${agree(count, "names", "name")} no element; ignored`, { element: kind });
664
740
  }
665
741
  this.checkAspects();
666
742
  this.setMeta();
@@ -678,9 +754,12 @@ class Cx2Reader {
678
754
  continue;
679
755
  }
680
756
  for (const [key, table] of Object.entries(held.value)) {
681
- const domain = DECLARATION_KEYS[key];
757
+ const domain = DECLARATION_KEYS.get(key);
682
758
  if (domain === undefined) {
683
- this.decls.other[key] = table;
759
+ report.warning("unsupported", UNKNOWN_ELEMENT_CODE, `attributeDeclarations holds the table "${key}", which CX2 does not define; kept in meta.extra.cx2.declarations, not applied`, { line: held.line, element: key });
760
+ setOwn(this.decls.other, key, plainJson(table, (digits) => {
761
+ this.precision(key, digits);
762
+ }));
684
763
  continue;
685
764
  }
686
765
  if (!isRecord(table)) {
@@ -713,7 +792,10 @@ class Cx2Reader {
713
792
  }
714
793
  for (const [key, raw] of Object.entries(value.v)) {
715
794
  const resolved = table.resolve(key);
716
- if ((resolved !== null && resolved.decl.type !== null) || raw === null || raw === undefined) {
795
+ if (key === "" ||
796
+ (resolved !== null && resolved.decl.type !== null) ||
797
+ raw === null ||
798
+ raw === undefined) {
717
799
  continue;
718
800
  }
719
801
  const name = resolved?.decl.name ?? key;
@@ -760,6 +842,10 @@ class Cx2Reader {
760
842
  const { report } = this;
761
843
  const table = this.decls[domain];
762
844
  const where = { line, element: name };
845
+ if (name === "") {
846
+ report.error("validation-error", BAD_VALUE_CODE, `a ${domain} attribute declaration has an empty name; ignored`, where);
847
+ return;
848
+ }
763
849
  if (!isRecord(raw)) {
764
850
  report.error("validation-error", BAD_VALUE_CODE, `the declaration of "${name}" is not an object; ignored`, where);
765
851
  return;
@@ -782,6 +868,9 @@ class Cx2Reader {
782
868
  }
783
869
  return;
784
870
  }
871
+ if (raw.a !== undefined && raw.a !== null && typeof raw.a !== "string") {
872
+ report.error("validation-error", BAD_VALUE_CODE, `the alias ${shown(raw.a)} of "${name}" is not a string; ignored`, where);
873
+ }
785
874
  let alias = typeof raw.a === "string" ? raw.a : null;
786
875
  let hasDefault = raw.v !== undefined && raw.v !== null;
787
876
  if (domain === "network" && (raw.a !== undefined || raw.v !== undefined)) {
@@ -817,7 +906,9 @@ class Cx2Reader {
817
906
  * @returns the default, or BAD when dropped
818
907
  */
819
908
  convertDefault(name, value, type, where) {
820
- const converted = convertValue(value, type, this.options.long, () => undefined);
909
+ const converted = convertValue(value, type, this.options.long, (digits) => {
910
+ this.precision(name, digits);
911
+ });
821
912
  if (converted !== BAD) {
822
913
  return converted;
823
914
  }
@@ -922,28 +1013,29 @@ class Cx2Reader {
922
1013
  * @param raw - the parsed id
923
1014
  * @param inexact - whether the literal was not a plain integer
924
1015
  * @param element - the element name for issues
1016
+ * @param line - the element's line
925
1017
  * @param edgeId - whether this is an edge's own id (stored in the f64 id column, not kept as digits)
926
1018
  * @returns the id, or null when it was reported
927
1019
  */
928
- idOf(raw, inexact, element, edgeId = false) {
1020
+ idOf(raw, inexact, element, line, edgeId = false) {
929
1021
  let id;
930
1022
  try {
931
1023
  const parsed = cxId(raw, inexact);
932
1024
  if (parsed.note === "text") {
933
- this.report.warnOnce("coercion", ID_TEXT_TYPE_CODE, `${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`, { element });
1025
+ this.report.warnOnce("coercion", ID_TEXT_TYPE_CODE, `${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`, { line, element });
934
1026
  }
935
1027
  else if (parsed.note === "precision" && edgeId) {
936
1028
  // the edge is told apart by its digits, but the id column is f64
937
- this.report.warnOnce("precision", PRECISION_CODE, `${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`, { element }, `${PRECISION_CODE}:edge`);
1029
+ this.report.warnOnce("precision", PRECISION_CODE, `${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`, { line, element }, `${PRECISION_CODE}:edge`);
938
1030
  }
939
1031
  else if (parsed.note === "precision") {
940
1032
  this.idType = "mixed";
941
- this.report.warnOnce("precision", PRECISION_CODE, `${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`, { element });
1033
+ this.report.warnOnce("precision", PRECISION_CODE, `${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`, { line, element });
942
1034
  }
943
1035
  id = this.options.ids === "keep" ? parsed.id : this.coercer.value(parsed.id);
944
1036
  }
945
1037
  catch (err) {
946
- this.report.recordError(err, { element });
1038
+ this.report.recordError(err, { line, element });
947
1039
  return null;
948
1040
  }
949
1041
  return id;
@@ -995,7 +1087,7 @@ class Cx2Reader {
995
1087
  return;
996
1088
  }
997
1089
  const element = `node ${shown(value.id)}`;
998
- const cx = this.idOf(value.id, (held.inexact & 1) !== 0, element);
1090
+ const cx = this.idOf(value.id, (held.inexact & 1) !== 0, element, line);
999
1091
  if (cx === null) {
1000
1092
  report.counts.skippedNodes++;
1001
1093
  return;
@@ -1012,7 +1104,14 @@ class Cx2Reader {
1012
1104
  report.warning("merged", DUPLICATE_NODE_CODE, `${element} is declared more than once; its attributes are merged (the later values win)`, { line, element });
1013
1105
  }
1014
1106
  else {
1015
- const original = this.originalIdOf(attrs);
1107
+ let original = this.originalIdOf(attrs, element, line);
1108
+ if (original !== null && this.restored.has(original)) {
1109
+ report.warning("merged", DUPLICATE_NODE_CODE, `${element}: its original id ${shown(original)} is already another node's; it keeps its CX id`, { line, element });
1110
+ original = null;
1111
+ }
1112
+ if (original !== null) {
1113
+ this.restored.add(original);
1114
+ }
1016
1115
  this.idTypeOf(original ?? cx);
1017
1116
  try {
1018
1117
  row = sink.addNode(original ?? cx);
@@ -1034,14 +1133,19 @@ class Cx2Reader {
1034
1133
  /**
1035
1134
  * The original id the exporter's `sanitizeIds: "mangle"` kept, when it is to be restored.
1036
1135
  * @param attrs - the node's v
1136
+ * @param element - the element name for issues
1137
+ * @param line - its line
1037
1138
  * @returns the original id, or null
1038
1139
  */
1039
- originalIdOf(attrs) {
1140
+ originalIdOf(attrs, element, line) {
1040
1141
  if (attrs === null || !this.options.restoreMangledIds) {
1041
1142
  return null;
1042
1143
  }
1043
1144
  const alias = this.decls.node.byName.get(ORIGINAL_ID_ATTRIBUTE)?.alias ?? null;
1044
1145
  const raw = attrs[ORIGINAL_ID_ATTRIBUTE] ?? (alias === null ? undefined : attrs[alias]);
1146
+ if (raw !== undefined && raw !== null && typeof raw !== "string") {
1147
+ this.report.error("validation-error", BAD_VALUE_CODE, `${element}: ${ORIGINAL_ID_ATTRIBUTE} is ${shown(raw)}, not a string; the node keeps its CX id`, { line, element });
1148
+ }
1045
1149
  return typeof raw === "string" ? raw : null;
1046
1150
  }
1047
1151
  /**
@@ -1085,7 +1189,10 @@ class Cx2Reader {
1085
1189
  // the specification requires x and y with z
1086
1190
  this.report.warnOnce("missing-value", CX2_ISSUE.PARTIAL_LAYOUT, `${element} has z without x and y; ${this.zAs === "column" ? "z is kept in the z column" : "with no position, z is dropped"}`, { line, element }, `${CX2_ISSUE.PARTIAL_LAYOUT}:z`);
1087
1191
  }
1088
- if (x !== null && y !== null) {
1192
+ if (x !== null && y !== null && (!fitsF32(x) || !fitsF32(y))) {
1193
+ this.report.error("validation-error", BAD_VALUE_CODE, `${element}: the coordinates ${String(x)}, ${String(y)} are beyond what the f32 position column holds; the position is unset`, { line, element });
1194
+ }
1195
+ else if (x !== null && y !== null) {
1089
1196
  this.setPosition(row, x, y, z);
1090
1197
  }
1091
1198
  if (z !== null && this.zAs === "column") {
@@ -1120,9 +1227,12 @@ class Cx2Reader {
1120
1227
  }
1121
1228
  if (this.positioned.size > 0) {
1122
1229
  this.doc.opaque.set("cartesianLayout", cartesianLayout.map((h) => plainJson(h.value)));
1123
- this.report.warning("unsupported", CX2_ISSUE.LEGACY_LAYOUT, `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`, { element: "cartesianLayout" });
1230
+ this.report.warning("unsupported", CX2_ISSUE.LEGACY_LAYOUT, `a CX1 cartesianLayout aspect (${cartesianLayout.length} element${plural(cartesianLayout.length)}) next to node coordinates; the node coordinates are used, the aspect is kept in meta.extra.cx2`, { element: "cartesianLayout" });
1124
1231
  return;
1125
1232
  }
1233
+ // the first view named is the layout; a CX1 file with several views keeps the others
1234
+ let view;
1235
+ const seen = new Set();
1126
1236
  for (const held of cartesianLayout) {
1127
1237
  const entry = held.value;
1128
1238
  if (!isRecord(entry) || typeof entry.x !== "number" || typeof entry.y !== "number") {
@@ -1134,6 +1244,16 @@ class Cx2Reader {
1134
1244
  this.dangle("cartesianLayout entry");
1135
1245
  continue;
1136
1246
  }
1247
+ view ?? (view = entry.view ?? null);
1248
+ if ((entry.view ?? null) !== view) {
1249
+ this.doc.opaque.set("cartesianLayout", cartesianLayout.map((h) => plainJson(h.value)));
1250
+ this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `the cartesianLayout aspect names several views; view ${shown(view)} is read, the aspect is kept in meta.extra.cx2`, { line: held.line, element: "cartesianLayout" }, `${DUPLICATE_ATTRIBUTE_CODE}:layout:view`);
1251
+ continue;
1252
+ }
1253
+ if (seen.has(row)) {
1254
+ this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `node ${shown(entry.node)} has two cartesianLayout entries; the later wins`, { line: held.line, element: "cartesianLayout" }, `${DUPLICATE_ATTRIBUTE_CODE}:layout`);
1255
+ }
1256
+ seen.add(row);
1137
1257
  const z = typeof entry.z === "number" ? entry.z : null;
1138
1258
  this.setPosition(row, entry.x, entry.y, z);
1139
1259
  if (z !== null && this.zAs === "column") {
@@ -1180,6 +1300,10 @@ class Cx2Reader {
1180
1300
  const table = this.decls[domain];
1181
1301
  for (const key of Object.keys(attrs)) {
1182
1302
  const value = attrs[key];
1303
+ if (key === "") {
1304
+ report.error("validation-error", BAD_VALUE_CODE, `${element}: v holds an attribute with an empty name; skipped`, { line, element });
1305
+ continue;
1306
+ }
1183
1307
  const resolved = table.resolve(key);
1184
1308
  const name = resolved?.decl.name ?? key;
1185
1309
  if (domain === "node" && this.options.restoreMangledIds && name === ORIGINAL_ID_ATTRIBUTE) {
@@ -1195,7 +1319,7 @@ class Cx2Reader {
1195
1319
  !resolved.viaAlias &&
1196
1320
  resolved.decl.alias !== null &&
1197
1321
  resolved.decl.alias !== key) {
1198
- if (resolved.decl.alias in attrs) {
1322
+ if (hasOwn(attrs, resolved.decl.alias)) {
1199
1323
  report.warning("validation-error", DUPLICATE_ATTRIBUTE_CODE, `${element}: "${name}" is given under its alias and its full name; the alias wins`, { line, element });
1200
1324
  continue;
1201
1325
  }
@@ -1274,9 +1398,9 @@ class Cx2Reader {
1274
1398
  return;
1275
1399
  }
1276
1400
  }
1277
- const id = this.idOf(value.id, (held.inexact & 1) !== 0, element, true);
1278
- const s = id === null ? null : this.idOf(value.s, (held.inexact & 2) !== 0, element);
1279
- const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element);
1401
+ const id = this.idOf(value.id, (held.inexact & 1) !== 0, element, line, true);
1402
+ const s = id === null ? null : this.idOf(value.s, (held.inexact & 2) !== 0, element, line);
1403
+ const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element, line);
1280
1404
  if (id === null || s === null || t === null) {
1281
1405
  report.counts.skippedEdges++;
1282
1406
  return;
@@ -1293,6 +1417,12 @@ class Cx2Reader {
1293
1417
  return;
1294
1418
  }
1295
1419
  const attrs = isRecord(value.v) ? value.v : null;
1420
+ if (value.v !== undefined && value.v !== null && attrs === null) {
1421
+ report.error("validation-error", BAD_VALUE_CODE, `${element}: v is ${shown(value.v)}, not an object`, {
1422
+ line,
1423
+ element,
1424
+ });
1425
+ }
1296
1426
  let weight;
1297
1427
  const weightKey = this.weightKey(attrs);
1298
1428
  try {
@@ -1334,10 +1464,10 @@ class Cx2Reader {
1334
1464
  return null;
1335
1465
  }
1336
1466
  const alias = this.decls.edge.byName.get(weightFrom)?.alias ?? null;
1337
- if (alias !== null && alias in attrs) {
1467
+ if (alias !== null && hasOwn(attrs, alias)) {
1338
1468
  return alias;
1339
1469
  }
1340
- return weightFrom in attrs ? weightFrom : null;
1470
+ return hasOwn(attrs, weightFrom) ? weightFrom : null;
1341
1471
  }
1342
1472
  /**
1343
1473
  * The sink id of an edge endpoint: a known node, or a node created under addMissingNodes.
@@ -1455,13 +1585,27 @@ class Cx2Reader {
1455
1585
  if (raw === null || raw === undefined) {
1456
1586
  continue;
1457
1587
  }
1588
+ if (property === "") {
1589
+ this.report.error("validation-error", BAD_VALUE_CODE, `a ${domain}Bypasses element holds a visual property with an empty name; skipped`, { line, element: `${domain}Bypasses` });
1590
+ continue;
1591
+ }
1458
1592
  let column = columns.get(property);
1459
1593
  if (column === undefined) {
1460
- column = { rows: [], values: [] };
1594
+ column = { rows: [], values: [], at: new Map() };
1461
1595
  columns.set(property, column);
1462
1596
  }
1463
- column.rows.push(row);
1464
- column.values.push(plainJson(raw));
1597
+ const plain = plainJson(raw);
1598
+ const index = column.at.get(row);
1599
+ if (index === undefined) {
1600
+ column.at.set(row, column.rows.length);
1601
+ column.rows.push(row);
1602
+ column.values.push(plain);
1603
+ continue;
1604
+ }
1605
+ if (JSON.stringify(column.values[index]) !== JSON.stringify(plain)) {
1606
+ this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `a ${domain} has the visual property "${property}" in two ${domain}Bypasses elements; the later wins`, { line, element: property }, `${DUPLICATE_ATTRIBUTE_CODE}:bypass:${domain}:${property}`);
1607
+ }
1608
+ column.values[index] = plain;
1465
1609
  }
1466
1610
  }
1467
1611
  for (const [property, { rows: targets, values }] of columns) {
@@ -1496,7 +1640,7 @@ class Cx2Reader {
1496
1640
  }
1497
1641
  }
1498
1642
  if (styles.length > 0) {
1499
- report.warning("unsupported", STYLES_NOT_IMPORTED_CODE, `the file's style rules are not applied (${styles.join("; ")}); they are kept in meta.extra.cx2.opaque (style import is issue #706)`, { element: STYLE_ASPECTS.filter((a) => doc.opaque.has(a)).join(",") });
1643
+ report.warning("unsupported", STYLES_NOT_IMPORTED_CODE, `the file's style rules are not applied (${styles.join("; ")}); they are kept in meta.extra.cx2.opaque`, { element: STYLE_ASPECTS.filter((a) => doc.opaque.has(a)).join(",") });
1500
1644
  }
1501
1645
  for (const aspect of SINGLE_ELEMENT_ASPECTS.slice(1)) {
1502
1646
  const count = doc.opaque.get(aspect)?.length ?? 0;
@@ -1590,14 +1734,15 @@ function describeStyle(aspect, elements) {
1590
1734
  const defaults = isRecord(first.default)
1591
1735
  ? Object.values(first.default).reduce((n, v) => n + (isRecord(v) ? Object.keys(v).length : 0), 0)
1592
1736
  : 0;
1593
- return `visualProperties: ${defaults} default(s), ${count("nodeMapping")} node mapping(s), ${count("edgeMapping")} edge mapping(s)`;
1737
+ return `visualProperties: ${defaults} default${plural(defaults)}, ${count("nodeMapping")} node mapping${plural(count("nodeMapping"))}, ${count("edgeMapping")} edge mapping${plural(count("edgeMapping"))}`;
1594
1738
  }
1595
- return `${aspect}: ${elements.length} element(s)`;
1739
+ return `${aspect}: ${elements.length} element${plural(elements.length)}`;
1596
1740
  }
1597
1741
  /**
1598
1742
  * Resolve the zAs option.
1599
1743
  * @param value - the option
1600
1744
  * @returns the mode; E_UNSUPPORTED for any other value
1745
+ * @category Plugin helpers
1601
1746
  */
1602
1747
  export function zAsOption(value) {
1603
1748
  if (value === undefined) {
@@ -1612,10 +1757,12 @@ export function zAsOption(value) {
1612
1757
  });
1613
1758
  }
1614
1759
  /**
1615
- * The CX2 importer plugin (design section 1.3).
1760
+ * The CX2 importer plugin.
1761
+ * @category Built-in formats
1616
1762
  */
1617
1763
  export const cx2Importer = Object.freeze({
1618
1764
  format: CX2_FORMAT,
1765
+ options: Object.freeze(["zAs"]),
1619
1766
  extensions: Object.freeze([".cx2"]),
1620
1767
  mimeTypes: Object.freeze(["application/json"]),
1621
1768
  /**
@@ -1625,7 +1772,7 @@ export const cx2Importer = Object.freeze({
1625
1772
  * @returns the confidence
1626
1773
  */
1627
1774
  sniff(head) {
1628
- const text = new TextDecoder("utf-8").decode(head.subarray(0, 1024)).replace(/^\uFEFF/, "");
1775
+ const text = headText(head);
1629
1776
  return /^\s*\[\s*\{(?:\s*"[^"]*"\s*:\s*(?:true|false|"[^"]*"|[0-9.]+)\s*,)*\s*"CXVersion"\s*:/.test(text)
1630
1777
  ? 0.97
1631
1778
  : 0;