@graphty/graph-io 0.3.25 → 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
@@ -1,26 +1,986 @@
1
- import { 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, N as NO_GRAPH_CODE, A as AMBIGUOUS_GRAPH_NAME_CODE, G as GRAPH_NOT_FOUND_CODE, M as MULTIPLE_GRAPHS_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, g as STYLES_NOT_IMPORTED_CODE, h as PARENT_CYCLE_CODE, U as UNKNOWN_PARENT_CODE, i as DUPLICATE_ATTRIBUTE_CODE, j as DANGLING_REFERENCE_CODE, k as UNKNOWN_ATTR_TYPE_CODE, W as WIDENED_CODE, B as BAD_VALUE_CODE, l as COUNT_MISMATCH_CODE, m as ASPECT_ORDER_CODE, n as BAD_ASPECT_BLOCK_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, x as textChunks, y as throwIfAborted, z as reportUnusedOptions, F as reportSinkOptions, H as chooseGraph, 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, E as ExactInteger, c as cxId, f as flipY, z as zDecl, a as positionDecl, d as declareFresh, b as inexactLiteral } from "./json-elements-DDS8N2Dd.js";
4
- import { w as weightFromValue } from "./weights-Dzba96G3.js";
5
- import { z as zAsOption } from "./importer-vi3bSdtw.js";
1
+ import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
2
+ import { c as childrenCsr } from "./children-CL3Cy0ez.js";
3
+ import { s as stringify, f as aspectBlock, d as directionNotes, p as planNodeIds, b as planEdgeIds, O as ORIGINAL_ID_ATTRIBUTE, h as nextNodeId, i as noteList, g as declaredType, z as zAsOption } from "./importer-DfVu6j59.js";
4
+ import { c as capabilities, L as LOSS, r as resolveExportOptions, p as pairFolding, b as plural, a as checkCapabilities, e as explicitWeights, ad as TOO_LARGE_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, R as ROLE_TAKEN_CODE, C as COLUMN_RENAMED_CODE, N as NO_GRAPH_CODE, ae as AMBIGUOUS_GRAPH_NAME_CODE, af as GRAPH_NOT_FOUND_CODE, l as MULTIPLE_GRAPHS_CODE, I as ID_MERGED_CODE, P as PRECISION_CODE, J as ID_TEXT_TYPE_CODE, a2 as JSON_NONSTANDARD_NUMBER_CODE, ag as SINGLE_OBJECT_ASPECT_CODE, W as MULTI_ASPECT_FRAGMENT_CODE, g as UNKNOWN_ELEMENT_CODE, m as DUPLICATE_EDGE_ID_CODE, n as DUPLICATE_NODE_CODE, s as MISSING_ENDPOINT_CODE, t as MISSING_ID_CODE, ah as STYLES_NOT_IMPORTED_CODE, ai as PARENT_CYCLE_CODE, q as UNKNOWN_PARENT_CODE, o as DUPLICATE_ATTRIBUTE_CODE, i as DANGLING_REFERENCE_CODE, U as UNKNOWN_ATTR_TYPE_CODE, aj as WIDENED_CODE, a3 as BAD_VALUE_CODE, j as COUNT_MISMATCH_CODE, Y as ASPECT_ORDER_CODE, V as BAD_ASPECT_BLOCK_CODE, _ as STATUS_WARNING_CODE, Z as STATUS_FAILED_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, ak as SYNTAX_CODE, E as EMPTY_INPUT_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE, al as resolveImportOptions, a8 as ImportReportBuilder, a4 as textChunks, am as throwIfAborted, an as reportUnusedOptions, ao as reportSinkOptions, a7 as chooseGraph, ap as graphChosen, aq as DirectionResolver, ar as IdCoercer, $ as agree, a1 as declareResolved, a0 as uniqueColumnName, as as weightFromValue } from "./export-BTI4yQgy.js";
5
+ import { i as isRecord, P as POSITION_COLUMN, h as headText, s as scanAspects, r as reportTooDeep, a as reportSharedBlock, C as CxStructure, J as JsonScanError, E as ExactInteger, c as cxId, p as plainJson, f as fitsF32, b as flipY, z as zDecl, d as positionDecl, e as declareFresh, g as inexactLiteral, k as keptPrecision } from "./json-elements-3tnyt5uZ.js";
6
+ import { j as joinText, e as encodeChunks } from "./writer-DxjgHwSe.js";
7
+ const CX_FORMAT$1 = "cx";
8
+ const CX_LOSS = Object.freeze({
9
+ /**
10
+ * Every edge is written as directed, so an undirected graph, or the undirected edges of a mixed graph, read back
11
+ * as directed.
12
+ */
13
+ UNDIRECTED_AS_DIRECTED: "W_CX_UNDIRECTED_AS_DIRECTED",
14
+ /** A nested (json) column is written as a string attribute holding its JSON text. */
15
+ JSON_AS_STRING: "W_CX_JSON_AS_STRING",
16
+ /** A mutual pair is written as two directed edges without its mark. */
17
+ MUTUAL_EXPANDED: LOSS.MUTUAL_EXPANDED,
18
+ /**
19
+ * An attribute named `weight` without the weight role reads back as the edge weight, or is not written when the
20
+ * graph has weights of its own.
21
+ */
22
+ WEIGHT_KEY_CLASH: LOSS.WEIGHT_KEY_CLASH,
23
+ /** A start / end / timestamp column: CX has no time. */
24
+ TEMPORAL_DROPPED: LOSS.TEMPORAL,
25
+ /** An attribute with a role the format has no place for is written as a plain attribute; the role is lost. */
26
+ ROLE_DROPPED: LOSS.ROLE,
27
+ /**
28
+ * An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and
29
+ * reads back with that role.
30
+ */
31
+ ROLE_ASSUMED: LOSS.ROLE_ASSUMED,
32
+ /**
33
+ * An attribute type CX stores as another (a 32-bit float as double, an unsigned integer as long, a byte as
34
+ * integer, a dictionary as string); it reads back with that type.
35
+ */
36
+ DTYPE_UNSUPPORTED: LOSS.DTYPE,
37
+ /** A multi-component column (a second view's `position@2`, a vector) is written as a list of doubles. */
38
+ COMPONENTS_FLATTENED: LOSS.COMPONENTS,
39
+ /** A declared default: CX has none. */
40
+ DEFAULT_DROPPED: LOSS.DEFAULT,
41
+ /** Declared options: CX has no enumerations. */
42
+ OPTIONS_DROPPED: LOSS.OPTIONS,
43
+ /**
44
+ * Node ids the format cannot write, under `sanitizeIds: "error"`; the save fails with E_INVALID_ID. Pass
45
+ * `sanitizeIds: "mangle"` to rewrite them.
46
+ */
47
+ ID_CHARSET: LOSS.ID_CHARSET,
48
+ /** Node ids that are not integers under sanitizeIds "mangle": renumbered, originals kept. */
49
+ ID_MANGLED: LOSS.ID_MANGLED,
50
+ /** Edges without a usable integer id get generated ids. */
51
+ EDGE_IDS_GENERATED: LOSS.EDGE_IDS_GENERATED,
52
+ /**
53
+ * An attribute with a role (for example, the label) is written where the format keeps that role, and reads back under the
54
+ * name the format's importer gives it.
55
+ */
56
+ COLUMN_NAME_CHANGED: LOSS.COLUMN_NAME_CHANGED,
57
+ /** An extension table other than the `cx:citations` / `cx:supports` tables a CX import creates. */
58
+ EXTENSION_TABLE_DROPPED: LOSS.EXTENSION_TABLE,
59
+ /** A position, stacking order or weight that is NaN or infinite: CX spells no such number there. */
60
+ NONFINITE_AS_NULL: LOSS.NONFINITE_AS_NULL,
61
+ /** Visual columns (color, size, shape, thickness roles): CX keeps style as visual properties, not roles. */
62
+ VIZ_DROPPED: LOSS.VIZ
63
+ });
64
+ const CX_CAPABILITIES = capabilities({
65
+ mixedDirection: false,
66
+ multiEdges: true,
67
+ selfLoops: true,
68
+ edgeIds: "required",
69
+ idCharset: "integer",
70
+ dtypes: ["string", "f64", "i32", "bool"],
71
+ components: false,
72
+ lists: true,
73
+ json: false,
74
+ defaults: false,
75
+ options: false,
76
+ hierarchy: true,
77
+ temporal: "none",
78
+ graphAttributes: true,
79
+ positions: true,
80
+ viz: false
81
+ });
82
+ const SLOT_ROLES = /* @__PURE__ */ new Set(["label", "position", "id"]);
83
+ const ROLE_NAMES = Object.freeze({
84
+ label: "name",
85
+ position: POSITION_COLUMN,
86
+ id: "id",
87
+ parent: "parent"
88
+ });
89
+ const SKIPPED_ROLES = /* @__PURE__ */ new Set([
90
+ "directed",
91
+ "pair",
92
+ "mutual",
93
+ "weight",
94
+ "timeText",
95
+ "originalId",
96
+ "parent",
97
+ "parents",
98
+ "start",
99
+ "end",
100
+ "timestamp",
101
+ "timestamps",
102
+ "spells",
103
+ "open",
104
+ "spellsOpen",
105
+ "position",
106
+ "color",
107
+ "size",
108
+ "shape",
109
+ "thickness",
110
+ "id"
111
+ ]);
112
+ const BYPASS_NAMESPACE = "cx.bypass";
113
+ const PROVENANCE_TABLES = Object.freeze({
114
+ "cx:citations": "citations",
115
+ "cx:supports": "supports"
116
+ });
117
+ const NEVER_WRITTEN = /* @__PURE__ */ new Set([
118
+ "nodes",
119
+ "edges",
120
+ "nodeAttributes",
121
+ "edgeAttributes",
122
+ "networkAttributes",
123
+ "cartesianLayout",
124
+ "cyGroups",
125
+ "cyVisualProperties",
126
+ "cyTableColumn",
127
+ "cySubNetworks",
128
+ "cyNetworkRelations",
129
+ "cyViews",
130
+ "CX Element ID",
131
+ "citations",
132
+ "supports",
133
+ "nodeCitations",
134
+ "edgeCitations",
135
+ "nodeSupports",
136
+ "edgeSupports",
137
+ "functionTerms",
138
+ "reifiedEdges",
139
+ "numberVerification",
140
+ "metaData",
141
+ "status",
142
+ "groups",
143
+ "subnetwork"
144
+ ]);
145
+ const WEIGHT_ATTRIBUTE = "weight";
146
+ const NUMERIC_DTYPES = /* @__PURE__ */ new Set(["f32", "f64", "i32", "u32", "u8"]);
147
+ function fromCx(column, namespace) {
148
+ return column.meta.origin?.format === CX_FORMAT$1 && column.meta.origin.namespace === namespace;
149
+ }
150
+ function isBypass(column) {
151
+ return column.meta.origin?.namespace === BYPASS_NAMESPACE;
152
+ }
153
+ function isZ(column) {
154
+ return column.meta.extra.cytoscape === "z";
155
+ }
156
+ function isLink(column) {
157
+ return fromCx(column, "provenance") && column.meta.dtype === "list" && (column.meta.name === "citations" || column.meta.name === "supports");
158
+ }
159
+ function coreNeedsText(column) {
160
+ const { dtype } = column.meta;
161
+ return dtype !== "string" && dtype !== "dict" && dtype !== "json";
162
+ }
163
+ function genericNotes(snapshot, common, several, own, notes, note) {
164
+ let fatal = null;
165
+ const edgeLabel = snapshot.edges.byRole("label")?.meta.name;
166
+ const ownNames = new Set(own.filter((c) => c !== null).map((c) => c.meta.name));
167
+ for (const gen of checkCapabilities(snapshot, CX_CAPABILITIES, common, {
168
+ roles: SLOT_ROLES,
169
+ // a parents column reads back as parents only when some node has several parents
170
+ roleNames: several ? { ...ROLE_NAMES, parents: "parents" } : ROLE_NAMES
171
+ })) {
172
+ if (writtenOtherwise(gen, ownNames)) {
173
+ continue;
174
+ }
175
+ if (gen.code === LOSS.JSON) {
176
+ const which = gen.column === null ? "a column" : `column "${gen.column}"`;
177
+ note(
178
+ CX_LOSS.JSON_AS_STRING,
179
+ `${which} holds nested values; written as a string attribute holding their JSON text`,
180
+ gen.column,
181
+ gen.count
182
+ );
183
+ continue;
184
+ }
185
+ if (isEdgeLabelRename(gen, edgeLabel)) {
186
+ note(
187
+ LOSS.ROLE,
188
+ `edge column "${edgeLabel ?? ""}" (label) is written as a plain attribute; CX edges have no label slot and the role is lost`,
189
+ gen.column,
190
+ gen.count
191
+ );
192
+ continue;
193
+ }
194
+ if (gen.code === LOSS.MIXED_DIRECTION_ERROR && fatal === null) {
195
+ fatal = new GraphFormatError("E_DIRECTED", gen.message, { reason: "mixed direction" });
196
+ }
197
+ notes.push(gen);
198
+ }
199
+ if (edgeLabel === "name") {
200
+ note(
201
+ LOSS.ROLE,
202
+ `edge column "name" (label) is written as a plain attribute; CX edges have no label slot and the role is lost`,
203
+ "name",
204
+ null
205
+ );
206
+ }
207
+ return fatal;
208
+ }
209
+ function writtenOtherwise(gen, ownNames) {
210
+ if (gen.column !== null && ownNames.has(gen.column) && (gen.code === LOSS.JSON || gen.code === LOSS.DTYPE)) {
211
+ return true;
212
+ }
213
+ if (gen.code === LOSS.ID_CHARSET || gen.code === LOSS.ID_MANGLED) {
214
+ return true;
215
+ }
216
+ return gen.code === LOSS.EXTENSION_TABLE && gen.column !== null && gen.column in PROVENANCE_TABLES;
217
+ }
218
+ function isEdgeLabelRename(gen, edgeLabel) {
219
+ return gen.code === LOSS.COLUMN_NAME_CHANGED && gen.column === edgeLabel && gen.message.startsWith("edge column");
220
+ }
221
+ function planPositions(snapshot, note) {
222
+ const position = snapshot.nodes.byRole("position");
223
+ const zColumn = [...snapshot.nodes].find(isZ) ?? null;
224
+ const z = zColumn !== null && zFitsLayout(zColumn, position) ? zColumn : null;
225
+ if (position === null) {
226
+ return { position, z, positionZ: false, nonfinite: 0 };
227
+ }
228
+ let nonfinite = nonFiniteCells(position, (v) => [v[0], v[1]]);
229
+ const depth = nonFiniteCells(position, (v) => v[2] === 0 ? [] : [Number.NaN]);
230
+ const positionZ = zColumn === null && (position.meta.extra.sourceDims === 3 || depth > 0);
231
+ if (positionZ) {
232
+ nonfinite += nonFiniteCells(position, (v) => Number.isFinite(v[0]) && Number.isFinite(v[1]) ? [v[2]] : []);
233
+ note(
234
+ LOSS.COMPONENTS,
235
+ `node column "${position.meta.name}" (position) has a third coordinate; CX writes it as the layout's z, which reads back in the z column (a stacking order), not in the position`,
236
+ position.meta.name,
237
+ depth
238
+ );
239
+ }
240
+ return { position, z, positionZ, nonfinite };
241
+ }
242
+ function graphColumnNotes(snapshot, note) {
243
+ for (const c of snapshot.graph) {
244
+ const named = c.meta.name === "name" || c.meta.name === "description";
245
+ if (!isBypass(c) && named && (c.dtype === "string" || c.dtype === "dict")) {
246
+ note(
247
+ LOSS.ROLE_ASSUMED,
248
+ `graph column "${c.meta.name}" is written as the network attribute ${c.meta.name}, which reads back as the graph's ${c.meta.name}, not as a column`,
249
+ c.meta.name,
250
+ null
251
+ );
252
+ }
253
+ }
254
+ }
255
+ function plan(snapshot, common) {
256
+ const { notes, note } = noteList();
257
+ const functionTerm = [...snapshot.nodes].find((c) => fromCx(c, "provenance") && c.meta.name === "functionTerm") ?? null;
258
+ const reifiedEdge = [...snapshot.nodes].find((c) => fromCx(c, "reifiedEdges") && c.dtype === "u32") ?? null;
259
+ const children = childrenCsr(snapshot);
260
+ const several = children.column?.meta.role === "parents" && severalParents(children.column);
261
+ let fatal = genericNotes(snapshot, common, several, [functionTerm, reifiedEdge], notes, note);
262
+ const folding = pairFolding(snapshot);
263
+ directionNotes(snapshot, folding, common, CX_LOSS.UNDIRECTED_AS_DIRECTED, note);
264
+ const planned = planNodeIds(snapshot, common, "CX", ORIGINAL_ID_ATTRIBUTE, note);
265
+ const ids = planned instanceof GraphFormatError ? null : planned;
266
+ if (planned instanceof GraphFormatError) {
267
+ fatal ??= planned;
268
+ }
269
+ const edgeIds = planEdgeIds(snapshot, note);
270
+ if (snapshot.nodes.has(ORIGINAL_ID_ATTRIBUTE)) {
271
+ note(
272
+ LOSS.ROLE_ASSUMED,
273
+ `node column "${ORIGINAL_ID_ATTRIBUTE}" is the importer's record of mangled ids: it reads back as the node ids unless restoreMangledIds is false`,
274
+ ORIGINAL_ID_ATTRIBUTE,
275
+ null
276
+ );
277
+ }
278
+ const positions = planPositions(snapshot, note);
279
+ const { position, z, positionZ } = positions;
280
+ let { nonfinite } = positions;
281
+ const collapsed = [...snapshot.nodes].find((c) => fromCx(c, "cyGroups") && c.dtype === "bool") ?? null;
282
+ const nodeCore = corePlans(snapshot, note);
283
+ const edgeCore = [];
284
+ const interaction = snapshot.edges.get("interaction");
285
+ if (interaction !== null && (interaction.meta.role === null || !SKIPPED_ROLES.has(interaction.meta.role))) {
286
+ edgeCore.push({ key: "i", column: interaction });
287
+ coreTextNote(interaction, "edge", note);
288
+ }
289
+ const coreColumns = new Set([...nodeCore, ...edgeCore].map((c) => c.column));
290
+ const special = (c) => coreColumns.has(c) || isBypass(c) || isLink(c) || c === z || c === collapsed || c === functionTerm || c === reifiedEdge || c.meta.role !== null && SKIPPED_ROLES.has(c.meta.role);
291
+ const label = snapshot.nodes.byRole("label");
292
+ const renamedName = label !== null && label.meta.name !== "name" ? snapshot.nodes.get("name") : null;
293
+ if (renamedName !== null) {
294
+ note(
295
+ LOSS.COLUMN_NAME_CHANGED,
296
+ `node column "name" is written as the attribute "name#2": the label column "${label?.meta.name ?? ""}" is written as n, which reads back as "name"`,
297
+ "name",
298
+ renamedName.length - renamedName.nullCount
299
+ );
300
+ }
301
+ const nodeAttrs = attributePlans(snapshot.nodes, special, (c) => c === renamedName ? "name#2" : c.meta.name);
302
+ const weight = planWeight(snapshot, folding, note);
303
+ const plainWeight = snapshot.edges.get(WEIGHT_ATTRIBUTE);
304
+ const edgeAttrs = attributePlans(
305
+ snapshot.edges,
306
+ (c) => special(c) || c === plainWeight && c.meta.role === null,
307
+ (c) => c.meta.name
308
+ );
309
+ nonfinite += weight.nonfinite;
310
+ graphColumnNotes(snapshot, note);
311
+ const graphAttrs = attributePlans(snapshot.graph, isBypass, (c) => c.meta.name);
312
+ if (children.column !== null && children.column.meta.role === "parents" && !several) {
313
+ note(
314
+ LOSS.COLUMN_NAME_CHANGED,
315
+ `node column "${children.column.meta.name}" (parents) holds at most one parent per node; it reads back as the single-parent column "parent"`,
316
+ children.column.meta.name,
317
+ children.column.length - children.column.nullCount
318
+ );
319
+ }
320
+ if (nonfinite > 0) {
321
+ note(
322
+ LOSS.NONFINITE_AS_NULL,
323
+ `${nonfinite} NaN or infinite position${plural(nonfinite)}, stacking orders or weights cannot be written in CX; they read back unset`,
324
+ null,
325
+ nonfinite
326
+ );
327
+ }
328
+ return {
329
+ notes,
330
+ fatal,
331
+ ids,
332
+ folding,
333
+ edgeIds,
334
+ weight: weight.plan,
335
+ nodeCore,
336
+ edgeCore,
337
+ nodeAttrs,
338
+ edgeAttrs,
339
+ graphAttrs,
340
+ position,
341
+ positionZ,
342
+ z,
343
+ bypass: {
344
+ node: [...snapshot.nodes].filter(isBypass),
345
+ edge: [...snapshot.edges].filter(isBypass),
346
+ graph: [...snapshot.graph].filter(isBypass)
347
+ },
348
+ collapsed,
349
+ links: linkPlans(snapshot),
350
+ functionTerm,
351
+ reifiedEdge
352
+ };
353
+ }
354
+ function corePlans(snapshot, note) {
355
+ const out = [];
356
+ const label = snapshot.nodes.byRole("label");
357
+ const plainName = snapshot.nodes.get("name");
358
+ const n = label ?? (plainName !== null && plainName.meta.role === null ? plainName : null);
359
+ if (n !== null) {
360
+ out.push({ key: "n", column: n });
361
+ coreTextNote(n, "node", note);
362
+ if (n !== label) {
363
+ note(
364
+ LOSS.ROLE_ASSUMED,
365
+ `node column "name" is written as n and reads back as the label`,
366
+ "name",
367
+ n.length - n.nullCount
368
+ );
369
+ }
370
+ }
371
+ const represents = snapshot.nodes.get("represents");
372
+ if (represents !== null && represents !== n && (represents.meta.role === null || !SKIPPED_ROLES.has(represents.meta.role))) {
373
+ out.push({ key: "r", column: represents });
374
+ coreTextNote(represents, "node", note);
375
+ }
376
+ return out;
377
+ }
378
+ function coreTextNote(column, domain, note) {
379
+ if (coreNeedsText(column)) {
380
+ note(
381
+ LOSS.DTYPE,
382
+ `${domain} column "${column.meta.name}" is ${column.dtype}; CX writes it as text and it reads back as string`,
383
+ column.meta.name,
384
+ column.length - column.nullCount
385
+ );
386
+ }
387
+ }
388
+ function attributePlans(table, skip, rename) {
389
+ return [...table].filter((c) => !skip(c)).map((column) => ({ column, name: rename(column), d: declaredType(column) }));
390
+ }
391
+ function planWeight(snapshot, folding, note) {
392
+ const weights = explicitWeights(snapshot);
393
+ const plain = snapshot.edges.get(WEIGHT_ATTRIBUTE);
394
+ const clash = plain !== null && plain.meta.role === null;
395
+ let plan2 = null;
396
+ if (weights.weighted) {
397
+ plan2 = { value: (e) => weights.isExplicit(e) ? weights.value(e) : void 0 };
398
+ if (clash) {
399
+ note(
400
+ LOSS.WEIGHT_KEY_CLASH,
401
+ `edge column "${WEIGHT_ATTRIBUTE}" is not written: the explicit weights are written under that name`,
402
+ WEIGHT_ATTRIBUTE,
403
+ null
404
+ );
405
+ }
406
+ } else if (clash) {
407
+ const numeric = NUMERIC_DTYPES.has(plain.dtype);
408
+ note(
409
+ LOSS.WEIGHT_KEY_CLASH,
410
+ numeric ? `edge column "${WEIGHT_ATTRIBUTE}" is written as the weight attribute and reads back as the edge weight` : `edge column "${WEIGHT_ATTRIBUTE}" is ${plain.dtype}, and CX reads that attribute as the edge weight; it is not written`,
411
+ WEIGHT_ATTRIBUTE,
412
+ null
413
+ );
414
+ if (numeric) {
415
+ plan2 = { value: (e) => plain.isSet(e) ? plain.value(e) : void 0 };
416
+ }
417
+ }
418
+ return { plan: plan2, nonfinite: plan2 === null ? 0 : nanWeights(snapshot.edgeCount, plan2, folding) };
419
+ }
420
+ function nanWeights(edgeCount, plan2, folding) {
421
+ let nan = 0;
422
+ for (let e = 0; e < edgeCount; e++) {
423
+ const w = plan2.value(e);
424
+ if (!folding.folded(e) && w !== void 0 && Number.isNaN(w)) {
425
+ nan++;
426
+ }
427
+ }
428
+ return nan;
429
+ }
430
+ function zFitsLayout(z, position) {
431
+ for (let i = 0; i < z.length; i++) {
432
+ if (!z.isSet(i)) {
433
+ continue;
434
+ }
435
+ const value = z.value(i);
436
+ const point = position?.isSet(i) === true ? position.value(i) : null;
437
+ if (typeof value !== "number" || !Number.isFinite(value) || point === null || !Number.isFinite(point[0]) || !Number.isFinite(point[1])) {
438
+ return false;
439
+ }
440
+ }
441
+ return true;
442
+ }
443
+ function nonFiniteCells(column, pick) {
444
+ let count = 0;
445
+ for (let i = 0; i < column.length; i++) {
446
+ if (column.isSet(i) && pick(column.value(i)).some((v) => !Number.isFinite(v))) {
447
+ count++;
448
+ }
449
+ }
450
+ return count;
451
+ }
452
+ function severalParents(column) {
453
+ for (let i = 0; i < column.length; i++) {
454
+ if (column.isSet(i) && column.value(i).length > 1) {
455
+ return true;
456
+ }
457
+ }
458
+ return false;
459
+ }
460
+ function linkPlans(snapshot) {
461
+ const out = [];
462
+ for (const [domain, table] of [
463
+ ["node", snapshot.nodes],
464
+ ["edge", snapshot.edges]
465
+ ]) {
466
+ for (const column of table) {
467
+ if (isLink(column)) {
468
+ const key = column.meta.name;
469
+ out.push({ aspect: `${domain}${key === "citations" ? "Citations" : "Supports"}`, key, domain, column });
470
+ }
471
+ }
472
+ }
473
+ return out;
474
+ }
475
+ function scalarText(value) {
476
+ if (typeof value === "number") {
477
+ return Object.is(value, -0) ? "-0" : String(value);
478
+ }
479
+ if (typeof value === "string") {
480
+ return value;
481
+ }
482
+ return typeof value === "boolean" ? String(value) : JSON.stringify(value);
483
+ }
484
+ function cellValue(column, d, row) {
485
+ if (!column.isSet(row)) {
486
+ return void 0;
487
+ }
488
+ const value = column.value(row);
489
+ if (column.meta.dtype === "json") {
490
+ return JSON.stringify(value);
491
+ }
492
+ if (column.meta.dtype === "list" || column.meta.components > 1) {
493
+ const items = Array.from(value);
494
+ return d === "string" ? JSON.stringify(items) : items.map(scalarText);
495
+ }
496
+ return scalarText(value);
497
+ }
498
+ function coreText(column, row) {
499
+ if (!column.isSet(row)) {
500
+ return void 0;
501
+ }
502
+ const value = column.value(row);
503
+ if (column.meta.dtype === "json" || column.meta.dtype === "list" || column.meta.components > 1) {
504
+ return JSON.stringify(column.meta.dtype === "json" ? value : Array.from(value));
505
+ }
506
+ return scalarText(value);
507
+ }
508
+ function writtenEdges(snapshot, folding) {
509
+ const out = [];
510
+ for (let e = 0; e < snapshot.edgeCount; e++) {
511
+ if (!folding.folded(e)) {
512
+ out.push(e);
513
+ }
514
+ }
515
+ return out;
516
+ }
517
+ function aspects(snapshot, p, ids) {
518
+ const { src, dst } = snapshot.edgeList();
519
+ const edges = writtenEdges(snapshot, p.folding);
520
+ const nodeRange = () => Array.from({ length: snapshot.nodeCount }, (_, i2) => i2);
521
+ const nodeCore = (key) => p.nodeCore.find((c) => c.key === key)?.column ?? null;
522
+ const n = nodeCore("n");
523
+ const r = nodeCore("r");
524
+ const i = p.edgeCore[0]?.column ?? null;
525
+ const kept = snapshot.meta.sourceFormat === CX_FORMAT$1 && isRecord(snapshot.meta.extra.cx) ? snapshot.meta.extra.cx : {};
526
+ const out = [
527
+ {
528
+ name: "nodes",
529
+ idCounter: nextNodeId(ids, snapshot.nodeCount),
530
+ *elements() {
531
+ for (let row = 0; row < snapshot.nodeCount; row++) {
532
+ const element = { "@id": ids.idAt(row) };
533
+ const name = n === null ? void 0 : coreText(n, row);
534
+ const represents = r === null ? void 0 : coreText(r, row);
535
+ if (name !== void 0) {
536
+ element.n = name;
537
+ }
538
+ if (represents !== void 0) {
539
+ element.r = represents;
540
+ }
541
+ yield element;
542
+ }
543
+ }
544
+ },
545
+ {
546
+ name: "edges",
547
+ idCounter: edges.reduce((max, e) => Math.max(max, p.edgeIds[e] + 1), 0),
548
+ *elements() {
549
+ for (const e of edges) {
550
+ const element = {
551
+ "@id": p.edgeIds[e],
552
+ s: ids.idAt(src[e]),
553
+ t: ids.idAt(dst[e])
554
+ };
555
+ const interaction = i === null ? void 0 : coreText(i, e);
556
+ if (interaction !== void 0) {
557
+ element.i = interaction;
558
+ }
559
+ yield element;
560
+ }
561
+ }
562
+ },
563
+ {
564
+ name: "nodeAttributes",
565
+ *elements() {
566
+ for (const a of p.nodeAttrs) {
567
+ for (let row = 0; row < snapshot.nodeCount; row++) {
568
+ const v = cellValue(a.column, a.d, row);
569
+ if (v !== void 0) {
570
+ yield { po: ids.idAt(row), n: a.name, v, d: a.d };
571
+ }
572
+ }
573
+ }
574
+ for (let row = 0; row < snapshot.nodeCount; row++) {
575
+ if (ids.isChanged(row)) {
576
+ yield {
577
+ po: ids.idAt(row),
578
+ n: ORIGINAL_ID_ATTRIBUTE,
579
+ v: String(ids.originalAt(row)),
580
+ d: "string"
581
+ };
582
+ }
583
+ }
584
+ }
585
+ },
586
+ {
587
+ name: "edgeAttributes",
588
+ *elements() {
589
+ for (const a of p.edgeAttrs) {
590
+ for (const e of edges) {
591
+ const v = cellValue(a.column, a.d, e);
592
+ if (v !== void 0) {
593
+ yield { po: p.edgeIds[e], n: a.name, v, d: a.d };
594
+ }
595
+ }
596
+ }
597
+ if (p.weight !== null) {
598
+ for (const e of edges) {
599
+ const w = p.weight.value(e);
600
+ if (w !== void 0 && !Number.isNaN(w)) {
601
+ yield { po: p.edgeIds[e], n: WEIGHT_ATTRIBUTE, v: scalarText(w), d: "double" };
602
+ }
603
+ }
604
+ }
605
+ }
606
+ },
607
+ {
608
+ name: "networkAttributes",
609
+ *elements() {
610
+ for (const a of p.graphAttrs) {
611
+ const v = cellValue(a.column, a.d, 0);
612
+ if (v !== void 0) {
613
+ yield { n: a.name, v, d: a.d };
614
+ }
615
+ }
616
+ const { meta } = snapshot;
617
+ for (const [key, value] of [
618
+ ["name", meta.name],
619
+ ["description", meta.description]
620
+ ]) {
621
+ if (value !== null && !p.graphAttrs.some((a) => a.name === key)) {
622
+ yield { n: key, v: value, d: "string" };
623
+ }
624
+ }
625
+ }
626
+ },
627
+ { name: "cyTableColumn", elements: () => tableColumns(p, ids) },
628
+ { name: "cartesianLayout", elements: () => layoutElements(p, ids, nodeRange()) },
629
+ { name: "cyGroups", elements: () => groupElements(snapshot, p, ids, edges) },
630
+ { name: "cyVisualProperties", elements: () => visualProperties(p, ids, nodeRange(), edges, kept) }
631
+ ];
632
+ out.push(...provenanceAspects(snapshot, p, ids, edges));
633
+ for (const [name, value] of Object.entries(kept)) {
634
+ if (!NEVER_WRITTEN.has(name) && Array.isArray(value)) {
635
+ out.push({ name, elements: () => keptElements(value, kept.subnetwork, STYLE_ASPECTS.has(name)) });
636
+ }
637
+ }
638
+ return out;
639
+ }
640
+ function* tableColumns(p, ids) {
641
+ for (const { key, column } of [...p.nodeCore, ...p.edgeCore]) {
642
+ const n = { n: "name", r: "represents", i: "interaction" }[key];
643
+ if (n !== void 0 && column !== null) {
644
+ yield { applies_to: key === "i" ? "edge_table" : "node_table", n, d: "string" };
645
+ }
646
+ }
647
+ for (const a of p.nodeAttrs) {
648
+ yield { applies_to: "node_table", n: a.name, d: a.d };
649
+ }
650
+ if (ids.changed > 0) {
651
+ yield { applies_to: "node_table", n: ORIGINAL_ID_ATTRIBUTE, d: "string" };
652
+ }
653
+ for (const a of p.edgeAttrs) {
654
+ yield { applies_to: "edge_table", n: a.name, d: a.d };
655
+ }
656
+ if (p.weight !== null) {
657
+ yield { applies_to: "edge_table", n: WEIGHT_ATTRIBUTE, d: "double" };
658
+ }
659
+ for (const a of p.graphAttrs) {
660
+ yield { applies_to: "network_table", n: a.name, d: a.d };
661
+ }
662
+ }
663
+ const STYLE_ASPECTS = /* @__PURE__ */ new Set(["tableVisualProperties", "cyTableVisualProperties"]);
664
+ function* keptElements(elements, subnetwork, style) {
665
+ for (const element of elements) {
666
+ if (!isRecord(element)) {
667
+ yield element;
668
+ continue;
669
+ }
670
+ if (element.s !== void 0 && element.s !== null && element.s !== subnetwork) {
671
+ continue;
672
+ }
673
+ const { s: _scope, ...rest } = element;
674
+ yield style ? rewriteView(rest) : rest;
675
+ }
676
+ }
677
+ function rewriteView(element) {
678
+ const out = { ...element };
679
+ if ("view" in out) {
680
+ out.view = 0;
681
+ }
682
+ if (typeof out.applies_to === "number") {
683
+ out.applies_to = 0;
684
+ }
685
+ return out;
686
+ }
687
+ function* layoutElements(p, ids, rows) {
688
+ const { position } = p;
689
+ if (position === null) {
690
+ return;
691
+ }
692
+ for (const row of rows) {
693
+ if (!position.isSet(row)) {
694
+ continue;
695
+ }
696
+ const point = position.value(row);
697
+ if (!Number.isFinite(point[0]) || !Number.isFinite(point[1])) {
698
+ continue;
699
+ }
700
+ const entry = { node: ids.idAt(row), x: point[0], y: point[1] === 0 ? 0 : -point[1] };
701
+ if (p.z?.isSet(row) === true) {
702
+ entry.z = p.z.value(row);
703
+ } else if (p.positionZ && Number.isFinite(point[2])) {
704
+ entry.z = point[2];
705
+ }
706
+ yield entry;
707
+ }
708
+ }
709
+ function* groupElements(snapshot, p, ids, edges) {
710
+ const children = childrenCsr(snapshot);
711
+ const groupsOf = /* @__PURE__ */ new Map();
712
+ const groups = [];
713
+ for (let g = 0; g < snapshot.nodeCount; g++) {
714
+ const members = children.childrenOf(g);
715
+ if (members.length > 0 || p.collapsed?.isSet(g) === true) {
716
+ groups.push(g);
717
+ }
718
+ for (const c of members) {
719
+ groupsOf.set(c, [...groupsOf.get(c) ?? [], g]);
720
+ }
721
+ }
722
+ if (groups.length === 0) {
723
+ return;
724
+ }
725
+ const { internal, external } = groupEdges(snapshot, p, groupsOf, edges);
726
+ const label = p.nodeCore.find((c) => c.key === "n")?.column ?? null;
727
+ for (const g of groups) {
728
+ const entry = { "@id": ids.idAt(g) };
729
+ const name = label === null ? void 0 : coreText(label, g);
730
+ if (name !== void 0) {
731
+ entry.n = name;
732
+ }
733
+ entry.nodes = Array.from(children.childrenOf(g), (c) => ids.idAt(c));
734
+ entry.internal_edges = internal.get(g) ?? [];
735
+ entry.external_edges = external.get(g) ?? [];
736
+ if (p.collapsed?.isSet(g) === true) {
737
+ entry.collapsed = p.collapsed.value(g);
738
+ }
739
+ yield entry;
740
+ }
741
+ }
742
+ function groupEdges(snapshot, p, groupsOf, edges) {
743
+ const internal = /* @__PURE__ */ new Map();
744
+ const external = /* @__PURE__ */ new Map();
745
+ const add = (table, g, id) => {
746
+ table.set(g, [...table.get(g) ?? [], id]);
747
+ };
748
+ const { src, dst } = snapshot.edgeList();
749
+ for (const e of edges) {
750
+ const ofSource = groupsOf.get(src[e]) ?? [];
751
+ const ofTarget = groupsOf.get(dst[e]) ?? [];
752
+ for (const g of ofSource) {
753
+ add(ofTarget.includes(g) ? internal : external, g, p.edgeIds[e]);
754
+ }
755
+ for (const g of ofTarget.filter((t) => !ofSource.includes(t))) {
756
+ add(external, g, p.edgeIds[e]);
757
+ }
758
+ }
759
+ return { internal, external };
760
+ }
761
+ function bypassValues(columns, row) {
762
+ let out = null;
763
+ for (const column of columns) {
764
+ if (column.isSet(row)) {
765
+ const property = column.meta.origin?.id;
766
+ out ??= {};
767
+ out[typeof property === "string" && property.length > 0 ? property : column.meta.name] = scalarText(
768
+ column.value(row)
769
+ );
770
+ }
771
+ }
772
+ return out;
773
+ }
774
+ function* visualProperties(p, ids, rows, edges, kept) {
775
+ const network = bypassValues(p.bypass.graph, 0);
776
+ if (network !== null) {
777
+ yield { properties_of: "network", properties: network };
778
+ }
779
+ const view = ownView(kept);
780
+ for (const entry of Array.isArray(kept.cyVisualProperties) ? kept.cyVisualProperties : []) {
781
+ if (isRecord(entry) && !["nodes", "edges", "network"].includes(String(entry.properties_of)) && (view === null || entry.view === void 0 || entry.view === null || entry.view === view)) {
782
+ yield rewriteView(entry);
783
+ }
784
+ }
785
+ for (const row of rows) {
786
+ const properties = bypassValues(p.bypass.node, row);
787
+ if (properties !== null) {
788
+ yield { properties_of: "nodes", applies_to: ids.idAt(row), properties };
789
+ }
790
+ }
791
+ for (const e of edges) {
792
+ const properties = bypassValues(p.bypass.edge, e);
793
+ if (properties !== null) {
794
+ yield { properties_of: "edges", applies_to: p.edgeIds[e], properties };
795
+ }
796
+ }
797
+ }
798
+ function ownView(kept) {
799
+ const sub = kept.subnetwork;
800
+ for (const r of Array.isArray(kept.cyNetworkRelations) ? kept.cyNetworkRelations : []) {
801
+ if (isRecord(r) && r.r === "view" && (sub === void 0 || r.p === sub)) {
802
+ return r.c;
803
+ }
804
+ }
805
+ for (const v of Array.isArray(kept.cyViews) ? kept.cyViews : []) {
806
+ if (isRecord(v) && (sub === void 0 || v.s === sub)) {
807
+ return v["@id"];
808
+ }
809
+ }
810
+ return null;
811
+ }
812
+ function provenanceAspects(snapshot, p, ids, edges) {
813
+ const out = [];
814
+ for (const [table, aspect2] of Object.entries(PROVENANCE_TABLES)) {
815
+ const ext = snapshot.extensions.get(table);
816
+ if (ext === void 0) {
817
+ continue;
818
+ }
819
+ const idColumn = ext.get("id");
820
+ const fields = [...ext].filter((c) => c !== idColumn);
821
+ let max = -1;
822
+ for (let row = 0; row < ext.rowCount; row++) {
823
+ if (idColumn?.isSet(row) === true) {
824
+ max = Math.max(max, Number(idColumn.value(row)));
825
+ }
826
+ }
827
+ out.push({
828
+ name: aspect2,
829
+ idCounter: max + 1,
830
+ *elements() {
831
+ for (let row = 0; row < ext.rowCount; row++) {
832
+ const element = {};
833
+ if (idColumn?.isSet(row) === true) {
834
+ element["@id"] = idColumn.value(row);
835
+ }
836
+ for (const field of fields) {
837
+ if (field.isSet(row)) {
838
+ element[field.meta.name] = field.value(row);
839
+ }
840
+ }
841
+ yield element;
842
+ }
843
+ }
844
+ });
845
+ }
846
+ for (const link of p.links) {
847
+ out.push({
848
+ name: link.aspect,
849
+ *elements() {
850
+ const rows = link.domain === "node" ? Array.from({ length: snapshot.nodeCount }, (_, i) => i) : edges;
851
+ for (const row of rows) {
852
+ if (link.column.isSet(row)) {
853
+ const po = link.domain === "node" ? ids.idAt(row) : p.edgeIds[row];
854
+ yield { po: [po], [link.key]: Array.from(link.column.value(row)) };
855
+ }
856
+ }
857
+ }
858
+ });
859
+ }
860
+ const terms = p.functionTerm;
861
+ if (terms !== null) {
862
+ out.push({
863
+ name: "functionTerms",
864
+ *elements() {
865
+ for (let row = 0; row < snapshot.nodeCount; row++) {
866
+ const term = terms.isSet(row) ? terms.value(row) : null;
867
+ if (isRecord(term)) {
868
+ yield { po: ids.idAt(row), ...term };
869
+ }
870
+ }
871
+ }
872
+ });
873
+ }
874
+ const reified = p.reifiedEdge;
875
+ if (reified !== null) {
876
+ out.push({
877
+ name: "reifiedEdges",
878
+ *elements() {
879
+ for (let row = 0; row < snapshot.nodeCount; row++) {
880
+ const edge = reified.isSet(row) ? reified.value(row) : -1;
881
+ if (edge >= 0 && edge < snapshot.edgeCount) {
882
+ yield { node: ids.idAt(row), edge: p.edgeIds[edge] };
883
+ }
884
+ }
885
+ }
886
+ });
887
+ }
888
+ return out;
889
+ }
890
+ function* write(snapshot, p) {
891
+ if (p.fatal !== null) {
892
+ throw p.fatal;
893
+ }
894
+ const { ids } = p;
895
+ if (ids === null) {
896
+ throw new GraphFormatError("E_INVALID_ID", "node ids cannot be written as CX integers", { reason: "charset" });
897
+ }
898
+ const all = aspects(snapshot, p, ids);
899
+ const written = [];
900
+ for (const aspect2 of all) {
901
+ let count = 0;
902
+ for (const _ of aspect2.elements()) {
903
+ count++;
904
+ }
905
+ if (count > 0 || aspect2.name === "nodes") {
906
+ written.push({ aspect: aspect2, count });
907
+ }
908
+ }
909
+ const metaData = written.map(({ aspect: aspect2, count }) => ({
910
+ name: aspect2.name,
911
+ version: "1.0",
912
+ ...aspect2.idCounter === void 0 ? {} : { idCounter: aspect2.idCounter },
913
+ elementCount: count,
914
+ consistencyGroup: 1
915
+ }));
916
+ yield '[{"numberVerification":[{"longNumber":281474976710655}]},\n';
917
+ yield `{"metaData":${stringify(metaData)}},
918
+ `;
919
+ for (const { aspect: aspect2 } of written) {
920
+ yield* aspectBlock(aspect2.name, mapIterable(aspect2.elements(), stringify));
921
+ }
922
+ yield '{"status":[{"error":"","success":true}]}]\n';
923
+ }
924
+ function* mapIterable(items, fn) {
925
+ for (const item of items) {
926
+ yield fn(item);
927
+ }
928
+ }
929
+ const cxExporter = Object.freeze({
930
+ format: CX_FORMAT$1,
931
+ options: Object.freeze([]),
932
+ capabilities: CX_CAPABILITIES,
933
+ /**
934
+ * Pre-flight: what export() would lose, without writing anything.
935
+ * @param snapshot - the snapshot
936
+ * @param options - the common options
937
+ * @returns the notes, empty when the export is exact
938
+ */
939
+ check(snapshot, options) {
940
+ return Object.freeze([...plan(snapshot, resolveExportOptions(options)).notes]);
941
+ },
942
+ /**
943
+ * Write the document as UTF-8 chunks.
944
+ * @param snapshot - the snapshot
945
+ * @param options - the common options
946
+ * @returns the chunks
947
+ */
948
+ export(snapshot, options) {
949
+ return encodeChunks(write(snapshot, plan(snapshot, resolveExportOptions(options))));
950
+ },
951
+ /**
952
+ * Write the document as one string.
953
+ * @param snapshot - the snapshot
954
+ * @param options - the common options
955
+ * @returns the document
956
+ */
957
+ exportToString(snapshot, options) {
958
+ return joinText(write(snapshot, plan(snapshot, resolveExportOptions(options))));
959
+ }
960
+ });
6
961
  const CX_FORMAT = "cx";
7
962
  const CX_BYPASS_NAMESPACE = "cx.bypass";
8
963
  const CX_ISSUE = Object.freeze({
9
- /** The input is empty (fatal). */
964
+ ...INPUT_ISSUE,
965
+ ...ELEMENT_ISSUE,
966
+ /** The input is empty. The import stops. */
10
967
  EMPTY_INPUT: EMPTY_INPUT_CODE,
11
- /** The text is not JSON (fatal). */
968
+ /** The text is not valid JSON. The import stops. */
12
969
  SYNTAX: SYNTAX_CODE,
13
- /** Invalid UTF-8 (fatal). */
970
+ /** The input is not valid UTF-8. The import stops. */
14
971
  INVALID_UTF8: INVALID_UTF8_CODE,
15
- /** Invalid bytes in the encoding a BOM or the encoding option chose (fatal). */
972
+ /**
973
+ * Some bytes are not valid in the encoding that was chosen (by a byte order mark or the `encoding` option). The
974
+ * import stops.
975
+ */
16
976
  INVALID_ENCODING: INVALID_ENCODING_CODE,
17
977
  /** Bytes that are not UTF-8 were read as windows-1252. */
18
978
  ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
19
979
  /** An encoding the platform cannot decode was ignored. */
20
980
  UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
21
- /** The document is not a CX array, or it is CX2 (fatal; the message names CX2). */
981
+ /** The document is not a CX array, or it is CX2 (the message says so; read it as `cx2`). The import stops. */
22
982
  NOT_CX: "E_CX_NOT_CX",
23
- /** numberVerification holds another value than 2^48 - 1, or comes twice. */
983
+ /** NumberVerification holds another value than 2^48 - 1, or comes twice. */
24
984
  NUMBER_VERIFICATION: "W_CX_NUMBER_VERIFICATION",
25
985
  /** An old Cytoscape aspect name (visualProperties, subNetworks, ...) read under its cy name. */
26
986
  OLD_ASPECT_NAME: "W_CX_OLD_ASPECT_NAME",
@@ -28,11 +988,14 @@ const CX_ISSUE = Object.freeze({
28
988
  GROUP_NODE_ADDED: "W_CX_GROUP_NODE_ADDED",
29
989
  /** Nodes or edges of the root network that no subnetwork holds: Cytoscape shows them in no network; not read. */
30
990
  ROOT_ONLY: "W_CX_ROOT_ONLY",
31
- /** The producer marked the document as failed (fatal). */
991
+ /** The program that wrote the file marked it as failed, so it is incomplete. The import stops. */
32
992
  STATUS_FAILED: STATUS_FAILED_CODE,
33
- /** The producer marked the document as successful with an error text. */
993
+ /** The program that wrote the file marked it as successful but added an error message; the message is shown. */
34
994
  STATUS_WARNING: STATUS_WARNING_CODE,
35
- /** A member of the array that is not a one-key aspect block, or an element that is not an object. */
995
+ /**
996
+ * A member of the file's top-level array that is not a block with one key (`{"nodes": [...]}`), or an element of a
997
+ * block that is not an object. It is skipped; the rest of the file is read.
998
+ */
36
999
  BAD_ASPECT_BLOCK: BAD_ASPECT_BLOCK_CODE,
37
1000
  /** An aspect after the post-metadata or after the status, a third metaData. */
38
1001
  ASPECT_ORDER: ASPECT_ORDER_CODE,
@@ -40,7 +1003,10 @@ const CX_ISSUE = Object.freeze({
40
1003
  COUNT_MISMATCH: COUNT_MISMATCH_CODE,
41
1004
  /** A value that does not parse as its data type; the cell is unset. */
42
1005
  BAD_VALUE: BAD_VALUE_CODE,
43
- /** One attribute name with several data types: the column takes the wider one. */
1006
+ /**
1007
+ * An attribute's type was widened because a later value did not fit: an integer above 2^31 in an integer column,
1008
+ * or two declared types for one attribute.
1009
+ */
44
1010
  WIDENED: WIDENED_CODE,
45
1011
  /** A data type CX does not define; the value is kept as text. */
46
1012
  UNKNOWN_ATTR_TYPE: UNKNOWN_ATTR_TYPE_CODE,
@@ -52,9 +1018,9 @@ const CX_ISSUE = Object.freeze({
52
1018
  UNKNOWN_PARENT: UNKNOWN_PARENT_CODE,
53
1019
  /** A group membership that would close a parent cycle; dropped. */
54
1020
  PARENT_CYCLE: PARENT_CYCLE_CODE,
55
- /** The style rules of cyVisualProperties are not applied (issue #706). */
1021
+ /** The style rules of cyVisualProperties are not applied; they are kept so a CX export writes them back. */
56
1022
  STYLES_NOT_IMPORTED: STYLES_NOT_IMPORTED_CODE,
57
- /** A node without an @id. */
1023
+ /** A node without an id (its `@id` key). The node is skipped. */
58
1024
  MISSING_ID: MISSING_ID_CODE,
59
1025
  /** An edge without s or t. */
60
1026
  MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
@@ -64,37 +1030,63 @@ const CX_ISSUE = Object.freeze({
64
1030
  DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,
65
1031
  /** An id that is not an integer. */
66
1032
  INVALID_ID: "E_INVALID_ID",
1033
+ /**
1034
+ * A node or edge key CX does not define, an aspect graph-io keeps for writing back, or a table CX does not define;
1035
+ * it is skipped.
1036
+ */
1037
+ UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
1038
+ /** A member holding several aspects; each array-valued key is read as its own fragment. */
1039
+ MULTI_ASPECT_FRAGMENT: MULTI_ASPECT_FRAGMENT_CODE,
1040
+ /** An aspect written as one object, not an array of elements; read as one element. */
1041
+ SINGLE_OBJECT_ASPECT: SINGLE_OBJECT_ASPECT_CODE,
1042
+ /** The bare tokens NaN / Infinity / -Infinity (Python's json writes them), read as numbers. */
1043
+ JSON_NONSTANDARD_NUMBER: JSON_NONSTANDARD_NUMBER_CODE,
67
1044
  /** An edge endpoint naming no node of the graph (addMissingNodes false, the default). */
68
1045
  UNKNOWN_NODE: "E_UNKNOWN_NODE",
69
1046
  /** A weight that is not a number. */
70
1047
  INVALID_WEIGHT: "E_INVALID_WEIGHT",
71
1048
  /** An id spelled as a string or a non-integer literal; read as the integer. */
72
1049
  ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
73
- /** An integer beyond 2^53: an id kept as its digits, a value stored as the nearest f64. */
1050
+ /** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
74
1051
  PRECISION: PRECISION_CODE,
75
1052
  /** Two id texts merged under `ids: "number"`. */
76
1053
  ID_MERGED: ID_MERGED_CODE,
77
- /** A collection read by import(): the other subnetworks are skipped. */
1054
+ /**
1055
+ * The file holds several graphs and only the first was read. It is not added when `graphIndex` or `graphName` chose the graph.
1056
+ * `importAllGraphs()` reads every one.
1057
+ */
78
1058
  MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
79
- /** graphIndex or graphName names no subnetwork (fatal). */
1059
+ /** `graphIndex` or `graphName` matches no subnetwork. The import stops. */
80
1060
  GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
81
- /** graphName names several subnetworks (fatal). */
1061
+ /** `graphName` matches several subnetworks. The import stops. */
82
1062
  AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
83
- /** The input holds no graph (fatal). */
1063
+ /** The input holds no graph. The import stops. */
84
1064
  NO_GRAPH: NO_GRAPH_CODE,
85
- /** A column renamed because its name was taken. */
1065
+ /**
1066
+ * An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example
1067
+ * two attributes declared with the same name.
1068
+ */
86
1069
  COLUMN_RENAMED: COLUMN_RENAMED_CODE,
87
- /** A column that lost its role because another column holds it. */
1070
+ /**
1071
+ * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
1072
+ * a plain attribute.
1073
+ */
88
1074
  ROLE_TAKEN: ROLE_TAKEN_CODE,
89
- /** The sink refused the direction. */
1075
+ /**
1076
+ * You read into a graph builder whose direction is already set, or which already holds edges, so the file is read
1077
+ * with the builder's direction instead of its own.
1078
+ */
90
1079
  DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
91
- /** Edges forced to the policy's direction. */
1080
+ /** Edges of the other direction were read with the direction `onMixedDirection` chose. */
92
1081
  DIRECTION_FORCED: DIRECTION_FORCED_CODE,
93
- /** A common option CX has no use for. */
1082
+ /** You set an option this format does not use; it had no effect. The message names the option. */
94
1083
  OPTION_IGNORED: OPTION_IGNORED_CODE,
95
- /** A builder option the caller's sink does not honour. */
1084
+ /**
1085
+ * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
1086
+ * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
1087
+ */
96
1088
  SINK_OPTION: SINK_OPTION_CODE,
97
- /** The input is beyond a size limit (fatal). */
1089
+ /** The input is larger than graph-io's size limit. The import stops. */
98
1090
  TOO_LARGE: TOO_LARGE_CODE
99
1091
  });
100
1092
  const USED_OPTIONS = /* @__PURE__ */ new Set([
@@ -106,6 +1098,7 @@ const USED_OPTIONS = /* @__PURE__ */ new Set([
106
1098
  "weightFrom",
107
1099
  "weightDtype",
108
1100
  "long",
1101
+ "restoreMangledIds",
109
1102
  "errorLimit",
110
1103
  "signal",
111
1104
  "onProgress"
@@ -118,12 +1111,15 @@ const FORMAT_DEFAULTS = {
118
1111
  };
119
1112
  const ABORT_CHECK_INTERVAL = 64;
120
1113
  const NUMBER_VERIFICATION_VALUES = /* @__PURE__ */ new Set(["281474976710655", "9223372036854775807"]);
121
- const OLD_ASPECT_NAMES = {
122
- visualProperties: "cyVisualProperties",
123
- subNetworks: "cySubNetworks",
124
- networkRelations: "cyNetworkRelations",
125
- hiddenAttributes: "cyHiddenAttributes"
126
- };
1114
+ const OLD_ASPECT_NAMES = /* @__PURE__ */ new Map([
1115
+ ["visualProperties", "cyVisualProperties"],
1116
+ ["subNetworks", "cySubNetworks"],
1117
+ ["networkRelations", "cyNetworkRelations"],
1118
+ ["hiddenAttributes", "cyHiddenAttributes"]
1119
+ ]);
1120
+ const NODE_KEYS = /* @__PURE__ */ new Set(["@id", "n", "r"]);
1121
+ const EDGE_KEYS = /* @__PURE__ */ new Set(["@id", "s", "t", "i"]);
1122
+ const TABLES = /* @__PURE__ */ new Set(["node_table", "edge_table", "network_table"]);
127
1123
  const READ_ASPECTS = /* @__PURE__ */ new Set([
128
1124
  "nodes",
129
1125
  "edges",
@@ -159,6 +1155,7 @@ const ALSO_KEPT = /* @__PURE__ */ new Set([
159
1155
  "metaData"
160
1156
  ]);
161
1157
  const TABLE_STYLE_ASPECTS = ["tableVisualProperties", "cyTableVisualProperties"];
1158
+ const OWN_EXTRA_KEYS = ["groups", "subnetwork"];
162
1159
  const CX_FIRST_KEYS = ["numberVerification", "metaData"];
163
1160
  const CX_ASPECT_KEYS = [
164
1161
  "nodes",
@@ -185,14 +1182,14 @@ const CX_ASPECT_KEYS = [
185
1182
  "supports",
186
1183
  "functionTerms"
187
1184
  ];
188
- const SCALARS = {
189
- string: "string",
190
- boolean: "boolean",
191
- integer: "integer",
192
- long: "long",
193
- double: "double",
194
- float: "double"
195
- };
1185
+ const SCALARS = /* @__PURE__ */ new Map([
1186
+ ["string", "string"],
1187
+ ["boolean", "boolean"],
1188
+ ["integer", "integer"],
1189
+ ["long", "long"],
1190
+ ["double", "double"],
1191
+ ["float", "double"]
1192
+ ]);
196
1193
  function cxType(d) {
197
1194
  if (d === void 0 || d === null) {
198
1195
  return { scalar: "string", list: false };
@@ -201,7 +1198,7 @@ function cxType(d) {
201
1198
  return null;
202
1199
  }
203
1200
  const list = d.startsWith("list_of_");
204
- const scalar = SCALARS[list ? d.slice("list_of_".length) : d];
1201
+ const scalar = SCALARS.get(list ? d.slice("list_of_".length) : d);
205
1202
  return scalar === void 0 ? null : { scalar, list };
206
1203
  }
207
1204
  function typeText(type) {
@@ -290,7 +1287,14 @@ function parseScalar(value, scalar, long, onPrecision) {
290
1287
  if (value === "Infinity" || value === "-Infinity") {
291
1288
  return Number(value);
292
1289
  }
293
- return DECIMAL_TEXT.test(value) ? Number(value) : BAD;
1290
+ if (!DECIMAL_TEXT.test(value)) {
1291
+ return BAD;
1292
+ }
1293
+ const n = Number(value);
1294
+ if (!Number.isFinite(n)) {
1295
+ onPrecision(value);
1296
+ }
1297
+ return n;
294
1298
  }
295
1299
  }
296
1300
  }
@@ -369,13 +1373,13 @@ function shown(value) {
369
1373
  function aspect(doc, name) {
370
1374
  return doc.aspects.get(name) ?? [];
371
1375
  }
372
- function inexactBits(text, keys) {
1376
+ function inexactBits(text, keys, depth = 1) {
373
1377
  if (!/[0-9][.eE]/.test(text)) {
374
1378
  return 0;
375
1379
  }
376
1380
  let bits = 0;
377
1381
  keys.forEach((key, i) => {
378
- if (inexactLiteral(text, key)) {
1382
+ if (inexactLiteral(text, key, depth)) {
379
1383
  bits |= 1 << i;
380
1384
  }
381
1385
  });
@@ -405,18 +1409,99 @@ function checkNumberVerification(value, count, report, line) {
405
1409
  );
406
1410
  }
407
1411
  }
1412
+ function isCxAspect(name) {
1413
+ return READ_ASPECTS.has(name) || OLD_ASPECT_NAMES.has(name) || CX_ASPECT_KEYS.includes(name) || TABLE_STYLE_ASPECTS.includes(name);
1414
+ }
1415
+ const ID_KEYS = /* @__PURE__ */ new Map([
1416
+ ["nodes", ["@id"]],
1417
+ ["edges", ["@id", "s", "t"]]
1418
+ ]);
1419
+ function fileAttribute(table, name, held, report) {
1420
+ const { value, line } = held;
1421
+ const n = isRecord(value) ? value.n : void 0;
1422
+ if (n === void 0 || n === null) {
1423
+ report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no attribute name n; skipped`, {
1424
+ line,
1425
+ element: name
1426
+ });
1427
+ return;
1428
+ }
1429
+ if (typeof n !== "string" || n === "") {
1430
+ report.error(
1431
+ "validation-error",
1432
+ BAD_VALUE_CODE,
1433
+ `a ${name} element has the attribute name n ${shown(n)}, not a non-empty string; skipped`,
1434
+ { line, element: name }
1435
+ );
1436
+ return;
1437
+ }
1438
+ if (!table.has(n)) {
1439
+ table.set(n, []);
1440
+ }
1441
+ table.get(n)?.push(held);
1442
+ }
1443
+ function collectElement(state, element) {
1444
+ const { doc, report } = state;
1445
+ const { name, value, text, line, exact, depth } = element;
1446
+ doc.structure.element(name, value, line);
1447
+ if (ALSO_KEPT.has(name) || !READ_ASPECTS.has(name)) {
1448
+ if (!doc.kept.has(name)) {
1449
+ doc.kept.set(name, []);
1450
+ }
1451
+ const onPrecision = name === "numberVerification" ? void 0 : state.onKeptPrecision;
1452
+ doc.kept.get(name)?.push(exact ? plainJson(value, onPrecision) : value);
1453
+ }
1454
+ if (!READ_ASPECTS.has(name) || name === "metaData" || name === "status") {
1455
+ return;
1456
+ }
1457
+ if (name === "numberVerification") {
1458
+ checkNumberVerification(value, ++state.verifications, report, line);
1459
+ return;
1460
+ }
1461
+ const ids = ID_KEYS.get(name);
1462
+ const held = { value, line, inexact: ids === void 0 ? 0 : inexactBits(text, ids, depth) };
1463
+ const table = attributeTable(doc, name);
1464
+ if (table !== null) {
1465
+ fileAttribute(table, name, held, report);
1466
+ return;
1467
+ }
1468
+ if (!doc.aspects.has(name)) {
1469
+ doc.aspects.set(name, []);
1470
+ }
1471
+ doc.aspects.get(name)?.push(held);
1472
+ }
408
1473
  async function readDocument(input, report, options) {
409
- const structure = new CxStructure(report);
1474
+ const structure = new CxStructure(report, (name) => OLD_ASPECT_NAMES.get(name) ?? name);
410
1475
  const doc = {
411
1476
  aspects: /* @__PURE__ */ new Map(),
412
1477
  attributes: { node: /* @__PURE__ */ new Map(), edge: /* @__PURE__ */ new Map(), network: /* @__PURE__ */ new Map() },
413
1478
  kept: /* @__PURE__ */ new Map(),
414
1479
  structure
415
1480
  };
1481
+ const state = {
1482
+ doc,
1483
+ report,
1484
+ verifications: 0,
1485
+ onKeptPrecision: keptPrecision(report, "meta.extra.cx")
1486
+ };
416
1487
  let sinceCheck = 0;
417
- let verifications = 0;
1488
+ let members = 0;
1489
+ let known = 0;
1490
+ const unknownNames = /* @__PURE__ */ new Set();
1491
+ const lastMember = { block: -1 };
1492
+ const countMember = (block, name) => {
1493
+ if (block !== lastMember.block) {
1494
+ lastMember.block = block;
1495
+ members++;
1496
+ }
1497
+ if (name !== null && isCxAspect(name)) {
1498
+ known++;
1499
+ } else if (name !== null) {
1500
+ unknownNames.add(name);
1501
+ }
1502
+ };
418
1503
  const canonical = (name, line) => {
419
- const renamed = OLD_ASPECT_NAMES[name];
1504
+ const renamed = OLD_ASPECT_NAMES.get(name);
420
1505
  if (renamed === void 0) {
421
1506
  return name;
422
1507
  }
@@ -429,51 +1514,11 @@ async function readDocument(input, report, options) {
429
1514
  );
430
1515
  return renamed;
431
1516
  };
432
- const collect = (name, value, text, line, exact) => {
433
- structure.element(name, value, line);
434
- if (ALSO_KEPT.has(name) || !READ_ASPECTS.has(name)) {
435
- if (!doc.kept.has(name)) {
436
- doc.kept.set(name, []);
437
- }
438
- doc.kept.get(name)?.push(exact ? plainJson(value) : value);
439
- }
440
- if (!READ_ASPECTS.has(name) || name === "metaData" || name === "status") {
441
- return;
442
- }
443
- if (name === "numberVerification") {
444
- checkNumberVerification(value, ++verifications, report, line);
445
- return;
446
- }
447
- let inexact = 0;
448
- if (name === "nodes") {
449
- inexact = inexactBits(text, ["@id"]);
450
- } else if (name === "edges") {
451
- inexact = inexactBits(text, ["@id", "s", "t"]);
452
- }
453
- const held = { value, line, inexact };
454
- const table = attributeTable(doc, name);
455
- if (table !== null) {
456
- if (!isRecord(value) || typeof value.n !== "string") {
457
- report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no attribute name n; skipped`, {
458
- line,
459
- element: name
460
- });
461
- return;
462
- }
463
- const key = value.n;
464
- if (!table.has(key)) {
465
- table.set(key, []);
466
- }
467
- table.get(key)?.push(held);
468
- return;
469
- }
470
- if (!doc.aspects.has(name)) {
471
- doc.aspects.set(name, []);
472
- }
473
- doc.aspects.get(name)?.push(held);
1517
+ const collect = (name, value, text, line, exact, depth = 1) => {
1518
+ collectElement(state, { name, value, text, line, exact, depth });
474
1519
  };
475
1520
  try {
476
- for await (const event of scanAspects(textChunks(input, report, options))) {
1521
+ for await (const event of scanAspects(textChunks(input, report, options), report)) {
477
1522
  if (++sinceCheck >= ABORT_CHECK_INTERVAL) {
478
1523
  sinceCheck = 0;
479
1524
  throwIfAborted(options.signal);
@@ -482,7 +1527,7 @@ async function readDocument(input, report, options) {
482
1527
  case "root":
483
1528
  report.fail(
484
1529
  CX_ISSUE.NOT_CX,
485
- `a CX document is a JSON array of aspects; found ${isRecord(event.value) ? "an object" : shown(event.value)}`,
1530
+ `a CX document is a JSON array of aspects; found ${event.object ? "an object" : shown(event.value)}`,
486
1531
  { line: event.line }
487
1532
  );
488
1533
  break;
@@ -497,11 +1542,21 @@ async function readDocument(input, report, options) {
497
1542
  }
498
1543
  const keys = isRecord(value) ? Object.keys(value) : [];
499
1544
  if (isRecord(value) && keys.length === 1 && isRecord(value[keys[0]])) {
1545
+ countMember(event.block, keys[0]);
500
1546
  const name = canonical(keys[0], event.line);
1547
+ if (isCxAspect(name)) {
1548
+ report.warning(
1549
+ "validation-error",
1550
+ CX_ISSUE.SINGLE_OBJECT_ASPECT,
1551
+ `the "${keys[0]}" aspect is one object, not an array of elements; read as one element`,
1552
+ { line: event.line, element: keys[0] }
1553
+ );
1554
+ }
501
1555
  structure.block(name, event.line);
502
- collect(name, value[keys[0]], "", event.line, event.exact);
1556
+ collect(name, value[keys[0]], event.text, event.line, event.exact, 2);
503
1557
  break;
504
1558
  }
1559
+ countMember(event.block, null);
505
1560
  report.error(
506
1561
  "parse-error",
507
1562
  BAD_ASPECT_BLOCK_CODE,
@@ -511,21 +1566,24 @@ async function readDocument(input, report, options) {
511
1566
  break;
512
1567
  }
513
1568
  case "block":
1569
+ countMember(event.block, event.aspect);
1570
+ reportSharedBlock(report, event);
514
1571
  structure.block(canonical(event.aspect, event.line), event.line);
515
1572
  break;
516
1573
  case "element": {
517
- const name = OLD_ASPECT_NAMES[event.aspect] ?? event.aspect;
1574
+ const name = OLD_ASPECT_NAMES.get(event.aspect) ?? event.aspect;
518
1575
  collect(name, event.value, event.text, event.line, event.exact);
519
1576
  break;
520
1577
  }
521
1578
  case "deep":
1579
+ countMember(event.block, event.aspect);
522
1580
  reportTooDeep(report, event);
523
1581
  break;
524
1582
  case "extraKeys":
525
1583
  report.error(
526
1584
  "parse-error",
527
1585
  BAD_ASPECT_BLOCK_CODE,
528
- `the "${event.aspect}" fragment holds more keys (${event.keys.join(", ")}); a fragment has one key, the others are skipped`,
1586
+ `the "${event.aspect}" fragment holds more keys (${event.keys.join(", ")}) whose values are not arrays; a fragment has one key, the others are skipped`,
529
1587
  { line: event.line, element: event.aspect }
530
1588
  );
531
1589
  break;
@@ -539,99 +1597,88 @@ async function readDocument(input, report, options) {
539
1597
  }
540
1598
  throw err;
541
1599
  }
1600
+ if (members > 0 && known === 0) {
1601
+ const found = [...unknownNames].slice(0, 5).map((n) => JSON.stringify(n));
1602
+ const hint = unknownNames.has("data") ? ": Cytoscape.js elements?" : "";
1603
+ const seen = found.length > 0 ? ` (found ${found.join(", ")}${hint})` : "";
1604
+ report.fail(CX_ISSUE.NOT_CX, `no member of the array is a CX aspect${seen}; the input is not CX`);
1605
+ }
1606
+ if (structure.hasStatus && !structure.statusWellFormed()) {
1607
+ report.error(
1608
+ "validation-error",
1609
+ BAD_VALUE_CODE,
1610
+ `the status element is ${shown(structure.status ?? null)}, not an object with a boolean success; ignored`,
1611
+ { element: "status" }
1612
+ );
1613
+ }
542
1614
  structure.checkCounts();
543
1615
  return doc;
544
1616
  }
545
1617
  function refId(raw) {
546
1618
  try {
547
- return cxId(raw).id;
548
- } catch {
549
- return null;
550
- }
551
- }
552
- function memberSet(raw) {
553
- if (raw === "all") {
554
- return null;
555
- }
556
- const out = /* @__PURE__ */ new Set();
557
- if (Array.isArray(raw)) {
558
- for (const item of raw) {
559
- const id = refId(item);
560
- if (id !== null) {
561
- out.add(id);
562
- }
563
- }
564
- }
565
- return out;
566
- }
567
- function graphPlans(doc) {
568
- const subs = /* @__PURE__ */ new Map();
569
- for (const { value } of aspect(doc, "cySubNetworks")) {
570
- const id = isRecord(value) ? refId(value["@id"]) : null;
571
- if (id !== null && isRecord(value) && !subs.has(id)) {
572
- subs.set(id, { nodes: memberSet(value.nodes), edges: memberSet(value.edges) });
573
- }
574
- }
575
- const relationNames = /* @__PURE__ */ new Map();
576
- const order = [];
577
- const views = /* @__PURE__ */ new Map();
578
- const addView = (sub, view) => {
579
- const list = views.get(sub) ?? [];
580
- if (!list.includes(view)) {
581
- list.push(view);
582
- }
583
- views.set(sub, list);
584
- };
585
- for (const { value } of aspect(doc, "cyNetworkRelations")) {
586
- if (!isRecord(value)) {
587
- continue;
588
- }
589
- const child = refId(value.c);
590
- if (child === null) {
591
- continue;
592
- }
593
- if (value.r === "view") {
594
- const parent = refId(value.p);
595
- if (parent !== null) {
596
- addView(parent, child);
597
- }
598
- continue;
599
- }
600
- if (typeof value.name === "string") {
601
- relationNames.set(child, value.name);
602
- }
603
- if (subs.has(child) && !order.includes(child)) {
604
- order.push(child);
605
- }
1619
+ return cxId(raw).id;
1620
+ } catch {
1621
+ return null;
606
1622
  }
607
- for (const { value } of aspect(doc, "cyViews")) {
608
- const view = isRecord(value) ? refId(value["@id"]) : null;
609
- const sub = isRecord(value) ? refId(value.s) : null;
610
- if (view !== null && sub !== null) {
611
- addView(sub, view);
612
- }
1623
+ }
1624
+ function memberSet(raw, onBad) {
1625
+ if (raw === "all") {
1626
+ return null;
613
1627
  }
614
- for (const id of subs.keys()) {
615
- if (!order.includes(id)) {
616
- order.push(id);
1628
+ const out = /* @__PURE__ */ new Set();
1629
+ if (Array.isArray(raw)) {
1630
+ for (const item of raw) {
1631
+ const id = refId(item);
1632
+ if (id === null) {
1633
+ onBad?.(CX_ISSUE.INVALID_ID, `the member ${shown(item)} is not an id; ignored`);
1634
+ } else {
1635
+ out.add(id);
1636
+ }
617
1637
  }
1638
+ } else if (raw !== void 0) {
1639
+ onBad?.(BAD_VALUE_CODE, `the member list ${shown(raw)} is neither a list of ids nor "all"; read as empty`);
618
1640
  }
619
- const networkName = (sub) => {
620
- let found = null;
621
- for (const { value } of doc.attributes.network.get("name") ?? []) {
622
- if (!isRecord(value) || typeof value.v !== "string") {
623
- continue;
624
- }
625
- const scope = value.s === void 0 ? null : refId(value.s);
626
- if (scope === sub) {
627
- return value.v;
1641
+ return out;
1642
+ }
1643
+ function structuralId(held, name, key, report) {
1644
+ const { value, line } = held;
1645
+ if (!isRecord(value)) {
1646
+ report.error(
1647
+ "parse-error",
1648
+ BAD_ASPECT_BLOCK_CODE,
1649
+ `a ${name} element is ${shown(value)}, not an object; skipped`,
1650
+ {
1651
+ line,
1652
+ element: name
628
1653
  }
629
- if (scope === null) {
630
- found ??= value.v;
1654
+ );
1655
+ return null;
1656
+ }
1657
+ if (value[key] === void 0 || value[key] === null) {
1658
+ report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no ${key}; skipped`, {
1659
+ line,
1660
+ element: name
1661
+ });
1662
+ return null;
1663
+ }
1664
+ const id = refId(value[key]);
1665
+ if (id === null) {
1666
+ report.error(
1667
+ "validation-error",
1668
+ CX_ISSUE.INVALID_ID,
1669
+ `a ${name} element has the ${key} ${shown(value[key])}, not an integer id; skipped`,
1670
+ {
1671
+ line,
1672
+ element: name
631
1673
  }
632
- }
633
- return found;
634
- };
1674
+ );
1675
+ return null;
1676
+ }
1677
+ return { value, id };
1678
+ }
1679
+ function graphPlans(doc, report) {
1680
+ const subs = readSubnetworks(doc, report);
1681
+ const { relationNames, order, views } = readRelations(doc, subs, report);
635
1682
  if (order.length <= 1) {
636
1683
  const sub = order.length === 1 ? order[0] : null;
637
1684
  const members = sub === null ? void 0 : subs.get(sub);
@@ -642,10 +1689,11 @@ function graphPlans(doc) {
642
1689
  if (graphViews.length === 0) {
643
1690
  graphViews = layoutViews(doc);
644
1691
  }
1692
+ const relationName = sub === null ? null : relationNames.get(sub) ?? null;
645
1693
  return [
646
1694
  {
647
1695
  index: 0,
648
- name: (sub === null ? null : relationNames.get(sub) ?? null) ?? networkName(sub),
1696
+ name: relationName ?? networkName(doc, sub),
649
1697
  subnetwork: sub,
650
1698
  nodes: members?.nodes ?? null,
651
1699
  edges: members?.edges ?? null,
@@ -655,13 +1703,99 @@ function graphPlans(doc) {
655
1703
  }
656
1704
  return order.map((sub, index) => ({
657
1705
  index,
658
- name: relationNames.get(sub) ?? networkName(sub),
1706
+ name: relationNames.get(sub) ?? networkName(doc, sub),
659
1707
  subnetwork: sub,
660
1708
  nodes: subs.get(sub)?.nodes ?? null,
661
1709
  edges: subs.get(sub)?.edges ?? null,
662
1710
  views: views.get(sub) ?? []
663
1711
  }));
664
1712
  }
1713
+ function readSubnetworks(doc, report) {
1714
+ const subs = /* @__PURE__ */ new Map();
1715
+ for (const held of aspect(doc, "cySubNetworks")) {
1716
+ const sub = structuralId(held, "cySubNetworks", "@id", report);
1717
+ if (sub === null) {
1718
+ continue;
1719
+ }
1720
+ const { value, id } = sub;
1721
+ if (subs.has(id)) {
1722
+ report.error(
1723
+ "validation-error",
1724
+ BAD_ASPECT_BLOCK_CODE,
1725
+ `a second cySubNetworks element has the @id ${String(id)}; skipped, the first is used`,
1726
+ { line: held.line, element: "cySubNetworks" }
1727
+ );
1728
+ continue;
1729
+ }
1730
+ const onBad = (code, message) => {
1731
+ report.error("validation-error", code, `subnetwork ${String(id)}: ${message}`, {
1732
+ line: held.line,
1733
+ element: "cySubNetworks"
1734
+ });
1735
+ };
1736
+ subs.set(id, { nodes: memberSet(value.nodes, onBad), edges: memberSet(value.edges, onBad) });
1737
+ }
1738
+ return subs;
1739
+ }
1740
+ function readRelations(doc, subs, report) {
1741
+ const relationNames = /* @__PURE__ */ new Map();
1742
+ const order = [];
1743
+ const views = /* @__PURE__ */ new Map();
1744
+ const addView = (sub, view) => {
1745
+ const list = views.get(sub) ?? [];
1746
+ if (!list.includes(view)) {
1747
+ list.push(view);
1748
+ }
1749
+ views.set(sub, list);
1750
+ };
1751
+ for (const held of aspect(doc, "cyNetworkRelations")) {
1752
+ const relation = structuralId(held, "cyNetworkRelations", "c", report);
1753
+ if (relation !== null) {
1754
+ fileRelation(relation, subs, { relationNames, order, addView });
1755
+ }
1756
+ }
1757
+ for (const held of aspect(doc, "cyViews")) {
1758
+ const view = structuralId(held, "cyViews", "@id", report);
1759
+ const sub = view === null ? null : refId(view.value.s);
1760
+ if (view !== null && sub !== null) {
1761
+ addView(sub, view.id);
1762
+ }
1763
+ }
1764
+ order.push(...[...subs.keys()].filter((id) => !order.includes(id)));
1765
+ return { relationNames, order, views };
1766
+ }
1767
+ function fileRelation(relation, subs, out) {
1768
+ const { value, id: child } = relation;
1769
+ if (value.r === "view") {
1770
+ const parent = refId(value.p);
1771
+ if (parent !== null) {
1772
+ out.addView(parent, child);
1773
+ }
1774
+ return;
1775
+ }
1776
+ if (typeof value.name === "string") {
1777
+ out.relationNames.set(child, value.name);
1778
+ }
1779
+ if (subs.has(child) && !out.order.includes(child)) {
1780
+ out.order.push(child);
1781
+ }
1782
+ }
1783
+ function networkName(doc, sub) {
1784
+ let found = null;
1785
+ for (const { value } of doc.attributes.network.get("name") ?? []) {
1786
+ if (!isRecord(value) || typeof value.v !== "string") {
1787
+ continue;
1788
+ }
1789
+ const scope = value.s === void 0 ? null : refId(value.s);
1790
+ if (scope === sub) {
1791
+ return value.v;
1792
+ }
1793
+ if (scope === null) {
1794
+ found ??= value.v;
1795
+ }
1796
+ }
1797
+ return found;
1798
+ }
665
1799
  function layoutViews(doc) {
666
1800
  const out = [];
667
1801
  for (const { value } of aspect(doc, "cartesianLayout")) {
@@ -685,11 +1819,13 @@ class CxReader {
685
1819
  * @param doc - the collected document
686
1820
  * @param plan - the graph to read
687
1821
  */
688
- constructor(sink, report, options, zAs, doc, plan) {
1822
+ constructor(sink, report, options, zAs, doc, plan2) {
689
1823
  this.rootNodes = /* @__PURE__ */ new Set();
690
1824
  this.rootEdges = /* @__PURE__ */ new Set();
691
1825
  this.nodeRows = /* @__PURE__ */ new Map();
1826
+ this.sinkRows = /* @__PURE__ */ new Map();
692
1827
  this.edgeRows = /* @__PURE__ */ new Map();
1828
+ this.sinkIds = /* @__PURE__ */ new Map();
693
1829
  this.subnetworks = /* @__PURE__ */ new Set();
694
1830
  this.dangling = /* @__PURE__ */ new Map();
695
1831
  this.nameHandle = INVALID_INDEX;
@@ -700,13 +1836,13 @@ class CxReader {
700
1836
  this.idType = "integer";
701
1837
  this.weighted = false;
702
1838
  this.coreValues = /* @__PURE__ */ new Map();
703
- this.weights = null;
1839
+ this.weights = /* @__PURE__ */ new Map();
704
1840
  this.sink = sink;
705
1841
  this.report = report;
706
1842
  this.options = options;
707
1843
  this.zAs = zAs;
708
1844
  this.doc = doc;
709
- this.plan = plan;
1845
+ this.plan = plan2;
710
1846
  this.direction = new DirectionResolver(sink, report, options.onMixedDirection);
711
1847
  this.coercer = new IdCoercer(options.ids);
712
1848
  for (const { value } of aspect(doc, "nodes")) {
@@ -734,6 +1870,7 @@ class CxReader {
734
1870
  this.direction.setHeader(true);
735
1871
  this.checkMembers();
736
1872
  this.checkRootOnly();
1873
+ this.checkTableColumns();
737
1874
  const nodeColumns = this.declareAttributes("node");
738
1875
  this.readNodes();
739
1876
  this.readGroups();
@@ -746,9 +1883,14 @@ class CxReader {
746
1883
  this.readVisualProperties();
747
1884
  this.readProvenance();
748
1885
  for (const [kind, count] of this.dangling) {
749
- report.warning("validation-error", DANGLING_REFERENCE_CODE, `${count} ${kind}(s) name nothing; ignored`, {
750
- element: kind
751
- });
1886
+ report.warning(
1887
+ "validation-error",
1888
+ DANGLING_REFERENCE_CODE,
1889
+ `${count} ${kind}${plural(count)} ${agree(count, "names", "name")} nothing; ignored`,
1890
+ {
1891
+ element: kind
1892
+ }
1893
+ );
752
1894
  }
753
1895
  this.setMeta();
754
1896
  }
@@ -775,15 +1917,6 @@ class CxReader {
775
1917
  inGraph(id) {
776
1918
  return this.plan.nodes === null || this.plan.nodes.has(id);
777
1919
  }
778
- /**
779
- * The key of the node and edge row maps for a CX id as an aspect writes it: the id the `ids`
780
- * option makes of it, which is what idOf() stored the element under.
781
- * @param raw - the CX id
782
- * @returns the row key
783
- */
784
- key(raw) {
785
- return this.options.ids === "keep" ? raw : this.coercer.value(raw);
786
- }
787
1920
  /**
788
1921
  * Whether a scoped value applies to this graph: unscoped, or scoped to its subnetwork. A scope
789
1922
  * naming no subnetwork is counted as dangling.
@@ -804,14 +1937,16 @@ class CxReader {
804
1937
  return 0;
805
1938
  }
806
1939
  /**
807
- * The id of an element, by the CX id rule and the `ids` option.
1940
+ * The id of an element by the CX id rule, as the document's references name it (the `ids`
1941
+ * option applies only to the id the sink holds, sinkId()).
808
1942
  * @param raw - the parsed id
809
1943
  * @param inexact - whether the literal was not a plain integer
810
1944
  * @param element - the element name for issues
1945
+ * @param line - the element's line
811
1946
  * @param edgeId - whether this is an edge's own id (stored in the f64 id column, not kept as digits)
812
1947
  * @returns the id, or null when it was reported
813
1948
  */
814
- idOf(raw, inexact, element, edgeId = false) {
1949
+ idOf(raw, inexact, element, line, edgeId = false) {
815
1950
  try {
816
1951
  const parsed = cxId(raw, inexact);
817
1952
  if (parsed.note === "text") {
@@ -819,14 +1954,14 @@ class CxReader {
819
1954
  "coercion",
820
1955
  ID_TEXT_TYPE_CODE,
821
1956
  `${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`,
822
- { element }
1957
+ { line, element }
823
1958
  );
824
1959
  } else if (parsed.note === "precision" && edgeId) {
825
1960
  this.report.warnOnce(
826
1961
  "precision",
827
1962
  PRECISION_CODE,
828
1963
  `${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`,
829
- { element },
1964
+ { line, element },
830
1965
  `${PRECISION_CODE}:edge`
831
1966
  );
832
1967
  } else if (parsed.note === "precision") {
@@ -835,29 +1970,93 @@ class CxReader {
835
1970
  "precision",
836
1971
  PRECISION_CODE,
837
1972
  `${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`,
838
- { element }
1973
+ { line, element }
839
1974
  );
840
1975
  }
841
- return this.options.ids === "keep" ? parsed.id : this.coercer.value(parsed.id);
1976
+ return parsed.id;
842
1977
  } catch (err) {
843
- this.report.recordError(err, { element });
1978
+ this.report.recordError(err, { line, element });
844
1979
  return null;
845
1980
  }
846
1981
  }
1982
+ /**
1983
+ * The id the sink holds a CX node under: its restored original id, else the `ids` option applied.
1984
+ * @param id - the CX id
1985
+ * @returns the sink id
1986
+ */
1987
+ sinkId(id) {
1988
+ const original = this.sinkIds.get(id);
1989
+ if (original !== void 0) {
1990
+ return original;
1991
+ }
1992
+ return this.options.ids === "keep" ? id : this.coercer.value(id);
1993
+ }
1994
+ /**
1995
+ * Add a node to the sink (once per sink id) and map its CX id to the row.
1996
+ * @param id - the CX id
1997
+ * @returns the row, and whether the sink id was new
1998
+ */
1999
+ addNode(id) {
2000
+ const sinkId = this.sinkId(id);
2001
+ const existing = this.sinkRows.get(sinkId);
2002
+ if (existing !== void 0) {
2003
+ this.nodeRows.set(id, existing);
2004
+ return { row: existing, added: false };
2005
+ }
2006
+ const row = this.sink.addNode(sinkId);
2007
+ this.sinkRows.set(sinkId, row);
2008
+ this.nodeRows.set(id, row);
2009
+ return { row, added: true };
2010
+ }
847
2011
  /**
848
2012
  * Record a long value beyond 2^53 (once).
849
2013
  * @param name - the attribute
850
2014
  * @param digits - the value
851
2015
  */
852
2016
  precision(name, digits) {
2017
+ const overflow = !Number.isFinite(Number(digits));
853
2018
  this.report.warnOnce(
854
2019
  "precision",
855
2020
  PRECISION_CODE,
856
- `"${name}": ${digits} is beyond 2^53; stored as the nearest double`,
2021
+ overflow ? `"${name}": ${digits} is beyond the range of a double; stored as ${String(Number(digits))}` : `"${name}": ${digits} is beyond 2^53; stored as the nearest double`,
857
2022
  { element: name },
858
2023
  `${PRECISION_CODE}:value`
859
2024
  );
860
2025
  }
2026
+ /**
2027
+ * Report the keys of a node or edge element CX does not define, once per key.
2028
+ * @param record - the element
2029
+ * @param known - the keys it defines
2030
+ * @param what - nodes or edges
2031
+ * @param line - its line
2032
+ */
2033
+ unknownKeys(record, known, what, line) {
2034
+ for (const key of Object.keys(record)) {
2035
+ if (!known.has(key)) {
2036
+ this.report.warnOnce(
2037
+ "unsupported",
2038
+ UNKNOWN_ELEMENT_CODE,
2039
+ `the ${what} key "${key}" is not defined by CX; its values are not read`,
2040
+ { line, element: key },
2041
+ `${UNKNOWN_ELEMENT_CODE}:${what}:${key}`
2042
+ );
2043
+ }
2044
+ }
2045
+ }
2046
+ /** Report cyTableColumn entries for a table CX does not define (once per table name). */
2047
+ checkTableColumns() {
2048
+ for (const { value, line } of aspect(this.doc, "cyTableColumn")) {
2049
+ if (isRecord(value) && typeof value.applies_to === "string" && !TABLES.has(value.applies_to)) {
2050
+ this.report.warnOnce(
2051
+ "unsupported",
2052
+ UNKNOWN_ELEMENT_CODE,
2053
+ `a cyTableColumn entry applies to "${value.applies_to}", which is not node_table, edge_table or network_table; ignored`,
2054
+ { line, element: value.applies_to },
2055
+ `${UNKNOWN_ELEMENT_CODE}:table:${value.applies_to}`
2056
+ );
2057
+ }
2058
+ }
2059
+ }
861
2060
  /** Count the subnetwork members that name no node or edge. */
862
2061
  checkMembers() {
863
2062
  for (const id of this.plan.nodes ?? []) {
@@ -902,7 +2101,7 @@ class CxReader {
902
2101
  this.report.warning(
903
2102
  "unsupported",
904
2103
  CX_ISSUE.ROOT_ONLY,
905
- `${nodes} node(s) and ${edges} edge(s) of the root network belong to no subnetwork (cySubNetworks); they are not read`,
2104
+ `${nodes} node${plural(nodes)} and ${edges} edge${plural(edges)} of the root network belong to no subnetwork (cySubNetworks); they are not read`,
906
2105
  { element: "cySubNetworks" }
907
2106
  );
908
2107
  }
@@ -930,7 +2129,7 @@ class CxReader {
930
2129
  const declared = this.tableColumns(domain === "node" ? "node_table" : "edge_table");
931
2130
  const names = /* @__PURE__ */ new Set([...declared.keys(), ...this.doc.attributes[domain].keys()]);
932
2131
  for (const name of names) {
933
- if (name === "" || domain === "edge" && name === this.options.weightFrom) {
2132
+ if (domain === "edge" && name === this.options.weightFrom || domain === "node" && name === ORIGINAL_ID_ATTRIBUTE && this.options.restoreMangledIds) {
934
2133
  continue;
935
2134
  }
936
2135
  const types = [];
@@ -1023,7 +2222,15 @@ class CxReader {
1023
2222
  continue;
1024
2223
  }
1025
2224
  const type = cxType(value.d);
1026
- if (type !== null) {
2225
+ if (type === null) {
2226
+ this.report.warnOnce(
2227
+ "unsupported",
2228
+ UNKNOWN_ATTR_TYPE_CODE,
2229
+ `cyTableColumn declares "${value.n}" with the data type ${shown(value.d)}, which CX does not define; its values are typed by their own d`,
2230
+ { element: value.n },
2231
+ `${UNKNOWN_ATTR_TYPE_CODE}:table:${table}:${value.n}`
2232
+ );
2233
+ } else {
1027
2234
  out.set(value.n, type);
1028
2235
  }
1029
2236
  }
@@ -1078,14 +2285,6 @@ class CxReader {
1078
2285
  if (scope === 0) {
1079
2286
  continue;
1080
2287
  }
1081
- const target = refId(value.po);
1082
- const row = target === null ? void 0 : rows.get(this.key(target));
1083
- if (row === void 0) {
1084
- if (target === null || !root.has(target)) {
1085
- this.dangle(`${domain} attribute target`);
1086
- }
1087
- continue;
1088
- }
1089
2288
  if (value.v === void 0) {
1090
2289
  this.report.error(
1091
2290
  "missing-value",
@@ -1098,23 +2297,33 @@ class CxReader {
1098
2297
  );
1099
2298
  continue;
1100
2299
  }
1101
- if (value.v === null) {
1102
- continue;
1103
- }
1104
- const previous = chosen.get(row);
1105
- if (previous !== void 0 && previous.scope > scope) {
1106
- continue;
1107
- }
1108
- if (previous?.scope === scope && JSON.stringify(previous.value) !== JSON.stringify(plainJson(value.v))) {
1109
- this.report.warnOnce(
1110
- "validation-error",
1111
- DUPLICATE_ATTRIBUTE_CODE,
1112
- `${domain} ${shown(value.po)} has the attribute "${column.name}" twice; the later value wins`,
1113
- { line, element: column.name },
1114
- `${DUPLICATE_ATTRIBUTE_CODE}:${domain}:${column.name}`
1115
- );
2300
+ for (const raw of Array.isArray(value.po) ? value.po : [value.po]) {
2301
+ const target = refId(raw);
2302
+ const row = target === null ? void 0 : rows.get(target);
2303
+ if (row === void 0) {
2304
+ if (target === null || !root.has(target)) {
2305
+ this.dangle(`${domain} attribute target`);
2306
+ }
2307
+ continue;
2308
+ }
2309
+ if (value.v === null) {
2310
+ continue;
2311
+ }
2312
+ const previous = chosen.get(row);
2313
+ if (previous !== void 0 && previous.scope > scope) {
2314
+ continue;
2315
+ }
2316
+ if (previous?.scope === scope && JSON.stringify(previous.value) !== JSON.stringify(plainJson(value.v))) {
2317
+ this.report.warnOnce(
2318
+ "validation-error",
2319
+ DUPLICATE_ATTRIBUTE_CODE,
2320
+ `${domain} ${shown(raw)} has the attribute "${column.name}" twice; the later value wins`,
2321
+ { line, element: column.name },
2322
+ `${DUPLICATE_ATTRIBUTE_CODE}:${domain}:${column.name}`
2323
+ );
2324
+ }
2325
+ chosen.set(row, { scope, value: value.v, d: value.d, line });
1116
2326
  }
1117
- chosen.set(row, { scope, value: value.v, d: value.d, line });
1118
2327
  }
1119
2328
  for (const [row, { value, d, line }] of chosen) {
1120
2329
  this.writeCell(domain, column, row, value, d, line);
@@ -1160,6 +2369,15 @@ class CxReader {
1160
2369
  }
1161
2370
  let { handle } = column;
1162
2371
  if (this.isStructural(domain, column.name)) {
2372
+ if (Array.isArray(raw)) {
2373
+ this.report.error(
2374
+ "validation-error",
2375
+ BAD_VALUE_CODE,
2376
+ `${element}: ${shown(raw)} is a list; "${column.name}" holds one string, the cell is unset`,
2377
+ { line, element: column.name }
2378
+ );
2379
+ return;
2380
+ }
1163
2381
  handle = this.coreColumn(domain, column.name);
1164
2382
  const prior = this.coreValue(domain, row, column.name);
1165
2383
  if (prior !== void 0 && prior !== value) {
@@ -1229,7 +2447,8 @@ class CxReader {
1229
2447
  // ------------------------------------------------------------ nodes, groups, layout
1230
2448
  /** Read the nodes of this graph. */
1231
2449
  readNodes() {
1232
- const { report, sink } = this;
2450
+ const { report } = this;
2451
+ const originals = this.originalIds();
1233
2452
  for (const held of aspect(this.doc, "nodes")) {
1234
2453
  this.checkAbort();
1235
2454
  const { value, line } = held;
@@ -1251,38 +2470,64 @@ class CxReader {
1251
2470
  report.counts.skippedNodes++;
1252
2471
  continue;
1253
2472
  }
2473
+ this.unknownKeys(value, NODE_KEYS, "node", line);
1254
2474
  const element = `node ${shown(value["@id"])}`;
1255
- const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element);
2475
+ const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, line);
1256
2476
  if (id === null) {
1257
2477
  report.counts.skippedNodes++;
1258
2478
  continue;
1259
2479
  }
1260
- if (!this.inGraph(refId(value["@id"]) ?? id)) {
2480
+ if (!this.inGraph(id)) {
1261
2481
  continue;
1262
2482
  }
1263
2483
  let row = this.nodeRows.get(id);
1264
- if (row !== void 0) {
1265
- report.warning(
1266
- "merged",
1267
- DUPLICATE_NODE_CODE,
1268
- `${element} is declared more than once; its fields are merged (the later values win)`,
1269
- { line, element }
1270
- );
1271
- } else {
2484
+ let added = false;
2485
+ if (row === void 0) {
2486
+ const original = originals.get(refId(value["@id"]) ?? id);
2487
+ if (original !== void 0) {
2488
+ this.idType = "mixed";
2489
+ this.sinkIds.set(id, original);
2490
+ }
1272
2491
  try {
1273
- row = sink.addNode(id);
2492
+ ({ row, added } = this.addNode(id));
1274
2493
  } catch (err) {
1275
2494
  report.recordError(err, { line, element });
1276
2495
  report.counts.skippedNodes++;
1277
2496
  continue;
1278
2497
  }
1279
- this.nodeRows.set(id, row);
2498
+ }
2499
+ if (added) {
1280
2500
  report.counts.nodes++;
2501
+ } else {
2502
+ report.warning(
2503
+ "merged",
2504
+ DUPLICATE_NODE_CODE,
2505
+ `${element} is declared more than once; its fields are merged (the later values win)`,
2506
+ { line, element }
2507
+ );
1281
2508
  }
1282
2509
  this.writeCore("node", row, "name", value.n, element, line);
1283
2510
  this.writeCore("node", row, "represents", value.r, element, line);
1284
2511
  }
1285
2512
  }
2513
+ /**
2514
+ * The original ids the exporter's `sanitizeIds: "mangle"` kept in the `graphty:originalId`
2515
+ * attribute, when they are to be restored.
2516
+ * @returns CX node id -> original id
2517
+ */
2518
+ originalIds() {
2519
+ const out = /* @__PURE__ */ new Map();
2520
+ if (!this.options.restoreMangledIds) {
2521
+ return out;
2522
+ }
2523
+ for (const { value } of this.doc.attributes.node.get(ORIGINAL_ID_ATTRIBUTE) ?? []) {
2524
+ const po = isRecord(value) && this.scopePeek(value.s) !== 0 ? refId(value.po) : null;
2525
+ if (po !== null && isRecord(value) && typeof value.v === "string") {
2526
+ out.set(po, value.v);
2527
+ }
2528
+ }
2529
+ return out;
2530
+ }
1286
2531
  /**
1287
2532
  * Write a core string field (n, r, i).
1288
2533
  * @param domain - node or edge
@@ -1334,30 +2579,40 @@ class CxReader {
1334
2579
  }
1335
2580
  const parents = /* @__PURE__ */ new Map();
1336
2581
  const collapsed = [];
1337
- for (const { value, line } of groups) {
1338
- if (!isRecord(value)) {
2582
+ for (const held of groups) {
2583
+ const group = structuralId(held, "cyGroups", "@id", this.report);
2584
+ if (group === null) {
1339
2585
  continue;
1340
2586
  }
1341
- const id = refId(value["@id"]);
1342
- if (id === null) {
1343
- this.report.error("missing-value", MISSING_ID_CODE, "a cyGroups element has no @id", {
1344
- line,
1345
- element: "cyGroups"
1346
- });
1347
- continue;
2587
+ const { value, id } = group;
2588
+ const { line } = held;
2589
+ if (value.nodes !== void 0 && value.nodes !== null && !Array.isArray(value.nodes)) {
2590
+ this.report.error(
2591
+ "validation-error",
2592
+ BAD_VALUE_CODE,
2593
+ `group ${String(id)}: nodes is ${shown(value.nodes)}, not a list of node ids; no member is read`,
2594
+ { line, element: String(id) }
2595
+ );
2596
+ }
2597
+ if (value.collapsed !== void 0 && value.collapsed !== null && typeof value.collapsed !== "boolean") {
2598
+ this.report.error(
2599
+ "validation-error",
2600
+ BAD_VALUE_CODE,
2601
+ `group ${String(id)}: collapsed is ${shown(value.collapsed)}, not a boolean; unset`,
2602
+ { line, element: String(id) }
2603
+ );
1348
2604
  }
1349
- let groupRow = this.nodeRows.get(this.key(id));
2605
+ let groupRow = this.nodeRows.get(id);
1350
2606
  if (groupRow === void 0) {
1351
2607
  if (this.rootNodes.has(id) || !this.hasMemberIn(value.nodes)) {
1352
2608
  continue;
1353
2609
  }
1354
2610
  try {
1355
- groupRow = this.sink.addNode(this.key(id));
2611
+ groupRow = this.addNode(id).row;
1356
2612
  } catch (err) {
1357
2613
  this.report.recordError(err, { line, element: String(id) });
1358
2614
  continue;
1359
2615
  }
1360
- this.nodeRows.set(this.key(id), groupRow);
1361
2616
  this.report.counts.nodes++;
1362
2617
  this.report.warning(
1363
2618
  "coercion",
@@ -1374,7 +2629,7 @@ class CxReader {
1374
2629
  }
1375
2630
  for (const raw of Array.isArray(value.nodes) ? value.nodes : []) {
1376
2631
  const member = refId(raw);
1377
- const row = member === null ? void 0 : this.nodeRows.get(this.key(member));
2632
+ const row = member === null ? void 0 : this.nodeRows.get(member);
1378
2633
  if (row === void 0) {
1379
2634
  if (member === null || !this.rootNodes.has(member)) {
1380
2635
  this.report.error(
@@ -1428,7 +2683,7 @@ class CxReader {
1428
2683
  hasMemberIn(raw) {
1429
2684
  return Array.isArray(raw) && raw.some((item) => {
1430
2685
  const id = refId(item);
1431
- return id !== null && this.nodeRows.has(this.key(id));
2686
+ return id !== null && this.nodeRows.has(id);
1432
2687
  });
1433
2688
  }
1434
2689
  /**
@@ -1489,6 +2744,12 @@ class CxReader {
1489
2744
  for (const { value, line } of entries) {
1490
2745
  this.checkAbort();
1491
2746
  if (!isRecord(value)) {
2747
+ this.report.error(
2748
+ "parse-error",
2749
+ BAD_ASPECT_BLOCK_CODE,
2750
+ `a cartesianLayout element is ${shown(value)}, not an object; skipped`,
2751
+ { line, element: "cartesianLayout" }
2752
+ );
1492
2753
  continue;
1493
2754
  }
1494
2755
  const view = value.view === void 0 || value.view === null ? null : refId(value.view);
@@ -1509,14 +2770,31 @@ class CxReader {
1509
2770
  );
1510
2771
  continue;
1511
2772
  }
2773
+ if (!fitsF32(value.x) || !fitsF32(value.y)) {
2774
+ this.report.error(
2775
+ "validation-error",
2776
+ BAD_VALUE_CODE,
2777
+ `a cartesianLayout element for node ${shown(value.node)} has the coordinates ${String(value.x)}, ${String(value.y)}, beyond what the f32 position column holds; skipped`,
2778
+ { line, element: "cartesianLayout" }
2779
+ );
2780
+ continue;
2781
+ }
1512
2782
  const node = refId(value.node);
1513
- const row = node === null ? void 0 : this.nodeRows.get(this.key(node));
2783
+ const row = node === null ? void 0 : this.nodeRows.get(node);
1514
2784
  if (row === void 0) {
1515
2785
  if (node === null || !this.rootNodes.has(node)) {
1516
2786
  this.dangle("layout entry");
1517
2787
  }
1518
2788
  continue;
1519
2789
  }
2790
+ if (value.z !== void 0 && value.z !== null && typeof value.z !== "number") {
2791
+ this.report.error(
2792
+ "validation-error",
2793
+ BAD_VALUE_CODE,
2794
+ `a cartesianLayout element for node ${shown(value.node)} has the z ${shown(value.z)}, not a number; z is unset`,
2795
+ { line, element: "cartesianLayout" }
2796
+ );
2797
+ }
1520
2798
  const key = `${slot}:${row}`;
1521
2799
  if (seen.has(key)) {
1522
2800
  this.report.warnOnce(
@@ -1588,6 +2866,7 @@ class CxReader {
1588
2866
  readEdges() {
1589
2867
  const { report, sink } = this;
1590
2868
  const members = this.plan.edges;
2869
+ this.weights = this.edgeWeights();
1591
2870
  for (const held of aspect(this.doc, "edges")) {
1592
2871
  this.checkAbort();
1593
2872
  const { value, line } = held;
@@ -1609,13 +2888,14 @@ class CxReader {
1609
2888
  report.counts.skippedEdges++;
1610
2889
  continue;
1611
2890
  }
2891
+ this.unknownKeys(value, EDGE_KEYS, "edge", line);
1612
2892
  const element = `edge ${shown(value["@id"])}`;
1613
- const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, true);
2893
+ const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, line, true);
1614
2894
  if (id === null) {
1615
2895
  report.counts.skippedEdges++;
1616
2896
  continue;
1617
2897
  }
1618
- if (members !== null && !members.has(refId(value["@id"]) ?? id)) {
2898
+ if (members !== null && !members.has(id)) {
1619
2899
  continue;
1620
2900
  }
1621
2901
  if (value.s === void 0 || value.s === null || value.t === void 0 || value.t === null) {
@@ -1631,8 +2911,8 @@ class CxReader {
1631
2911
  report.counts.skippedEdges++;
1632
2912
  continue;
1633
2913
  }
1634
- const s = this.idOf(value.s, (held.inexact & 2) !== 0, element);
1635
- const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element);
2914
+ const s = this.idOf(value.s, (held.inexact & 2) !== 0, element, line);
2915
+ const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element, line);
1636
2916
  if (s === null || t === null) {
1637
2917
  report.counts.skippedEdges++;
1638
2918
  continue;
@@ -1662,7 +2942,7 @@ class CxReader {
1662
2942
  const before = sink.edgeCount;
1663
2943
  let edge;
1664
2944
  try {
1665
- edge = this.direction.addEdge(s, t, "directed", weight, { line, element });
2945
+ edge = this.direction.addEdge(this.sinkId(s), this.sinkId(t), "directed", weight, { line, element });
1666
2946
  } catch (err) {
1667
2947
  report.recordError(err, { line, element });
1668
2948
  report.counts.skippedEdges++;
@@ -1704,34 +2984,76 @@ class CxReader {
1704
2984
  return false;
1705
2985
  }
1706
2986
  try {
1707
- this.nodeRows.set(id, this.sink.addNode(id));
2987
+ if (this.addNode(id).added) {
2988
+ this.report.counts.nodes++;
2989
+ }
1708
2990
  } catch (err) {
1709
2991
  this.report.recordError(err, { line, element });
1710
2992
  return false;
1711
2993
  }
1712
- this.report.counts.nodes++;
1713
2994
  return true;
1714
2995
  }
1715
2996
  /**
1716
- * The weight of an edge: its weightFrom attribute, parsed as a number.
1717
- * @param id - the edge id
1718
- * @returns the weight, or undefined; E_INVALID_WEIGHT for a non-number
2997
+ * The weightFrom attribute values by edge id: this subnetwork's own value beats the unscoped
2998
+ * one, and of two in one scope the later wins (W_DUPLICATE_ATTRIBUTE when they differ), as
2999
+ * writeAttributes() rules every other attribute.
3000
+ * @returns the values (parsed JSON) by edge id
1719
3001
  */
1720
- weightOf(id) {
3002
+ edgeWeights() {
1721
3003
  const { weightFrom } = this.options;
3004
+ const chosen = /* @__PURE__ */ new Map();
1722
3005
  if (weightFrom === null) {
1723
- return void 0;
3006
+ return /* @__PURE__ */ new Map();
1724
3007
  }
1725
- if (this.weights === null) {
1726
- this.weights = /* @__PURE__ */ new Map();
1727
- for (const { value } of this.doc.attributes.edge.get(weightFrom) ?? []) {
1728
- if (isRecord(value) && this.scopePeek(value.s) !== 0) {
1729
- const po = refId(value.po);
1730
- if (po !== null && value.v !== void 0 && value.v !== null) {
1731
- this.weights.set(this.key(po), plainJson(value.v));
1732
- }
1733
- }
3008
+ for (const { value, line } of this.doc.attributes.edge.get(weightFrom) ?? []) {
3009
+ const scope = isRecord(value) ? this.scopePeek(value.s) : 0;
3010
+ if (!isRecord(value) || scope === 0 || value.v === void 0 || value.v === null) {
3011
+ continue;
1734
3012
  }
3013
+ const v = plainJson(value.v);
3014
+ for (const raw of Array.isArray(value.po) ? value.po : [value.po]) {
3015
+ this.chooseWeight(chosen, raw, { scope, value: v }, line, weightFrom);
3016
+ }
3017
+ }
3018
+ return new Map([...chosen].map(([id, { value }]) => [id, value]));
3019
+ }
3020
+ /**
3021
+ * Keep one weight attribute element's value for one edge, unless a more specific scope already
3022
+ * gave it one; a second value in the same scope wins, with a warning.
3023
+ * @param chosen - the value chosen so far per edge
3024
+ * @param raw - the edge reference as written
3025
+ * @param weight - the value and the scope it applies in
3026
+ * @param weight.scope - the scope: higher is more specific
3027
+ * @param weight.value - the value
3028
+ * @param line - the element's line
3029
+ * @param weightFrom - the attribute name
3030
+ */
3031
+ chooseWeight(chosen, raw, weight, line, weightFrom) {
3032
+ const po = refId(raw);
3033
+ const previous = po === null ? void 0 : chosen.get(po);
3034
+ if (po === null || previous !== void 0 && previous.scope > weight.scope) {
3035
+ return;
3036
+ }
3037
+ if (previous?.scope === weight.scope && JSON.stringify(previous.value) !== JSON.stringify(weight.value)) {
3038
+ this.report.warnOnce(
3039
+ "validation-error",
3040
+ DUPLICATE_ATTRIBUTE_CODE,
3041
+ `edge ${shown(raw)} has the attribute "${weightFrom}" twice; the later value wins`,
3042
+ { line, element: weightFrom },
3043
+ `${DUPLICATE_ATTRIBUTE_CODE}:edge:${weightFrom}`
3044
+ );
3045
+ }
3046
+ chosen.set(po, weight);
3047
+ }
3048
+ /**
3049
+ * The weight of an edge: its weightFrom attribute, parsed as a number (the declared d is not
3050
+ * consulted: a value that is not a number is E_INVALID_WEIGHT whatever its type says).
3051
+ * @param id - the edge id
3052
+ * @returns the weight, or undefined; E_INVALID_WEIGHT for a non-number
3053
+ */
3054
+ weightOf(id) {
3055
+ if (this.options.weightFrom === null) {
3056
+ return void 0;
1735
3057
  }
1736
3058
  const raw = this.weights.get(id);
1737
3059
  if (typeof raw === "string" && isNullText(raw)) {
@@ -1745,17 +3067,38 @@ class CxReader {
1745
3067
  for (const [name, elements] of this.doc.attributes.network) {
1746
3068
  let chosen = null;
1747
3069
  let chosenScope = 0;
1748
- for (const { value } of elements) {
3070
+ for (const { value, line } of elements) {
1749
3071
  if (!isRecord(value)) {
1750
3072
  continue;
1751
3073
  }
1752
3074
  const scope = this.scopeOf(value.s);
1753
- if (scope !== 0 && scope >= chosenScope) {
3075
+ if (scope === 0) {
3076
+ continue;
3077
+ }
3078
+ if (value.v === void 0) {
3079
+ this.report.error(
3080
+ "missing-value",
3081
+ BAD_VALUE_CODE,
3082
+ `a networkAttributes element for "${name}" has no v; skipped`,
3083
+ { line, element: name }
3084
+ );
3085
+ continue;
3086
+ }
3087
+ if (chosen !== null && scope === chosenScope && JSON.stringify(plainJson(chosen.v)) !== JSON.stringify(plainJson(value.v))) {
3088
+ this.report.warnOnce(
3089
+ "validation-error",
3090
+ DUPLICATE_ATTRIBUTE_CODE,
3091
+ `the network attribute "${name}" is given twice; the later value wins`,
3092
+ { line, element: name },
3093
+ `${DUPLICATE_ATTRIBUTE_CODE}:network:${name}`
3094
+ );
3095
+ }
3096
+ if (scope >= chosenScope) {
1754
3097
  chosen = value;
1755
3098
  chosenScope = scope;
1756
3099
  }
1757
3100
  }
1758
- if (chosen === null || chosen.v === void 0 || chosen.v === null || name === "") {
3101
+ if (chosen?.v === void 0 || chosen.v === null) {
1759
3102
  continue;
1760
3103
  }
1761
3104
  const type = cxType(chosen.d);
@@ -1830,7 +3173,7 @@ class CxReader {
1830
3173
  const domain = value.properties_of === "nodes" ? "node" : "edge";
1831
3174
  const target = refId(value.applies_to);
1832
3175
  const rows = domain === "node" ? this.nodeRows : this.edgeRows;
1833
- const row = target === null ? void 0 : rows.get(this.key(target));
3176
+ const row = target === null ? void 0 : rows.get(target);
1834
3177
  if (row === void 0) {
1835
3178
  if (target === null || !(domain === "node" ? this.rootNodes : this.rootEdges).has(target)) {
1836
3179
  this.dangle(`${domain} visual property entry`);
@@ -1863,7 +3206,7 @@ class CxReader {
1863
3206
  name: property,
1864
3207
  dtype: "string",
1865
3208
  nullable: true,
1866
- origin: { format: CX_FORMAT, id: null, namespace: CX_BYPASS_NAMESPACE }
3209
+ origin: { format: CX_FORMAT, id: property, namespace: CX_BYPASS_NAMESPACE }
1867
3210
  },
1868
3211
  this.report
1869
3212
  );
@@ -1888,15 +3231,15 @@ class CxReader {
1888
3231
  }
1889
3232
  if (defaults + mappings + dependencies + tableStyles > 0) {
1890
3233
  const parts = [
1891
- `cyVisualProperties: ${defaults} default(s), ${mappings} mapping(s), ${dependencies} dependenc(ies)`
3234
+ `cyVisualProperties: ${defaults} default${plural(defaults)}, ${mappings} mapping${plural(mappings)}, ${dependencies} dependenc(ies)`
1892
3235
  ];
1893
3236
  if (tableStyles > 0) {
1894
- parts.push(`${tableStyles} table style element(s)`);
3237
+ parts.push(`${tableStyles} table style element${plural(tableStyles)}`);
1895
3238
  }
1896
3239
  this.report.warning(
1897
3240
  "unsupported",
1898
3241
  STYLES_NOT_IMPORTED_CODE,
1899
- `the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx (style import is issue #706)`,
3242
+ `the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx`,
1900
3243
  { element: "cyVisualProperties" }
1901
3244
  );
1902
3245
  }
@@ -1924,7 +3267,7 @@ class CxReader {
1924
3267
  continue;
1925
3268
  }
1926
3269
  const po = refId(value.po);
1927
- const row = po === null ? void 0 : this.nodeRows.get(this.key(po));
3270
+ const row = po === null ? void 0 : this.nodeRows.get(po);
1928
3271
  if (row === void 0) {
1929
3272
  if (po === null || !this.rootNodes.has(po)) {
1930
3273
  this.dangle("functionTerms entry");
@@ -1942,8 +3285,8 @@ class CxReader {
1942
3285
  }
1943
3286
  const node = refId(value.node);
1944
3287
  const edge = refId(value.edge);
1945
- const row = node === null ? void 0 : this.nodeRows.get(this.key(node));
1946
- const edgeRow = edge === null ? void 0 : this.edgeRows.get(this.key(edge));
3288
+ const row = node === null ? void 0 : this.nodeRows.get(node);
3289
+ const edgeRow = edge === null ? void 0 : this.edgeRows.get(edge);
1947
3290
  if (row === void 0 || edgeRow === void 0) {
1948
3291
  if (node === null || !this.rootNodes.has(node) || edge === null || !this.rootEdges.has(edge)) {
1949
3292
  this.dangle("reifiedEdges entry");
@@ -1981,16 +3324,35 @@ class CxReader {
1981
3324
  return;
1982
3325
  }
1983
3326
  const fields = [...new Set(records.flatMap((r) => Object.keys(r)).filter((k) => k !== "@id"))];
3327
+ const names = /* @__PURE__ */ new Set(["id"]);
1984
3328
  const decls = [
1985
3329
  { name: "id", dtype: "f64", nullable: true },
1986
- ...fields.map((field) => ({ name: field, dtype: "json", nullable: true }))
3330
+ ...fields.map((field) => {
3331
+ const column = uniqueColumnName(field, null, (n) => names.has(n));
3332
+ names.add(column);
3333
+ if (column !== field) {
3334
+ this.report.warning(
3335
+ "coercion",
3336
+ COLUMN_RENAMED_CODE,
3337
+ `${name} column "${field}" renamed to "${column}": the table's own id column holds that name`,
3338
+ { element: field }
3339
+ );
3340
+ }
3341
+ return { name: column, dtype: "json", nullable: true };
3342
+ })
1987
3343
  ];
1988
3344
  const table = this.sink.addExtensionTable(name, decls);
3345
+ const precision = (digits) => {
3346
+ this.precision(name, digits);
3347
+ };
1989
3348
  for (const record of records) {
1990
3349
  const id = refId(record["@id"]);
3350
+ if (typeof id === "string") {
3351
+ precision(id);
3352
+ }
1991
3353
  this.sink.addExtensionRow(table, [
1992
3354
  id === null ? null : Number(id),
1993
- ...fields.map((field) => record[field] === void 0 ? null : plainJson(record[field]))
3355
+ ...fields.map((field) => record[field] === void 0 ? null : plainJson(record[field], precision))
1994
3356
  ]);
1995
3357
  }
1996
3358
  }
@@ -2012,7 +3374,7 @@ class CxReader {
2012
3374
  const ids = (Array.isArray(value[column]) ? value[column] : []).map(refId).filter((id) => id !== null).map(Number);
2013
3375
  for (const raw of targets) {
2014
3376
  const target = refId(raw);
2015
- const row = target === null ? void 0 : rows.get(this.key(target));
3377
+ const row = target === null ? void 0 : rows.get(target);
2016
3378
  if (row === void 0) {
2017
3379
  if (target === null || !root.has(target)) {
2018
3380
  this.dangle(`${aspectName} entry`);
@@ -2071,12 +3433,24 @@ class CxReader {
2071
3433
  };
2072
3434
  const extra = Object.fromEntries(this.doc.kept);
2073
3435
  const groups = aspect(this.doc, "cyGroups").map((h) => plainJson(h.value));
3436
+ const own = {};
2074
3437
  if (groups.length > 0) {
2075
- extra.groups = groups;
3438
+ own.groups = groups;
2076
3439
  }
2077
3440
  if (this.plan.subnetwork !== null) {
2078
- extra.subnetwork = this.plan.subnetwork;
3441
+ own.subnetwork = this.plan.subnetwork;
3442
+ }
3443
+ for (const key of OWN_EXTRA_KEYS) {
3444
+ if (key in own && this.doc.kept.has(key)) {
3445
+ this.report.warning(
3446
+ "unsupported",
3447
+ UNKNOWN_ELEMENT_CODE,
3448
+ `the aspect "${key}" is not kept: meta.extra.cx.${key} holds the importer's own ${key === "groups" ? "cyGroups" : "subnetwork"} data`,
3449
+ { element: key }
3450
+ );
3451
+ }
2079
3452
  }
3453
+ Object.assign(extra, own);
2080
3454
  const { weightFrom } = this.options;
2081
3455
  const name = this.plan.name ?? describe("name");
2082
3456
  const description = describe("description");
@@ -2098,7 +3472,7 @@ async function run(input, options, sinkFor, pick) {
2098
3472
  const first = new ImportReportBuilder(CX_FORMAT, resolved.errorLimit);
2099
3473
  reportUnusedOptions(options, first, USED_OPTIONS);
2100
3474
  const doc = await readDocument(input, first, resolved);
2101
- const plans = graphPlans(doc);
3475
+ const plans = graphPlans(doc, first);
2102
3476
  const chosen = pick(plans, first);
2103
3477
  const reports = [];
2104
3478
  chosen.forEach((index, k) => {
@@ -2113,6 +3487,7 @@ async function run(input, options, sinkFor, pick) {
2113
3487
  }
2114
3488
  const cxImporter = Object.freeze({
2115
3489
  format: CX_FORMAT,
3490
+ options: Object.freeze(["zAs"]),
2116
3491
  extensions: Object.freeze([".cx"]),
2117
3492
  mimeTypes: Object.freeze(["application/json"]),
2118
3493
  /**
@@ -2123,8 +3498,7 @@ const cxImporter = Object.freeze({
2123
3498
  * @returns the confidence
2124
3499
  */
2125
3500
  sniff(head) {
2126
- const text = new TextDecoder("utf-8").decode(head.subarray(0, 1024)).replace(/^\uFEFF/, "");
2127
- const match = /^\s*\[\s*\{\s*"((?:[^"\\]|\\.)*)"\s*:/.exec(text);
3501
+ const match = /^\s*\[\s*\{\s*"((?:[^"\\]|\\.)*)"\s*:/.exec(headText(head));
2128
3502
  if (match === null) {
2129
3503
  return 0;
2130
3504
  }
@@ -2152,11 +3526,11 @@ const cxImporter = Object.freeze({
2152
3526
  options,
2153
3527
  first
2154
3528
  );
2155
- if (plans.length > 1) {
3529
+ if (plans.length > 1 && !graphChosen(options)) {
2156
3530
  first.warning(
2157
3531
  "unsupported",
2158
3532
  MULTIPLE_GRAPHS_CODE,
2159
- `the collection holds ${plans.length} subnetworks; graph ${index} is read and ${plans.length - 1} skipped (importAll() reads every one)`
3533
+ `the collection holds ${plans.length} subnetworks; graph ${index} is read and ${plans.length - 1} skipped (importAllGraphs() reads every one)`
2160
3534
  );
2161
3535
  }
2162
3536
  return [index];
@@ -2187,18 +3561,21 @@ const cxImporter = Object.freeze({
2187
3561
  const doc = await readDocument(input, report, resolved);
2188
3562
  const nodes = aspect(doc, "nodes").length;
2189
3563
  const edges = aspect(doc, "edges").length;
2190
- return graphPlans(doc).map(
2191
- (plan) => Object.freeze({
2192
- index: plan.index,
2193
- name: plan.name,
2194
- nodes: memberCount(plan.nodes, nodes),
2195
- edges: memberCount(plan.edges, edges)
3564
+ return graphPlans(doc, report).map(
3565
+ (plan2) => Object.freeze({
3566
+ index: plan2.index,
3567
+ name: plan2.name,
3568
+ nodes: memberCount(plan2.nodes, nodes),
3569
+ edges: memberCount(plan2.edges, edges)
2196
3570
  })
2197
3571
  );
2198
3572
  }
2199
3573
  });
2200
3574
  export {
2201
- CX_ISSUE as C,
2202
- cxImporter as c
3575
+ CX_CAPABILITIES as C,
3576
+ CX_ISSUE as a,
3577
+ CX_LOSS as b,
3578
+ cxExporter as c,
3579
+ cxImporter as d
2203
3580
  };
2204
- //# sourceMappingURL=importer-DET1tg6s.js.map
3581
+ //# sourceMappingURL=importer-DCjOvO7U.js.map