@graphty/graph-io 0.3.43 → 0.3.44

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 (674) hide show
  1. package/dist/chunks/approximations-3Q4TsdTh.js +40 -0
  2. package/dist/chunks/approximations-3Q4TsdTh.js.map +1 -0
  3. package/dist/chunks/block-JQiwViEt.js +2777 -0
  4. package/dist/chunks/block-JQiwViEt.js.map +1 -0
  5. package/dist/chunks/{choose-DvpbdRuz.js → choose-BemygfH8.js} +3 -2
  6. package/dist/chunks/{choose-DvpbdRuz.js.map → choose-BemygfH8.js.map} +1 -1
  7. package/dist/chunks/{constants-fT3E8JYY.js → constants-BFUR_CtU.js} +6 -2
  8. package/dist/chunks/constants-BFUR_CtU.js.map +1 -0
  9. package/dist/chunks/convert-BcQ8_jMo.js +59 -0
  10. package/dist/chunks/convert-BcQ8_jMo.js.map +1 -0
  11. package/dist/chunks/emit-DCwFLts5.js +3456 -0
  12. package/dist/chunks/emit-DCwFLts5.js.map +1 -0
  13. package/dist/chunks/{escape-B62ZGM_a.js → escape-B1rMftd9.js} +43 -20
  14. package/dist/chunks/escape-B1rMftd9.js.map +1 -0
  15. package/dist/chunks/{export-rKLeGQMo.js → export-D7IGzTG4.js} +196 -792
  16. package/dist/chunks/export-D7IGzTG4.js.map +1 -0
  17. package/dist/chunks/{exporter-BWwEuOPT.js → exporter-BtnqYuuj.js} +22 -12
  18. package/dist/chunks/exporter-BtnqYuuj.js.map +1 -0
  19. package/dist/chunks/{exporter-yLxKBvLE.js → exporter-ChPyhBOv.js} +4 -3
  20. package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter-ChPyhBOv.js.map} +1 -1
  21. package/dist/chunks/{exporter-8hLNemFk.js → exporter-KEPxYUXD.js} +133 -74
  22. package/dist/chunks/exporter-KEPxYUXD.js.map +1 -0
  23. package/dist/chunks/{exporter-RVtXVetw.js → exporter-quPUCBiC.js} +1805 -287
  24. package/dist/chunks/exporter-quPUCBiC.js.map +1 -0
  25. package/dist/chunks/format-GDmO6m6Q.js +676 -0
  26. package/dist/chunks/format-GDmO6m6Q.js.map +1 -0
  27. package/dist/chunks/generic-BL06jrJf.js +1347 -0
  28. package/dist/chunks/generic-BL06jrJf.js.map +1 -0
  29. package/dist/chunks/{importer-WrGRZb0N.js → importer-B7r2owVf.js} +449 -102
  30. package/dist/chunks/importer-B7r2owVf.js.map +1 -0
  31. package/dist/chunks/{importer-Ht7LNg8L.js → importer-BFmOP96W.js} +116 -296
  32. package/dist/chunks/importer-BFmOP96W.js.map +1 -0
  33. package/dist/chunks/{importer-BbNb7eNg.js → importer-C2nzGhdV.js} +1390 -129
  34. package/dist/chunks/importer-C2nzGhdV.js.map +1 -0
  35. package/dist/chunks/{importer-Dz7tI8Sa.js → importer-CbX2_rNF.js} +617 -115
  36. package/dist/chunks/importer-CbX2_rNF.js.map +1 -0
  37. package/dist/chunks/{importer-D794XUTe.js → importer-CdzeN6X4.js} +31 -8
  38. package/dist/chunks/importer-CdzeN6X4.js.map +1 -0
  39. package/dist/chunks/{importer-xZ-3suY1.js → importer-ChKNC_GN.js} +791 -135
  40. package/dist/chunks/importer-ChKNC_GN.js.map +1 -0
  41. package/dist/chunks/{importer-C98dpSH4.js → importer-dwoYpSqb.js} +141 -82
  42. package/dist/chunks/importer-dwoYpSqb.js.map +1 -0
  43. package/dist/chunks/index-B9fdNpTA.js +4310 -0
  44. package/dist/chunks/index-B9fdNpTA.js.map +1 -0
  45. package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-C4FwgnH5.js} +123 -32
  46. package/dist/chunks/json-elements-C4FwgnH5.js.map +1 -0
  47. package/dist/chunks/lexicon-B-knUi-l.js +508 -0
  48. package/dist/chunks/lexicon-B-knUi-l.js.map +1 -0
  49. package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-BSpbY5FL.js} +3 -2
  50. package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-BSpbY5FL.js.map} +1 -1
  51. package/dist/chunks/{records-Cu4bxYOm.js → records-Nwz_rbNt.js} +2 -2
  52. package/dist/chunks/{records-Cu4bxYOm.js.map → records-Nwz_rbNt.js.map} +1 -1
  53. package/dist/chunks/styles-Brt9JMfs.js +690 -0
  54. package/dist/chunks/styles-Brt9JMfs.js.map +1 -0
  55. package/dist/chunks/styles-_N-C8S-U.js +2102 -0
  56. package/dist/chunks/styles-_N-C8S-U.js.map +1 -0
  57. package/dist/chunks/styles-yed-DAfcIkf7.js +842 -0
  58. package/dist/chunks/styles-yed-DAfcIkf7.js.map +1 -0
  59. package/dist/chunks/{text-DHOJXnED.js → text-CgzMqHQb.js} +2 -2
  60. package/dist/chunks/{text-DHOJXnED.js.map → text-CgzMqHQb.js.map} +1 -1
  61. package/dist/chunks/units-XMZFIFe1.js +19 -0
  62. package/dist/chunks/units-XMZFIFe1.js.map +1 -0
  63. package/dist/csv.js +5 -4
  64. package/dist/csv.js.map +1 -1
  65. package/dist/cx.js +1 -1
  66. package/dist/cx2.js +2 -2
  67. package/dist/cys.js +1 -1
  68. package/dist/dot.js +5 -2
  69. package/dist/dot.js.map +1 -1
  70. package/dist/gexf.js +292 -412
  71. package/dist/gexf.js.map +1 -1
  72. package/dist/gml.js +8 -3031
  73. package/dist/gml.js.map +1 -1
  74. package/dist/graph-io.js +354 -240
  75. package/dist/graph-io.js.map +1 -1
  76. package/dist/graphml.js +4 -2
  77. package/dist/graphml.js.map +1 -1
  78. package/dist/json.js +1 -1
  79. package/dist/neo4j.js +4 -3
  80. package/dist/neo4j.js.map +1 -1
  81. package/dist/obo.js +1 -1
  82. package/dist/pajek.js +4 -2
  83. package/dist/positions.d.ts +1 -0
  84. package/dist/positions.js +427 -0
  85. package/dist/positions.js.map +1 -0
  86. package/dist/src/common/codes.d.ts +6 -0
  87. package/dist/src/common/codes.d.ts.map +1 -1
  88. package/dist/src/common/codes.js +6 -0
  89. package/dist/src/common/codes.js.map +1 -1
  90. package/dist/src/common/escape.d.ts.map +1 -1
  91. package/dist/src/common/escape.js +4 -2
  92. package/dist/src/common/escape.js.map +1 -1
  93. package/dist/src/common/export.d.ts +10 -0
  94. package/dist/src/common/export.d.ts.map +1 -1
  95. package/dist/src/common/export.js +123 -100
  96. package/dist/src/common/export.js.map +1 -1
  97. package/dist/src/common/input.d.ts.map +1 -1
  98. package/dist/src/common/input.js +8 -1
  99. package/dist/src/common/input.js.map +1 -1
  100. package/dist/src/common/json-elements.d.ts +12 -9
  101. package/dist/src/common/json-elements.d.ts.map +1 -1
  102. package/dist/src/common/json-elements.js +160 -37
  103. package/dist/src/common/json-elements.js.map +1 -1
  104. package/dist/src/common/notes.d.ts +42 -0
  105. package/dist/src/common/notes.d.ts.map +1 -0
  106. package/dist/src/common/notes.js +72 -0
  107. package/dist/src/common/notes.js.map +1 -0
  108. package/dist/src/common/report.d.ts +18 -0
  109. package/dist/src/common/report.d.ts.map +1 -1
  110. package/dist/src/common/report.js +36 -1
  111. package/dist/src/common/report.js.map +1 -1
  112. package/dist/src/common/units.d.ts +24 -0
  113. package/dist/src/common/units.d.ts.map +1 -0
  114. package/dist/src/common/units.js +26 -0
  115. package/dist/src/common/units.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +18 -0
  117. package/dist/src/common/xml.d.ts.map +1 -1
  118. package/dist/src/common/xml.js +42 -5
  119. package/dist/src/common/xml.js.map +1 -1
  120. package/dist/src/formats/cx/exporter.d.ts +6 -0
  121. package/dist/src/formats/cx/exporter.d.ts.map +1 -1
  122. package/dist/src/formats/cx/exporter.js +71 -8
  123. package/dist/src/formats/cx/exporter.js.map +1 -1
  124. package/dist/src/formats/cx/importer.d.ts +5 -2
  125. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cx/importer.js +102 -72
  127. package/dist/src/formats/cx/importer.js.map +1 -1
  128. package/dist/src/formats/cx/styles.d.ts +102 -0
  129. package/dist/src/formats/cx/styles.d.ts.map +1 -0
  130. package/dist/src/formats/cx/styles.js +225 -0
  131. package/dist/src/formats/cx/styles.js.map +1 -0
  132. package/dist/src/formats/cx2/exporter.d.ts +5 -0
  133. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  134. package/dist/src/formats/cx2/exporter.js +181 -71
  135. package/dist/src/formats/cx2/exporter.js.map +1 -1
  136. package/dist/src/formats/cx2/importer.d.ts +4 -1
  137. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  138. package/dist/src/formats/cx2/importer.js +19 -1
  139. package/dist/src/formats/cx2/importer.js.map +1 -1
  140. package/dist/src/formats/cx2/styles.d.ts +18 -0
  141. package/dist/src/formats/cx2/styles.d.ts.map +1 -0
  142. package/dist/src/formats/cx2/styles.js +24 -0
  143. package/dist/src/formats/cx2/styles.js.map +1 -0
  144. package/dist/src/formats/cys/constants.d.ts +2 -0
  145. package/dist/src/formats/cys/constants.d.ts.map +1 -1
  146. package/dist/src/formats/cys/constants.js +2 -0
  147. package/dist/src/formats/cys/constants.js.map +1 -1
  148. package/dist/src/formats/cys/exporter.d.ts +6 -2
  149. package/dist/src/formats/cys/exporter.d.ts.map +1 -1
  150. package/dist/src/formats/cys/exporter.js +215 -53
  151. package/dist/src/formats/cys/exporter.js.map +1 -1
  152. package/dist/src/formats/cys/importer.d.ts +2 -1
  153. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  154. package/dist/src/formats/cys/importer.js +119 -6
  155. package/dist/src/formats/cys/importer.js.map +1 -1
  156. package/dist/src/formats/cys/session.d.ts +2 -0
  157. package/dist/src/formats/cys/session.d.ts.map +1 -1
  158. package/dist/src/formats/cys/session.js +5 -0
  159. package/dist/src/formats/cys/session.js.map +1 -1
  160. package/dist/src/formats/cys/styles.d.ts +38 -0
  161. package/dist/src/formats/cys/styles.d.ts.map +1 -0
  162. package/dist/src/formats/cys/styles.js +213 -0
  163. package/dist/src/formats/cys/styles.js.map +1 -0
  164. package/dist/src/formats/cys/tables.d.ts +8 -3
  165. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  166. package/dist/src/formats/cys/tables.js +88 -34
  167. package/dist/src/formats/cys/tables.js.map +1 -1
  168. package/dist/src/formats/dot/exporter.d.ts +2 -0
  169. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  170. package/dist/src/formats/dot/exporter.js +195 -81
  171. package/dist/src/formats/dot/exporter.js.map +1 -1
  172. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  173. package/dist/src/formats/dot/importer.js +100 -20
  174. package/dist/src/formats/dot/importer.js.map +1 -1
  175. package/dist/src/formats/dot/index.d.ts +1 -0
  176. package/dist/src/formats/dot/index.d.ts.map +1 -1
  177. package/dist/src/formats/dot/index.js +1 -0
  178. package/dist/src/formats/dot/index.js.map +1 -1
  179. package/dist/src/formats/dot/positions.d.ts +14 -0
  180. package/dist/src/formats/dot/positions.d.ts.map +1 -0
  181. package/dist/src/formats/dot/positions.js +70 -0
  182. package/dist/src/formats/dot/positions.js.map +1 -0
  183. package/dist/src/formats/dot/styles.d.ts +82 -0
  184. package/dist/src/formats/dot/styles.d.ts.map +1 -0
  185. package/dist/src/formats/dot/styles.js +1484 -0
  186. package/dist/src/formats/dot/styles.js.map +1 -0
  187. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  188. package/dist/src/formats/gexf/exporter.js +20 -5
  189. package/dist/src/formats/gexf/exporter.js.map +1 -1
  190. package/dist/src/formats/gexf/importer.d.ts +8 -1
  191. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  192. package/dist/src/formats/gexf/importer.js +251 -131
  193. package/dist/src/formats/gexf/importer.js.map +1 -1
  194. package/dist/src/formats/gexf/index.d.ts +8 -0
  195. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  196. package/dist/src/formats/gexf/index.js +10 -2
  197. package/dist/src/formats/gexf/index.js.map +1 -1
  198. package/dist/src/formats/gexf/schema.d.ts +5 -0
  199. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  200. package/dist/src/formats/gexf/schema.js +5 -0
  201. package/dist/src/formats/gexf/schema.js.map +1 -1
  202. package/dist/src/formats/gexf/styles.d.ts +74 -0
  203. package/dist/src/formats/gexf/styles.d.ts.map +1 -0
  204. package/dist/src/formats/gexf/styles.js +604 -0
  205. package/dist/src/formats/gexf/styles.js.map +1 -0
  206. package/dist/src/formats/gml/exporter.d.ts +1 -5
  207. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  208. package/dist/src/formats/gml/exporter.js +19 -8
  209. package/dist/src/formats/gml/exporter.js.map +1 -1
  210. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  211. package/dist/src/formats/gml/importer.js +4 -0
  212. package/dist/src/formats/gml/importer.js.map +1 -1
  213. package/dist/src/formats/gml/index.d.ts +2 -1
  214. package/dist/src/formats/gml/index.d.ts.map +1 -1
  215. package/dist/src/formats/gml/index.js +2 -1
  216. package/dist/src/formats/gml/index.js.map +1 -1
  217. package/dist/src/formats/gml/positions.d.ts +14 -0
  218. package/dist/src/formats/gml/positions.d.ts.map +1 -0
  219. package/dist/src/formats/gml/positions.js +46 -0
  220. package/dist/src/formats/gml/positions.js.map +1 -0
  221. package/dist/src/formats/gml/styles.d.ts +59 -0
  222. package/dist/src/formats/gml/styles.d.ts.map +1 -0
  223. package/dist/src/formats/gml/styles.js +1056 -0
  224. package/dist/src/formats/gml/styles.js.map +1 -0
  225. package/dist/src/formats/gml/syntax.d.ts +11 -0
  226. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  227. package/dist/src/formats/gml/syntax.js +151 -62
  228. package/dist/src/formats/gml/syntax.js.map +1 -1
  229. package/dist/src/formats/graphml/constants.d.ts +4 -0
  230. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  231. package/dist/src/formats/graphml/constants.js +5 -1
  232. package/dist/src/formats/graphml/constants.js.map +1 -1
  233. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  234. package/dist/src/formats/graphml/exporter.js +133 -65
  235. package/dist/src/formats/graphml/exporter.js.map +1 -1
  236. package/dist/src/formats/graphml/importer.d.ts +4 -1
  237. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  238. package/dist/src/formats/graphml/importer.js +100 -44
  239. package/dist/src/formats/graphml/importer.js.map +1 -1
  240. package/dist/src/formats/graphml/index.d.ts +1 -0
  241. package/dist/src/formats/graphml/index.d.ts.map +1 -1
  242. package/dist/src/formats/graphml/index.js +1 -0
  243. package/dist/src/formats/graphml/index.js.map +1 -1
  244. package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
  245. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
  246. package/dist/src/formats/graphml/styles-plain.js +187 -0
  247. package/dist/src/formats/graphml/styles-plain.js.map +1 -0
  248. package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
  249. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
  250. package/dist/src/formats/graphml/styles-yed.js +1086 -0
  251. package/dist/src/formats/graphml/styles-yed.js.map +1 -0
  252. package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
  253. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
  254. package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
  255. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
  256. package/dist/src/formats/graphml/tree.d.ts.map +1 -1
  257. package/dist/src/formats/graphml/tree.js +34 -19
  258. package/dist/src/formats/graphml/tree.js.map +1 -1
  259. package/dist/src/formats/graphml/yfiles.d.ts +16 -2
  260. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  261. package/dist/src/formats/graphml/yfiles.js +83 -12
  262. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  263. package/dist/src/formats/json/exporter.d.ts +5 -0
  264. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  265. package/dist/src/formats/json/exporter.js +68 -25
  266. package/dist/src/formats/json/exporter.js.map +1 -1
  267. package/dist/src/formats/json/importer.d.ts +22 -3
  268. package/dist/src/formats/json/importer.d.ts.map +1 -1
  269. package/dist/src/formats/json/importer.js +439 -264
  270. package/dist/src/formats/json/importer.js.map +1 -1
  271. package/dist/src/formats/json/positions.d.ts +16 -0
  272. package/dist/src/formats/json/positions.d.ts.map +1 -0
  273. package/dist/src/formats/json/positions.js +44 -0
  274. package/dist/src/formats/json/positions.js.map +1 -0
  275. package/dist/src/formats/json/styles.d.ts +102 -0
  276. package/dist/src/formats/json/styles.d.ts.map +1 -0
  277. package/dist/src/formats/json/styles.js +1753 -0
  278. package/dist/src/formats/json/styles.js.map +1 -0
  279. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  280. package/dist/src/formats/neo4j/exporter.js +2 -1
  281. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  282. package/dist/src/formats/pajek/exporter.d.ts +2 -0
  283. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  284. package/dist/src/formats/pajek/exporter.js +142 -76
  285. package/dist/src/formats/pajek/exporter.js.map +1 -1
  286. package/dist/src/formats/pajek/importer.d.ts +4 -0
  287. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  288. package/dist/src/formats/pajek/importer.js +60 -6
  289. package/dist/src/formats/pajek/importer.js.map +1 -1
  290. package/dist/src/formats/pajek/index.d.ts +2 -0
  291. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  292. package/dist/src/formats/pajek/index.js +2 -0
  293. package/dist/src/formats/pajek/index.js.map +1 -1
  294. package/dist/src/formats/pajek/styles.d.ts +50 -0
  295. package/dist/src/formats/pajek/styles.d.ts.map +1 -0
  296. package/dist/src/formats/pajek/styles.js +569 -0
  297. package/dist/src/formats/pajek/styles.js.map +1 -0
  298. package/dist/src/formats/pajek/syntax.d.ts +6 -0
  299. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  300. package/dist/src/formats/pajek/syntax.js +6 -0
  301. package/dist/src/formats/pajek/syntax.js.map +1 -1
  302. package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
  303. package/dist/src/formats/xgmml/columns.js +34 -9
  304. package/dist/src/formats/xgmml/columns.js.map +1 -1
  305. package/dist/src/formats/xgmml/constants.d.ts +2 -0
  306. package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
  307. package/dist/src/formats/xgmml/constants.js +3 -1
  308. package/dist/src/formats/xgmml/constants.js.map +1 -1
  309. package/dist/src/formats/xgmml/document.d.ts +12 -8
  310. package/dist/src/formats/xgmml/document.d.ts.map +1 -1
  311. package/dist/src/formats/xgmml/document.js +56 -13
  312. package/dist/src/formats/xgmml/document.js.map +1 -1
  313. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  314. package/dist/src/formats/xgmml/emit.js +12 -6
  315. package/dist/src/formats/xgmml/emit.js.map +1 -1
  316. package/dist/src/formats/xgmml/exporter.d.ts +3 -1
  317. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  318. package/dist/src/formats/xgmml/exporter.js +22 -9
  319. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  320. package/dist/src/formats/xgmml/importer.d.ts +2 -1
  321. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  322. package/dist/src/formats/xgmml/importer.js +31 -24
  323. package/dist/src/formats/xgmml/importer.js.map +1 -1
  324. package/dist/src/formats/xgmml/styles.d.ts +104 -0
  325. package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
  326. package/dist/src/formats/xgmml/styles.js +310 -0
  327. package/dist/src/formats/xgmml/styles.js.map +1 -0
  328. package/dist/src/index.d.ts +9 -7
  329. package/dist/src/index.d.ts.map +1 -1
  330. package/dist/src/index.js +7 -7
  331. package/dist/src/index.js.map +1 -1
  332. package/dist/src/positions/codes.d.ts +31 -0
  333. package/dist/src/positions/codes.d.ts.map +1 -0
  334. package/dist/src/positions/codes.js +26 -0
  335. package/dist/src/positions/codes.js.map +1 -0
  336. package/dist/src/positions/convert.d.ts +58 -0
  337. package/dist/src/positions/convert.d.ts.map +1 -0
  338. package/dist/src/positions/convert.js +107 -0
  339. package/dist/src/positions/convert.js.map +1 -0
  340. package/dist/src/positions/extract.d.ts +27 -0
  341. package/dist/src/positions/extract.d.ts.map +1 -0
  342. package/dist/src/positions/extract.js +526 -0
  343. package/dist/src/positions/extract.js.map +1 -0
  344. package/dist/src/positions/index.d.ts +10 -0
  345. package/dist/src/positions/index.d.ts.map +1 -0
  346. package/dist/src/positions/index.js +9 -0
  347. package/dist/src/positions/index.js.map +1 -0
  348. package/dist/src/positions/types.d.ts +208 -0
  349. package/dist/src/positions/types.d.ts.map +1 -0
  350. package/dist/src/positions/types.js +10 -0
  351. package/dist/src/positions/types.js.map +1 -0
  352. package/dist/src/registry.d.ts +30 -0
  353. package/dist/src/registry.d.ts.map +1 -1
  354. package/dist/src/registry.js +130 -9
  355. package/dist/src/registry.js.map +1 -1
  356. package/dist/src/styles/annotations.d.ts +132 -0
  357. package/dist/src/styles/annotations.d.ts.map +1 -0
  358. package/dist/src/styles/annotations.js +320 -0
  359. package/dist/src/styles/annotations.js.map +1 -0
  360. package/dist/src/styles/budget.d.ts +45 -0
  361. package/dist/src/styles/budget.d.ts.map +1 -0
  362. package/dist/src/styles/budget.js +58 -0
  363. package/dist/src/styles/budget.js.map +1 -0
  364. package/dist/src/styles/canon/bends.d.ts +32 -0
  365. package/dist/src/styles/canon/bends.d.ts.map +1 -0
  366. package/dist/src/styles/canon/bends.js +74 -0
  367. package/dist/src/styles/canon/bends.js.map +1 -0
  368. package/dist/src/styles/canon/color-tables.d.ts +22 -0
  369. package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
  370. package/dist/src/styles/canon/color-tables.js +369 -0
  371. package/dist/src/styles/canon/color-tables.js.map +1 -0
  372. package/dist/src/styles/canon/color.d.ts +66 -0
  373. package/dist/src/styles/canon/color.d.ts.map +1 -0
  374. package/dist/src/styles/canon/color.js +548 -0
  375. package/dist/src/styles/canon/color.js.map +1 -0
  376. package/dist/src/styles/canon/enums.d.ts +25 -0
  377. package/dist/src/styles/canon/enums.d.ts.map +1 -0
  378. package/dist/src/styles/canon/enums.js +187 -0
  379. package/dist/src/styles/canon/enums.js.map +1 -0
  380. package/dist/src/styles/canon/graphviz.d.ts +48 -0
  381. package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
  382. package/dist/src/styles/canon/graphviz.js +160 -0
  383. package/dist/src/styles/canon/graphviz.js.map +1 -0
  384. package/dist/src/styles/canon/index.d.ts +13 -0
  385. package/dist/src/styles/canon/index.d.ts.map +1 -0
  386. package/dist/src/styles/canon/index.js +13 -0
  387. package/dist/src/styles/canon/index.js.map +1 -0
  388. package/dist/src/styles/canon/number.d.ts +59 -0
  389. package/dist/src/styles/canon/number.d.ts.map +1 -0
  390. package/dist/src/styles/canon/number.js +142 -0
  391. package/dist/src/styles/canon/number.js.map +1 -0
  392. package/dist/src/styles/canon/site.d.ts +51 -0
  393. package/dist/src/styles/canon/site.d.ts.map +1 -0
  394. package/dist/src/styles/canon/site.js +63 -0
  395. package/dist/src/styles/canon/site.js.map +1 -0
  396. package/dist/src/styles/canon/text.d.ts +61 -0
  397. package/dist/src/styles/canon/text.d.ts.map +1 -0
  398. package/dist/src/styles/canon/text.js +199 -0
  399. package/dist/src/styles/canon/text.js.map +1 -0
  400. package/dist/src/styles/codes.d.ts +95 -0
  401. package/dist/src/styles/codes.d.ts.map +1 -0
  402. package/dist/src/styles/codes.js +97 -0
  403. package/dist/src/styles/codes.js.map +1 -0
  404. package/dist/src/styles/cyjs/cascade.d.ts +134 -0
  405. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
  406. package/dist/src/styles/cyjs/cascade.js +2252 -0
  407. package/dist/src/styles/cyjs/cascade.js.map +1 -0
  408. package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
  409. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
  410. package/dist/src/styles/cyjs/defaults-table.js +76 -0
  411. package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
  412. package/dist/src/styles/cyjs/emit.d.ts +82 -0
  413. package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
  414. package/dist/src/styles/cyjs/emit.js +790 -0
  415. package/dist/src/styles/cyjs/emit.js.map +1 -0
  416. package/dist/src/styles/cyjs/index.d.ts +13 -0
  417. package/dist/src/styles/cyjs/index.d.ts.map +1 -0
  418. package/dist/src/styles/cyjs/index.js +13 -0
  419. package/dist/src/styles/cyjs/index.js.map +1 -0
  420. package/dist/src/styles/cyjs/live.d.ts +46 -0
  421. package/dist/src/styles/cyjs/live.d.ts.map +1 -0
  422. package/dist/src/styles/cyjs/live.js +108 -0
  423. package/dist/src/styles/cyjs/live.js.map +1 -0
  424. package/dist/src/styles/cyjs/mappers.d.ts +147 -0
  425. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -0
  426. package/dist/src/styles/cyjs/mappers.js +744 -0
  427. package/dist/src/styles/cyjs/mappers.js.map +1 -0
  428. package/dist/src/styles/cyjs/selector.d.ts +113 -0
  429. package/dist/src/styles/cyjs/selector.d.ts.map +1 -0
  430. package/dist/src/styles/cyjs/selector.js +611 -0
  431. package/dist/src/styles/cyjs/selector.js.map +1 -0
  432. package/dist/src/styles/cyjs/stylesheet-text.d.ts +37 -0
  433. package/dist/src/styles/cyjs/stylesheet-text.d.ts.map +1 -0
  434. package/dist/src/styles/cyjs/stylesheet-text.js +181 -0
  435. package/dist/src/styles/cyjs/stylesheet-text.js.map +1 -0
  436. package/dist/src/styles/cytoscape/emit.d.ts +125 -0
  437. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -0
  438. package/dist/src/styles/cytoscape/emit.js +1025 -0
  439. package/dist/src/styles/cytoscape/emit.js.map +1 -0
  440. package/dist/src/styles/cytoscape/index.d.ts +10 -0
  441. package/dist/src/styles/cytoscape/index.d.ts.map +1 -0
  442. package/dist/src/styles/cytoscape/index.js +10 -0
  443. package/dist/src/styles/cytoscape/index.js.map +1 -0
  444. package/dist/src/styles/cytoscape/lexicon.d.ts +49 -0
  445. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -0
  446. package/dist/src/styles/cytoscape/lexicon.js +603 -0
  447. package/dist/src/styles/cytoscape/lexicon.js.map +1 -0
  448. package/dist/src/styles/cytoscape/mappings.d.ts +171 -0
  449. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -0
  450. package/dist/src/styles/cytoscape/mappings.js +460 -0
  451. package/dist/src/styles/cytoscape/mappings.js.map +1 -0
  452. package/dist/src/styles/cytoscape/resolve.d.ts +137 -0
  453. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -0
  454. package/dist/src/styles/cytoscape/resolve.js +381 -0
  455. package/dist/src/styles/cytoscape/resolve.js.map +1 -0
  456. package/dist/src/styles/cytoscape/translate.d.ts +50 -0
  457. package/dist/src/styles/cytoscape/translate.d.ts.map +1 -0
  458. package/dist/src/styles/cytoscape/translate.js +156 -0
  459. package/dist/src/styles/cytoscape/translate.js.map +1 -0
  460. package/dist/src/styles/defaults.d.ts +90 -0
  461. package/dist/src/styles/defaults.d.ts.map +1 -0
  462. package/dist/src/styles/defaults.js +414 -0
  463. package/dist/src/styles/defaults.js.map +1 -0
  464. package/dist/src/styles/index.d.ts +15 -0
  465. package/dist/src/styles/index.d.ts.map +1 -0
  466. package/dist/src/styles/index.js +14 -0
  467. package/dist/src/styles/index.js.map +1 -0
  468. package/dist/src/styles/internal.d.ts +17 -0
  469. package/dist/src/styles/internal.d.ts.map +1 -0
  470. package/dist/src/styles/internal.js +16 -0
  471. package/dist/src/styles/internal.js.map +1 -0
  472. package/dist/src/styles/io/approximations.d.ts +24 -0
  473. package/dist/src/styles/io/approximations.d.ts.map +1 -0
  474. package/dist/src/styles/io/approximations.js +51 -0
  475. package/dist/src/styles/io/approximations.js.map +1 -0
  476. package/dist/src/styles/io/block.d.ts +166 -0
  477. package/dist/src/styles/io/block.d.ts.map +1 -0
  478. package/dist/src/styles/io/block.js +930 -0
  479. package/dist/src/styles/io/block.js.map +1 -0
  480. package/dist/src/styles/io/generic.d.ts +88 -0
  481. package/dist/src/styles/io/generic.d.ts.map +1 -0
  482. package/dist/src/styles/io/generic.js +516 -0
  483. package/dist/src/styles/io/generic.js.map +1 -0
  484. package/dist/src/styles/io/index.d.ts +8 -0
  485. package/dist/src/styles/io/index.d.ts.map +1 -0
  486. package/dist/src/styles/io/index.js +7 -0
  487. package/dist/src/styles/io/index.js.map +1 -0
  488. package/dist/src/styles/io/writer.d.ts +80 -0
  489. package/dist/src/styles/io/writer.d.ts.map +1 -0
  490. package/dist/src/styles/io/writer.js +596 -0
  491. package/dist/src/styles/io/writer.js.map +1 -0
  492. package/dist/src/styles/notes.d.ts +47 -0
  493. package/dist/src/styles/notes.d.ts.map +1 -0
  494. package/dist/src/styles/notes.js +140 -0
  495. package/dist/src/styles/notes.js.map +1 -0
  496. package/dist/src/styles/plan/classes.d.ts +31 -0
  497. package/dist/src/styles/plan/classes.d.ts.map +1 -0
  498. package/dist/src/styles/plan/classes.js +139 -0
  499. package/dist/src/styles/plan/classes.js.map +1 -0
  500. package/dist/src/styles/plan/continuous.d.ts +32 -0
  501. package/dist/src/styles/plan/continuous.d.ts.map +1 -0
  502. package/dist/src/styles/plan/continuous.js +165 -0
  503. package/dist/src/styles/plan/continuous.js.map +1 -0
  504. package/dist/src/styles/plan/discrete.d.ts +39 -0
  505. package/dist/src/styles/plan/discrete.d.ts.map +1 -0
  506. package/dist/src/styles/plan/discrete.js +127 -0
  507. package/dist/src/styles/plan/discrete.js.map +1 -0
  508. package/dist/src/styles/plan/families.d.ts +31 -0
  509. package/dist/src/styles/plan/families.d.ts.map +1 -0
  510. package/dist/src/styles/plan/families.js +95 -0
  511. package/dist/src/styles/plan/families.js.map +1 -0
  512. package/dist/src/styles/plan/features.d.ts +58 -0
  513. package/dist/src/styles/plan/features.d.ts.map +1 -0
  514. package/dist/src/styles/plan/features.js +185 -0
  515. package/dist/src/styles/plan/features.js.map +1 -0
  516. package/dist/src/styles/plan/index.d.ts +7 -0
  517. package/dist/src/styles/plan/index.d.ts.map +1 -0
  518. package/dist/src/styles/plan/index.js +7 -0
  519. package/dist/src/styles/plan/index.js.map +1 -0
  520. package/dist/src/styles/plan/plan.d.ts +71 -0
  521. package/dist/src/styles/plan/plan.d.ts.map +1 -0
  522. package/dist/src/styles/plan/plan.js +893 -0
  523. package/dist/src/styles/plan/plan.js.map +1 -0
  524. package/dist/src/styles/plan/resolve.d.ts +137 -0
  525. package/dist/src/styles/plan/resolve.d.ts.map +1 -0
  526. package/dist/src/styles/plan/resolve.js +587 -0
  527. package/dist/src/styles/plan/resolve.js.map +1 -0
  528. package/dist/src/styles/types.d.ts +702 -0
  529. package/dist/src/styles/types.d.ts.map +1 -0
  530. package/dist/src/styles/types.js +10 -0
  531. package/dist/src/styles/types.js.map +1 -0
  532. package/dist/src/styles/vocabulary.d.ts +41 -0
  533. package/dist/src/styles/vocabulary.d.ts.map +1 -0
  534. package/dist/src/styles/vocabulary.js +140 -0
  535. package/dist/src/styles/vocabulary.js.map +1 -0
  536. package/dist/src/types.d.ts +64 -1
  537. package/dist/src/types.d.ts.map +1 -1
  538. package/dist/src/types.js.map +1 -1
  539. package/dist/styles.d.ts +1 -0
  540. package/dist/styles.js +322 -0
  541. package/dist/styles.js.map +1 -0
  542. package/dist/xgmml.js +5 -4
  543. package/dist/xgmml.js.map +1 -1
  544. package/package.json +13 -2
  545. package/src/common/codes.ts +7 -0
  546. package/src/common/escape.ts +5 -2
  547. package/src/common/export.ts +179 -140
  548. package/src/common/input.ts +8 -4
  549. package/src/common/json-elements.ts +169 -38
  550. package/src/common/notes.ts +94 -0
  551. package/src/common/report.ts +41 -1
  552. package/src/common/units.ts +42 -0
  553. package/src/common/xml.ts +45 -5
  554. package/src/formats/cx/exporter.ts +95 -11
  555. package/src/formats/cx/importer.ts +126 -90
  556. package/src/formats/cx/styles.ts +304 -0
  557. package/src/formats/cx2/exporter.ts +234 -77
  558. package/src/formats/cx2/importer.ts +19 -1
  559. package/src/formats/cx2/styles.ts +29 -0
  560. package/src/formats/cys/constants.ts +3 -0
  561. package/src/formats/cys/exporter.ts +246 -60
  562. package/src/formats/cys/importer.ts +125 -9
  563. package/src/formats/cys/session.ts +6 -0
  564. package/src/formats/cys/styles.ts +254 -0
  565. package/src/formats/cys/tables.ts +117 -48
  566. package/src/formats/dot/exporter.ts +262 -126
  567. package/src/formats/dot/importer.ts +111 -20
  568. package/src/formats/dot/index.ts +1 -0
  569. package/src/formats/dot/positions.ts +77 -0
  570. package/src/formats/dot/styles.ts +1741 -0
  571. package/src/formats/gexf/exporter.ts +27 -11
  572. package/src/formats/gexf/importer.ts +308 -158
  573. package/src/formats/gexf/index.ts +10 -0
  574. package/src/formats/gexf/schema.ts +5 -0
  575. package/src/formats/gexf/styles.ts +719 -0
  576. package/src/formats/gml/exporter.ts +33 -14
  577. package/src/formats/gml/importer.ts +4 -0
  578. package/src/formats/gml/index.ts +2 -1
  579. package/src/formats/gml/positions.ts +52 -0
  580. package/src/formats/gml/styles.ts +1248 -0
  581. package/src/formats/gml/syntax.ts +173 -62
  582. package/src/formats/graphml/constants.ts +5 -0
  583. package/src/formats/graphml/exporter.ts +163 -71
  584. package/src/formats/graphml/importer.ts +119 -45
  585. package/src/formats/graphml/index.ts +1 -0
  586. package/src/formats/graphml/styles-plain.ts +236 -0
  587. package/src/formats/graphml/styles-yed.ts +1221 -0
  588. package/src/formats/graphml/styles-yfiles3.ts +573 -0
  589. package/src/formats/graphml/tree.ts +36 -20
  590. package/src/formats/graphml/yfiles.ts +89 -13
  591. package/src/formats/json/exporter.ts +76 -26
  592. package/src/formats/json/importer.ts +541 -322
  593. package/src/formats/json/positions.ts +50 -0
  594. package/src/formats/json/styles.ts +2082 -0
  595. package/src/formats/neo4j/exporter.ts +2 -1
  596. package/src/formats/pajek/exporter.ts +195 -106
  597. package/src/formats/pajek/importer.ts +78 -9
  598. package/src/formats/pajek/index.ts +2 -0
  599. package/src/formats/pajek/styles.ts +685 -0
  600. package/src/formats/pajek/syntax.ts +7 -0
  601. package/src/formats/xgmml/columns.ts +39 -9
  602. package/src/formats/xgmml/constants.ts +3 -0
  603. package/src/formats/xgmml/document.ts +54 -12
  604. package/src/formats/xgmml/emit.ts +11 -6
  605. package/src/formats/xgmml/exporter.ts +32 -13
  606. package/src/formats/xgmml/importer.ts +43 -29
  607. package/src/formats/xgmml/styles.ts +400 -0
  608. package/src/index.ts +16 -0
  609. package/src/positions/codes.ts +32 -0
  610. package/src/positions/convert.ts +123 -0
  611. package/src/positions/extract.ts +619 -0
  612. package/src/positions/index.ts +23 -0
  613. package/src/positions/types.ts +221 -0
  614. package/src/registry.ts +179 -8
  615. package/src/styles/annotations.ts +478 -0
  616. package/src/styles/budget.ts +60 -0
  617. package/src/styles/canon/bends.ts +84 -0
  618. package/src/styles/canon/color-tables.ts +386 -0
  619. package/src/styles/canon/color.ts +624 -0
  620. package/src/styles/canon/enums.ts +211 -0
  621. package/src/styles/canon/graphviz.ts +189 -0
  622. package/src/styles/canon/index.ts +13 -0
  623. package/src/styles/canon/number.ts +152 -0
  624. package/src/styles/canon/site.ts +87 -0
  625. package/src/styles/canon/text.ts +221 -0
  626. package/src/styles/codes.ts +104 -0
  627. package/src/styles/cyjs/cascade.ts +2670 -0
  628. package/src/styles/cyjs/defaults-table.ts +82 -0
  629. package/src/styles/cyjs/emit.ts +951 -0
  630. package/src/styles/cyjs/index.ts +37 -0
  631. package/src/styles/cyjs/live.ts +147 -0
  632. package/src/styles/cyjs/mappers.ts +879 -0
  633. package/src/styles/cyjs/selector.ts +778 -0
  634. package/src/styles/cyjs/stylesheet-text.ts +214 -0
  635. package/src/styles/cytoscape/emit.ts +1264 -0
  636. package/src/styles/cytoscape/index.ts +40 -0
  637. package/src/styles/cytoscape/lexicon.ts +656 -0
  638. package/src/styles/cytoscape/mappings.ts +603 -0
  639. package/src/styles/cytoscape/resolve.ts +521 -0
  640. package/src/styles/cytoscape/translate.ts +237 -0
  641. package/src/styles/defaults.ts +504 -0
  642. package/src/styles/index.ts +57 -0
  643. package/src/styles/internal.ts +17 -0
  644. package/src/styles/io/approximations.ts +47 -0
  645. package/src/styles/io/block.ts +1138 -0
  646. package/src/styles/io/generic.ts +687 -0
  647. package/src/styles/io/index.ts +18 -0
  648. package/src/styles/io/writer.ts +745 -0
  649. package/src/styles/notes.ts +165 -0
  650. package/src/styles/plan/classes.ts +166 -0
  651. package/src/styles/plan/continuous.ts +218 -0
  652. package/src/styles/plan/discrete.ts +165 -0
  653. package/src/styles/plan/families.ts +115 -0
  654. package/src/styles/plan/features.ts +234 -0
  655. package/src/styles/plan/index.ts +7 -0
  656. package/src/styles/plan/plan.ts +1129 -0
  657. package/src/styles/plan/resolve.ts +685 -0
  658. package/src/styles/types.ts +814 -0
  659. package/src/styles/vocabulary.ts +172 -0
  660. package/src/types.ts +76 -1
  661. package/dist/chunks/constants-fT3E8JYY.js.map +0 -1
  662. package/dist/chunks/escape-B62ZGM_a.js.map +0 -1
  663. package/dist/chunks/export-rKLeGQMo.js.map +0 -1
  664. package/dist/chunks/exporter-8hLNemFk.js.map +0 -1
  665. package/dist/chunks/exporter-BWwEuOPT.js.map +0 -1
  666. package/dist/chunks/exporter-RVtXVetw.js.map +0 -1
  667. package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
  668. package/dist/chunks/importer-C98dpSH4.js.map +0 -1
  669. package/dist/chunks/importer-D794XUTe.js.map +0 -1
  670. package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
  671. package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
  672. package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
  673. package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
  674. package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
@@ -36,14 +36,17 @@ import { AMBIGUOUS_GRAPH_NAME_CODE, BAD_VALUE_CODE, COLUMN_RENAMED_CODE, DANGLIN
36
36
  import { DirectionResolver } from "../../common/direction.js";
37
37
  import { ID_MERGED_CODE, IdCoercer } from "../../common/ids.js";
38
38
  import { readText, throwIfAborted } from "../../common/input.js";
39
- import { findDuplicateKeys, flipY, MAYBE_INEXACT_EXPONENT, MAYBE_UNSAFE_INTEGER, rewriteNumbers, } from "../../common/json-elements.js";
39
+ import { findDuplicateKeys, flipY, keysInTextOrder, MAYBE_INEXACT_EXPONENT, MAYBE_UNSAFE_INTEGER, rewriteNumbers, } from "../../common/json-elements.js";
40
40
  import { chooseGraph, graphChosen, reportSinkOptions, reportUnusedOptions, resolveImportOptions, SINK_OPTION_CODE, } from "../../common/options.js";
41
41
  import { agree, plural } from "../../common/plural.js";
42
42
  import { ImportReportBuilder } from "../../common/report.js";
43
43
  import { weightFromValue } from "../../common/weights.js";
44
44
  import { headBytes, sniffJsonDialectHead } from "../../sniff.js";
45
+ import { STYLE_BLOCK_NAME } from "../../styles/io/block.js";
45
46
  import { CLASSES_COLUMN, CYTOSCAPE_ELEMENT_KEYS, CYTOSCAPE_STRUCTURAL_KEYS, DIALECT_DEFAULT_DIRECTED, hasKey, isJsonImportDialect, isJsonObject, JSON_IMPORT_DIALECTS, META_KEY, NODE_LINK_SOURCE_KEYS, NODE_LINK_TARGET_KEYS, PARENT_COLUMN, POSITION_COLUMN, sniffJsonDialect, SUFFIX, } from "./dialect.js";
46
47
  import { importObographs } from "./obographs.js";
48
+ import { jsonPositions } from "./positions.js";
49
+ import { jsonStyles } from "./styles.js";
47
50
  /**
48
51
  * The issue codes the JSON importer records, by name: the codes shared with
49
52
  * the other importers and the JSON-specific ones. A key is the code without
@@ -218,12 +221,18 @@ const JGF_EDGE_KEYS = new Set([
218
221
  ]);
219
222
  /** The JGF hyperedge keys that are not metadata. */
220
223
  const JGF_HYPEREDGE_KEYS = new Set([...JGF_EDGE_KEYS, "nodes"]);
224
+ /** A key JavaScript orders as an array index: digits with no leading zero, below 2^32 - 1. */
225
+ const ARRAY_INDEX = /^(?:0|[1-9]\d{0,9})$/;
221
226
  /** The JGF node keys that are not metadata. */
222
227
  const JGF_NODE_KEYS = new Set(["label", "metadata"]);
223
228
  /** The graphology node keys that are not attributes. */
224
229
  const GRAPHOLOGY_NODE_KEYS = new Set(["key", "attributes"]);
225
230
  /** The graphology edge keys that are not attributes. */
226
231
  const GRAPHOLOGY_EDGE_KEYS = new Set(["key", "source", "target", "attributes", "undirected"]);
232
+ /** The structural keys of a node in Gephi's JSON export. */
233
+ const GEPHI_NODE_KEYS = new Set(["id", "attributes"]);
234
+ /** The structural keys of an edge in Gephi's JSON export. */
235
+ const GEPHI_EDGE_KEYS = new Set([...GRAPHOLOGY_EDGE_KEYS, "id"]);
227
236
  /** The vis.js endpoint keys. */
228
237
  const VIS_SOURCE_KEYS = Object.freeze(["from"]);
229
238
  const VIS_TARGET_KEYS = Object.freeze(["to"]);
@@ -634,6 +643,8 @@ export class ImportContext {
634
643
  * @param explicitDefaultDirected - whether the caller passed defaultDirected
635
644
  */
636
645
  constructor(sink, report, options, json, explicitDefaultDirected) {
646
+ /** The document's text, for an order JSON.parse does not keep (a JGF nodes object's integer-like keys). */
647
+ this.text = "";
637
648
  /** Nodes and edges pushed since the signal was last checked. */
638
649
  this.elementsSinceCheck = 0;
639
650
  /** The direction the file declares (or the default), set by setHeader(). */
@@ -643,6 +654,8 @@ export class ImportContext {
643
654
  this.danglingCount = 0;
644
655
  /** Set when the document has no node section: every node comes from an edge (E_MISSING_SECTION says so). */
645
656
  this.nodesFromEdges = false;
657
+ /** What the dialect's reader keeps for the style reader (`report.keepForStyles()`), or null. */
658
+ this.styleSource = null;
646
659
  /** Dangling endpoints whose id, as the other JSON type, is a node (`"1"` next to the node 1). */
647
660
  this.nearMatches = [];
648
661
  this.sink = sink;
@@ -790,6 +803,31 @@ export class ImportContext {
790
803
  }
791
804
  return edge;
792
805
  }
806
+ /**
807
+ * A Cytoscape.js endpoint or parent id: Cytoscape.js holds every id as text, so an endpoint `0` names the node
808
+ * `"0"` and an endpoint `"0"` the node `0` when the file has no node of the endpoint's own type.
809
+ * @param raw - the JSON value
810
+ * @param element - the element name for issues
811
+ * @returns the id, or null when the value was reported
812
+ */
813
+ textualId(raw, element) {
814
+ const id = this.coerceId(raw, element);
815
+ if (id === null || this.sink.indexOf(id) !== INVALID_INDEX) {
816
+ return id;
817
+ }
818
+ let other = null;
819
+ if (typeof raw === "number" && Number.isFinite(raw)) {
820
+ other = String(raw);
821
+ }
822
+ else if (typeof raw === "string" && /^-?\d+(?:\.\d+)?$/.test(raw)) {
823
+ other = Number(raw);
824
+ }
825
+ if (other === null) {
826
+ return id;
827
+ }
828
+ const alt = this.ids.value(other);
829
+ return this.sink.indexOf(alt) === INVALID_INDEX ? id : alt;
830
+ }
793
831
  /**
794
832
  * Count a placeholder node an edge endpoint created.
795
833
  * @param id - the endpoint
@@ -953,10 +991,14 @@ export class ImportContext {
953
991
  * @param record - the element record
954
992
  * @param dict - the nested attribute dict
955
993
  * @param structural - the element keys that are not attributes
956
- * @param weightFrom - the weight key to skip in the dict, or null
957
994
  * @param element - the element name for issues
995
+ * @param how - `weightFrom`: the weight key to skip in the dict; `plain`: write the element-level keys under their
996
+ * own names, not with the `#element` suffix
997
+ * @param how.weightFrom - the weight key to skip in the dict, or null (the default)
998
+ * @param how.plain - write the element-level keys under their own names (default false)
958
999
  */
959
- writeNested(writer, row, record, dict, structural, weightFrom, element) {
1000
+ writeNested(writer, row, record, dict, structural, element, how = {}) {
1001
+ const { weightFrom = null, plain = false } = how;
960
1002
  for (const key of Object.keys(dict)) {
961
1003
  if (key !== weightFrom) {
962
1004
  writer.write(row, key, dict[key], SUFFIX.data, element);
@@ -964,7 +1006,7 @@ export class ImportContext {
964
1006
  }
965
1007
  for (const key of Object.keys(record)) {
966
1008
  if (!structural.has(key)) {
967
- writer.write(row, `${key}${SUFFIX.element}`, record[key], SUFFIX.data, element);
1009
+ writer.write(row, plain ? key : `${key}${SUFFIX.element}`, record[key], SUFFIX.data, element);
968
1010
  }
969
1011
  }
970
1012
  }
@@ -1352,9 +1394,15 @@ function reportUnreadKeys(ctx, record, known, prefix) {
1352
1394
  }
1353
1395
  }
1354
1396
  /** The keys of a vis.js document the reader reads. */
1355
- const VIS_DOCUMENT_KEYS = new Set(["nodes", "edges"]);
1397
+ const VIS_DOCUMENT_KEYS = new Set(["nodes", "edges", STYLE_BLOCK_NAME]);
1356
1398
  /** The keys of a graphology serialization the reader reads. */
1357
- const GRAPHOLOGY_DOCUMENT_KEYS = new Set(["nodes", "edges", "options", "attributes"]);
1399
+ const GRAPHOLOGY_DOCUMENT_KEYS = new Set([
1400
+ "nodes",
1401
+ "edges",
1402
+ "options",
1403
+ "attributes",
1404
+ STYLE_BLOCK_NAME,
1405
+ ]);
1358
1406
  /** The keys of a JGF graph object the reader reads. */
1359
1407
  const JGF_GRAPH_KEYS = new Set([
1360
1408
  "id",
@@ -1393,94 +1441,33 @@ function isIndexBelow(value, bound) {
1393
1441
  */
1394
1442
  function importNodeLink(ctx, root, dialect) {
1395
1443
  const { report, json } = ctx;
1396
- let { edgesKey } = json;
1397
- if (edgesKey === null) {
1398
- edgesKey = hasKey(root, "edges") || !hasKey(root, "links") ? "edges" : "links";
1399
- }
1400
- const nodes = arraySection(root.nodes, "nodes", report);
1401
- const edges = arraySection(root[edgesKey], edgesKey, report);
1402
- if (nodes === null) {
1403
- report.warning("missing-value", JSON_ISSUE.MISSING_SECTION, "the document has no nodes array; nodes come from the edges", { element: "nodes" });
1404
- ctx.nodesFromEdges = true;
1405
- }
1406
- if (edges === null) {
1407
- report.warning("missing-value", JSON_ISSUE.MISSING_SECTION, `the document has no ${edgesKey} array; the graph has no edges`, { element: edgesKey });
1408
- }
1444
+ const edgesKey = json.edgesKey ?? (hasKey(root, "edges") || !hasKey(root, "links") ? "edges" : "links");
1445
+ const { nodeList, edgeList } = nodeLinkSections(ctx, root, edgesKey);
1409
1446
  const directed = flagOf(root.directed, "directed", ctx.defaultDirected(dialect), report);
1410
1447
  const multigraph = hasKey(root, "multigraph") ? flagOf(root.multigraph, "multigraph", false, report) : null;
1411
1448
  ctx.setHeader(directed);
1412
1449
  ctx.writeGraphDict(root.graph, "graph");
1413
- reportUnreadKeys(ctx, root, new Set(["nodes", edgesKey, "directed", "multigraph", "graph"]), "");
1414
- const nodeList = nodes ?? [];
1415
- const edgeList = edges ?? [];
1450
+ reportUnreadKeys(ctx, root, new Set(["nodes", edgesKey, "directed", "multigraph", "graph", STYLE_BLOCK_NAME]), "");
1451
+ // NetworkX's node_link_data writes all three keys; its drawing defaults then apply
1452
+ const networkx = hasKey(root, "directed") && hasKey(root, "multigraph") && hasKey(root, "graph");
1453
+ ctx.styleSource = { dialect, block: blockOf(root), networkx };
1416
1454
  ctx.sink.reserve(nodeList.length, edgeList.length);
1417
- // the id key: the caller's, else "id" when any node has it, else "name" (d3); nodes without any
1418
- // id key are positional (nodeIdFrom "index", or a d3 v3 file whose nodes carry no id at all)
1419
- let { nodeIdKey } = json;
1420
- let positional = ctx.options.nodeIdFrom === "index";
1421
- if (!positional && nodeIdKey === null) {
1422
- const candidates = ctx.options.nodeIdFrom === "label" ? ["label", "name", "id"] : ["id", "name"];
1423
- nodeIdKey = candidates.find((key) => anyHas(nodeList, key)) ?? null;
1424
- if (ctx.options.nodeIdFrom === "label" && (nodeIdKey === "id" || nodeIdKey === "name")) {
1425
- report.warning("unsupported", JSON_ISSUE.OPTION_IGNORED, nodeIdKey === "id"
1426
- ? 'nodeIdFrom "label": no node has a label or name key; ids are read from "id"'
1427
- : 'nodeIdFrom "label": no node has a "label" key; ids are read from "name"', { element: "nodeIdFrom" });
1428
- }
1429
- if (nodeIdKey === null) {
1430
- if (nodeList.length > 0) {
1431
- positional = true;
1432
- report.warning("missing-value", JSON_ISSUE.POSITIONAL_NODES, "no node has an id or name key; array positions are the node ids", { element: "nodes" });
1433
- }
1434
- else {
1435
- nodeIdKey = "id";
1436
- }
1437
- }
1438
- }
1439
- if (positional) {
1440
- nodeIdKey = null;
1441
- }
1455
+ const nodeIdKey = nodeLinkIdKey(ctx, nodeList);
1456
+ const positional = nodeIdKey === null;
1442
1457
  let indexLinks;
1443
1458
  if (positional) {
1444
1459
  indexLinks = true;
1445
1460
  }
1446
1461
  else if (json.indexLinks === "auto") {
1447
- indexLinks = nodeIdKey !== null && looksIndexLinked(nodeList, edgeList, nodeIdKey, json);
1462
+ indexLinks = looksIndexLinked(nodeList, edgeList, nodeIdKey, json);
1448
1463
  }
1449
1464
  else {
1450
1465
  ({ indexLinks } = json);
1451
1466
  }
1452
- const positionIds = indexLinks ? [] : null;
1453
- if (indexLinks && !positional && json.indexLinks === "auto" && nodeIdKey !== null) {
1467
+ if (indexLinks && !positional && json.indexLinks === "auto") {
1454
1468
  reportAmbiguousIndexLinks(ctx, nodeList, edgeList, nodeIdKey);
1455
1469
  }
1456
- for (let i = 0; i < nodeList.length; i++) {
1457
- const element = `nodes[${i}]`;
1458
- const record = nodeList[i];
1459
- let pushed = null;
1460
- if (!isJsonObject(record)) {
1461
- ctx.badElement("node", element);
1462
- }
1463
- else {
1464
- let id;
1465
- if (nodeIdKey === null) {
1466
- id = i;
1467
- }
1468
- else {
1469
- id = ctx.coerceId(hasKey(record, nodeIdKey) ? record[nodeIdKey] : undefined, element);
1470
- }
1471
- if (id === null) {
1472
- ctx.countSkipped("node");
1473
- }
1474
- else {
1475
- const index = ctx.pushNode(id, element);
1476
- if (index >= 0) {
1477
- pushed = id;
1478
- writeFlat(ctx.nodes, index, record, id, (key) => key !== nodeIdKey);
1479
- }
1480
- }
1481
- }
1482
- positionIds?.push(pushed);
1483
- }
1470
+ const positionIds = importNodeLinkNodes(ctx, nodeList, nodeIdKey, indexLinks);
1484
1471
  throwIfAborted(ctx.options.signal);
1485
1472
  const endpointKeys = importNodeLinkEdges(ctx, edgeList, edgesKey, positionIds, multigraph);
1486
1473
  ctx.setMeta({
@@ -1492,6 +1479,103 @@ function importNodeLink(ctx, root, dialect) {
1492
1479
  targetKey: endpointKeys.target ?? undefined,
1493
1480
  }, { declaredMultigraph: multigraph, ...ctx.weightOriginPatch() });
1494
1481
  }
1482
+ /**
1483
+ * The node and edge arrays of a node-link or d3 document; a missing one is reported (without nodes, nodes come from
1484
+ * the edges).
1485
+ * @param ctx - the context
1486
+ * @param root - the document
1487
+ * @param edgesKey - the key of the edges
1488
+ * @returns the arrays, empty when missing
1489
+ */
1490
+ function nodeLinkSections(ctx, root, edgesKey) {
1491
+ const { report } = ctx;
1492
+ const nodes = arraySection(root.nodes, "nodes", report);
1493
+ const edges = arraySection(root[edgesKey], edgesKey, report);
1494
+ if (nodes === null) {
1495
+ report.warning("missing-value", JSON_ISSUE.MISSING_SECTION, "the document has no nodes array; nodes come from the edges", { element: "nodes" });
1496
+ ctx.nodesFromEdges = true;
1497
+ }
1498
+ if (edges === null) {
1499
+ report.warning("missing-value", JSON_ISSUE.MISSING_SECTION, `the document has no ${edgesKey} array; the graph has no edges`, { element: edgesKey });
1500
+ }
1501
+ return { nodeList: nodes ?? [], edgeList: edges ?? [] };
1502
+ }
1503
+ /**
1504
+ * The key node ids are read from: the caller's, else "id" when any node has it, else "name" (d3); null when the nodes
1505
+ * are positional (nodeIdFrom "index", or a d3 v3 file whose nodes carry no id at all).
1506
+ * @param ctx - the context
1507
+ * @param nodeList - the node records
1508
+ * @returns the key, or null for positional nodes
1509
+ */
1510
+ function nodeLinkIdKey(ctx, nodeList) {
1511
+ const { report, json } = ctx;
1512
+ if (ctx.options.nodeIdFrom === "index") {
1513
+ return null;
1514
+ }
1515
+ if (json.nodeIdKey !== null) {
1516
+ return json.nodeIdKey;
1517
+ }
1518
+ const byLabel = ctx.options.nodeIdFrom === "label";
1519
+ const candidates = byLabel ? ["label", "name", "id"] : ["id", "name"];
1520
+ const found = candidates.find((key) => anyHas(nodeList, key)) ?? null;
1521
+ if (byLabel && (found === "id" || found === "name")) {
1522
+ report.warning("unsupported", JSON_ISSUE.OPTION_IGNORED, found === "id"
1523
+ ? 'nodeIdFrom "label": no node has a label or name key; ids are read from "id"'
1524
+ : 'nodeIdFrom "label": no node has a "label" key; ids are read from "name"', { element: "nodeIdFrom" });
1525
+ }
1526
+ if (found !== null || nodeList.length === 0) {
1527
+ return found ?? "id";
1528
+ }
1529
+ report.warning("missing-value", JSON_ISSUE.POSITIONAL_NODES, "no node has an id or name key; array positions are the node ids", { element: "nodes" });
1530
+ return null;
1531
+ }
1532
+ /**
1533
+ * Read the node-link / d3 node records.
1534
+ * @param ctx - the context
1535
+ * @param nodeList - the node records
1536
+ * @param nodeIdKey - the key ids are read from, or null for positional nodes
1537
+ * @param indexLinks - whether edges name their endpoints by array position
1538
+ * @returns the ids by array position under index links (null where a node was not added), else null
1539
+ */
1540
+ function importNodeLinkNodes(ctx, nodeList, nodeIdKey, indexLinks) {
1541
+ const positionIds = indexLinks ? [] : null;
1542
+ for (let i = 0; i < nodeList.length; i++) {
1543
+ const element = `nodes[${i}]`;
1544
+ const record = nodeList[i];
1545
+ let pushed = null;
1546
+ if (isJsonObject(record)) {
1547
+ pushed = pushNodeLinkNode(ctx, record, i, nodeIdKey);
1548
+ }
1549
+ else {
1550
+ ctx.badElement("node", element);
1551
+ }
1552
+ positionIds?.push(pushed);
1553
+ }
1554
+ return positionIds;
1555
+ }
1556
+ /**
1557
+ * Add one node-link / d3 node and its other keys as attributes.
1558
+ * @param ctx - the context
1559
+ * @param record - the node record
1560
+ * @param i - its array position
1561
+ * @param nodeIdKey - the key its id is read from, or null for positional nodes
1562
+ * @returns its id, or null when it was not added
1563
+ */
1564
+ function pushNodeLinkNode(ctx, record, i, nodeIdKey) {
1565
+ const element = `nodes[${i}]`;
1566
+ const raw = nodeIdKey !== null && hasKey(record, nodeIdKey) ? record[nodeIdKey] : undefined;
1567
+ const id = nodeIdKey === null ? i : ctx.coerceId(raw, element);
1568
+ if (id === null) {
1569
+ ctx.countSkipped("node");
1570
+ return null;
1571
+ }
1572
+ const index = ctx.pushNode(id, element);
1573
+ if (index < 0) {
1574
+ return null;
1575
+ }
1576
+ writeFlat(ctx.nodes, index, record, id, (key) => key !== nodeIdKey);
1577
+ return id;
1578
+ }
1495
1579
  /**
1496
1580
  * Read the node-link / d3 edge records: endpoints by id or array position, the weight, the other
1497
1581
  * keys as attributes.
@@ -1770,6 +1854,7 @@ function importVis(ctx, root) {
1770
1854
  if (arrows > 0) {
1771
1855
  report.warning("validation-error", JSON_ISSUE.INCONSISTENT, `${arrows} edge${plural(arrows)} ${agree(arrows, "carries", "carry")} vis.js arrows but the graph is read undirected; the arrows are kept as an attribute (pass defaultDirected: true to read the edges as directed)`, { element: "arrows" });
1772
1856
  }
1857
+ ctx.styleSource = { dialect: "vis", block: blockOf(root), options: root.options, groups: root.groups };
1773
1858
  ctx.setMeta({ dialect: "vis", nodeIdKey, sourceKey: sourceKey ?? undefined, targetKey: targetKey ?? undefined }, ctx.weightOriginPatch());
1774
1859
  }
1775
1860
  // ============================================================ NetworkX adjacency_data / tree_data
@@ -2083,36 +2168,29 @@ function importGraphology(ctx, root) {
2083
2168
  });
2084
2169
  }
2085
2170
  const type = graphologyType(ctx, options);
2086
- ctx.setHeader(graphologyDirected(ctx, type, edges));
2171
+ ctx.setHeader(type === "mixed" ? mostlyDirected(ctx, edges) : type === "directed");
2087
2172
  const multi = hasKey(options, "multi") ? flagOf(options.multi, "options.multi", false, report) : null;
2088
2173
  const allowSelfLoops = hasKey(options, "allowSelfLoops")
2089
2174
  ? flagOf(options.allowSelfLoops, "options.allowSelfLoops", true, report)
2090
2175
  : undefined;
2091
2176
  ctx.writeGraphDict(root.attributes, "attributes");
2177
+ // Gephi's JSON export (for sigma.js) names nodes and edges by `id` and puts sigma's display keys (`label`, `x`,
2178
+ // `y`, `color`, `size`) beside `attributes`; they are read as attributes, not as unknown element keys
2179
+ const firstNode = nodes.find((n) => isJsonObject(n));
2180
+ const gephi = isJsonObject(firstNode) && !hasKey(firstNode, "key") && hasKey(firstNode, "id");
2181
+ ctx.styleSource = { dialect: "graphology", block: blockOf(root), gephi };
2092
2182
  ctx.reportNodeIdFrom("graphology", "the node key");
2093
2183
  ctx.sink.reserve(nodes.length, edges.length);
2094
- for (let i = 0; i < nodes.length; i++) {
2095
- pushGraphologyNode(ctx, nodes[i], `nodes[${i}]`);
2096
- }
2184
+ importGraphologyNodes(ctx, nodes, gephi);
2097
2185
  throwIfAborted(ctx.options.signal);
2098
- const reading = {
2099
- ids: ctx.declareEdgeIds(edges, (edge) => edge.key, "key", true),
2100
- type,
2101
- allowSelfLoops,
2102
- // what the edges do against the declared options: graphology itself refuses such a document
2103
- violations: { selfLoops: 0, parallels: 0, flags: 0 },
2104
- pairs: multi === false ? new Set() : null,
2105
- };
2106
- for (let i = 0; i < edges.length; i++) {
2107
- readGraphologyEdge(ctx, reading, edges[i], `edges[${i}]`);
2108
- }
2109
- reportGraphologyViolations(ctx, reading.violations, type);
2186
+ const violations = importGraphologyEdges(ctx, edges, { type, gephi, multi, allowSelfLoops });
2187
+ reportGraphologyViolations(ctx, violations, type);
2110
2188
  ctx.setMeta({ dialect: "graphology", allowSelfLoops }, { declaredMultigraph: multi, ...ctx.weightOriginPatch() });
2111
2189
  }
2112
2190
  /**
2113
- * A graphology document's `options.type`; anything else is reported and read as "mixed".
2191
+ * A graphology document's `options.type`: "mixed" when absent or not one of the three (reported).
2114
2192
  * @param ctx - the context
2115
- * @param options - the options object
2193
+ * @param options - the document's options
2116
2194
  * @returns the type
2117
2195
  */
2118
2196
  function graphologyType(ctx, options) {
@@ -2125,114 +2203,133 @@ function graphologyType(ctx, options) {
2125
2203
  return "mixed";
2126
2204
  }
2127
2205
  /**
2128
- * The header direction of a graphology document: its type, or for "mixed" directed unless every
2129
- * edge object says undirected (the dialect default when there is none).
2206
+ * The header direction of a mixed graphology graph: directed unless every edge is flagged undirected.
2130
2207
  * @param ctx - the context
2131
- * @param type - options.type
2132
2208
  * @param edges - the edge records
2133
- * @returns whether the header is directed
2209
+ * @returns true for directed
2134
2210
  */
2135
- function graphologyDirected(ctx, type, edges) {
2136
- if (type !== "mixed") {
2137
- return type === "directed";
2138
- }
2211
+ function mostlyDirected(ctx, edges) {
2139
2212
  const objects = edges.filter((edge) => isJsonObject(edge));
2140
2213
  const undirectedEdges = objects.filter((edge) => edge.undirected === true).length;
2141
2214
  return objects.length === 0 ? ctx.defaultDirected("graphology") : undirectedEdges < objects.length;
2142
2215
  }
2143
2216
  /**
2144
- * Push one graphology node record: `key` the id, `attributes` the columns.
2217
+ * Read the graphology node records.
2145
2218
  * @param ctx - the context
2146
- * @param record - the node record
2147
- * @param element - the element name
2219
+ * @param nodes - the node records
2220
+ * @param gephi - whether Gephi wrote them (ids under `id`, display keys beside the attributes)
2148
2221
  */
2149
- function pushGraphologyNode(ctx, record, element) {
2150
- if (!isJsonObject(record)) {
2151
- ctx.badElement("node", element);
2152
- return;
2153
- }
2154
- const id = ctx.coerceId(hasKey(record, "key") ? record.key : undefined, element);
2155
- if (id === null) {
2156
- ctx.countSkipped("node");
2157
- return;
2222
+ function importGraphologyNodes(ctx, nodes, gephi) {
2223
+ const nodeKey = gephi ? "id" : "key";
2224
+ for (let i = 0; i < nodes.length; i++) {
2225
+ const element = `nodes[${i}]`;
2226
+ const record = nodes[i];
2227
+ if (!isJsonObject(record)) {
2228
+ ctx.badElement("node", element);
2229
+ continue;
2230
+ }
2231
+ const id = ctx.coerceId(hasKey(record, nodeKey) ? record[nodeKey] : undefined, element);
2232
+ if (id === null) {
2233
+ ctx.countSkipped("node");
2234
+ continue;
2235
+ }
2236
+ pushNestedNode(ctx, id, record, "attributes", gephi ? GEPHI_NODE_KEYS : GRAPHOLOGY_NODE_KEYS, element, gephi);
2158
2237
  }
2159
- pushNestedNode(ctx, id, record, "attributes", GRAPHOLOGY_NODE_KEYS, element);
2160
2238
  }
2161
2239
  /**
2162
- * Read one graphology edge record, counting what it does against the declared options.
2240
+ * Read the graphology edge records.
2163
2241
  * @param ctx - the context
2164
- * @param reading - the read's state
2165
- * @param record - the edge record
2166
- * @param element - the element name
2167
- */
2168
- function readGraphologyEdge(ctx, reading, record, element) {
2169
- if (!isJsonObject(record)) {
2170
- ctx.badElement("edge", element);
2171
- return;
2172
- }
2173
- if (!present(record, "source") || !present(record, "target")) {
2174
- ctx.missingEndpoint(element, present(record, "source") ? "target" : "source");
2175
- return;
2176
- }
2177
- try {
2178
- const u = ctx.coerceId(record.source, `${element}.source`);
2179
- const v = ctx.coerceId(record.target, `${element}.target`);
2180
- if (u === null || v === null) {
2181
- ctx.countSkipped("edge");
2182
- return;
2242
+ * @param edges - the edge records
2243
+ * @param declared - what the document declares
2244
+ * @returns the edges that contradict the declared options: graphology itself refuses such a document
2245
+ */
2246
+ function importGraphologyEdges(ctx, edges, declared) {
2247
+ const { gephi } = declared;
2248
+ const ids = ctx.declareEdgeIds(edges, (edge) => (gephi && !hasKey(edge, "key") ? edge.id : edge.key), "key", true);
2249
+ const violations = { selfLoops: 0, parallels: 0, flags: 0 };
2250
+ const pairs = declared.multi === false ? new Set() : null;
2251
+ for (let i = 0; i < edges.length; i++) {
2252
+ const element = `edges[${i}]`;
2253
+ const record = graphologyEdgeRecord(ctx, edges[i], element);
2254
+ if (record === null) {
2255
+ continue;
2183
2256
  }
2184
- const kind = graphologyEdgeKind(ctx, reading, record, element);
2185
- const attributes = graphologyEdgeAttributes(ctx, record, element);
2186
- const idValue = ctx.edgeIdValue(reading.ids, record.key);
2187
- const edge = ctx.pushEdge(u, v, kind, ctx.weightOf(attributes), element);
2188
- ctx.setEdgeId(reading.ids, edge, idValue);
2189
- ctx.writeNested(ctx.edges, edge, record, attributes, GRAPHOLOGY_EDGE_KEYS, ctx.options.weightFrom, element);
2190
- if (reading.allowSelfLoops === false && u === v) {
2191
- reading.violations.selfLoops++;
2257
+ try {
2258
+ const pushed = pushGraphologyEdge(ctx, record, element, ids, declared, violations);
2259
+ if (pushed !== null && pairs !== null) {
2260
+ violations.parallels += pairs.has(pushed) ? 1 : 0;
2261
+ pairs.add(pushed);
2262
+ }
2192
2263
  }
2193
- if (reading.pairs !== null) {
2194
- const pair = `${kind} ${pairKey(u, v, kind === "directed")}`;
2195
- reading.violations.parallels += reading.pairs.has(pair) ? 1 : 0;
2196
- reading.pairs.add(pair);
2264
+ catch (err) {
2265
+ ctx.skip(err, "edge", element);
2197
2266
  }
2198
2267
  }
2199
- catch (err) {
2200
- ctx.skip(err, "edge", element);
2201
- }
2268
+ return violations;
2202
2269
  }
2203
2270
  /**
2204
- * The direction of one graphology edge: its `undirected` flag under type "mixed", else the type
2205
- * (a contrary flag is counted as a violation).
2271
+ * One graphology edge record, when it is an object with both endpoints.
2206
2272
  * @param ctx - the context
2207
- * @param reading - the read's state
2208
2273
  * @param record - the edge record
2209
2274
  * @param element - the element name
2210
- * @returns the edge's kind
2275
+ * @returns the record, or null when it was reported and is skipped
2211
2276
  */
2212
- function graphologyEdgeKind(ctx, reading, record, element) {
2213
- const { type } = reading;
2214
- if (type === "mixed") {
2215
- return flagOf(record.undirected, `${element}.undirected`, false, ctx.report) ? "undirected" : "directed";
2277
+ function graphologyEdgeRecord(ctx, record, element) {
2278
+ if (!isJsonObject(record)) {
2279
+ ctx.badElement("edge", element);
2280
+ return null;
2216
2281
  }
2217
- if (typeof record.undirected === "boolean" && record.undirected !== (type === "undirected")) {
2218
- reading.violations.flags++;
2282
+ if (!present(record, "source") || !present(record, "target")) {
2283
+ ctx.missingEndpoint(element, present(record, "source") ? "target" : "source");
2284
+ return null;
2219
2285
  }
2220
- return type;
2286
+ return record;
2221
2287
  }
2222
2288
  /**
2223
- * The `attributes` object of a graphology edge; any other non-null value is reported and read as
2224
- * no attributes.
2289
+ * Add one graphology edge with its id and attributes.
2225
2290
  * @param ctx - the context
2226
2291
  * @param record - the edge record
2227
2292
  * @param element - the element name
2228
- * @returns the attributes
2293
+ * @param ids - the edge id column
2294
+ * @param declared - what the document declares
2295
+ * @param violations - counts the edges that contradict the declared options
2296
+ * @returns the edge's kind and endpoint pair, or null when it was skipped
2229
2297
  */
2230
- function graphologyEdgeAttributes(ctx, record, element) {
2231
- const { attributes } = record;
2232
- if (attributes !== undefined && attributes !== null && !isJsonObject(attributes)) {
2233
- ctx.report.error("validation-error", JSON_ISSUE.BAD_VALUE, `${element}: attributes must be an object, found ${describe(attributes)}`, { element });
2298
+ function pushGraphologyEdge(ctx, record, element, ids, declared, violations) {
2299
+ const { report } = ctx;
2300
+ const { type, gephi } = declared;
2301
+ const u = ctx.coerceId(record.source, `${element}.source`);
2302
+ const v = ctx.coerceId(record.target, `${element}.target`);
2303
+ if (u === null || v === null) {
2304
+ ctx.countSkipped("edge");
2305
+ return null;
2306
+ }
2307
+ let kind;
2308
+ if (type === "mixed") {
2309
+ kind = flagOf(record.undirected, `${element}.undirected`, false, report) ? "undirected" : "directed";
2234
2310
  }
2235
- return isJsonObject(attributes) ? attributes : {};
2311
+ else {
2312
+ kind = type;
2313
+ if (typeof record.undirected === "boolean" && record.undirected !== (type === "undirected")) {
2314
+ violations.flags++;
2315
+ }
2316
+ }
2317
+ const { attributes: rawAttributes } = record;
2318
+ if (rawAttributes !== undefined && rawAttributes !== null && !isJsonObject(rawAttributes)) {
2319
+ report.error("validation-error", JSON_ISSUE.BAD_VALUE, `${element}: attributes must be an object, found ${describe(rawAttributes)}`, { element });
2320
+ }
2321
+ const attributes = isJsonObject(rawAttributes) ? rawAttributes : {};
2322
+ const idValue = ctx.edgeIdValue(ids, record.key);
2323
+ const edge = ctx.pushEdge(u, v, kind, ctx.weightOf(attributes), element);
2324
+ ctx.setEdgeId(ids, edge, idValue);
2325
+ ctx.writeNested(ctx.edges, edge, record, attributes, gephi ? GEPHI_EDGE_KEYS : GRAPHOLOGY_EDGE_KEYS, element, {
2326
+ weightFrom: ctx.options.weightFrom,
2327
+ plain: gephi,
2328
+ });
2329
+ if (declared.allowSelfLoops === false && u === v) {
2330
+ violations.selfLoops++;
2331
+ }
2332
+ return `${kind} ${pairKey(u, v, kind === "directed")}`;
2236
2333
  }
2237
2334
  /**
2238
2335
  * Report the edges that contradict a graphology document's declared options (W_JSON_INCONSISTENT,
@@ -2270,9 +2367,10 @@ function reportGraphologyViolations(ctx, violations, type) {
2270
2367
  * @param dictKey - the key of the nested dict
2271
2368
  * @param structural - the element keys that are not attributes
2272
2369
  * @param element - the element name
2370
+ * @param plain - write the element-level keys under their own names
2273
2371
  * @returns the node index, or -1 when skipped
2274
2372
  */
2275
- function pushNestedNode(ctx, id, record, dictKey, structural, element) {
2373
+ function pushNestedNode(ctx, id, record, dictKey, structural, element, plain = false) {
2276
2374
  const index = ctx.pushNode(id, element);
2277
2375
  if (index < 0) {
2278
2376
  return index;
@@ -2282,7 +2380,7 @@ function pushNestedNode(ctx, id, record, dictKey, structural, element) {
2282
2380
  if (dict !== undefined && dict !== null && !isJsonObject(dict)) {
2283
2381
  ctx.report.error("validation-error", JSON_ISSUE.BAD_VALUE, `${element}: ${dictKey} must be an object, found ${describe(dict)}`, { element });
2284
2382
  }
2285
- ctx.writeNested(ctx.nodes, index, record, isJsonObject(dict) ? dict : {}, structural, null, element);
2383
+ ctx.writeNested(ctx.nodes, index, record, isJsonObject(dict) ? dict : {}, structural, element, { plain });
2286
2384
  }
2287
2385
  catch (err) {
2288
2386
  ctx.report.recordError(err, { element: String(id) });
@@ -2320,7 +2418,7 @@ function importJgf(ctx, root) {
2320
2418
  ctx.sink.reserve(nodeRecords.length, edges.length);
2321
2419
  const labelColumn = ctx.nodes.declareIf({ name: "label", dtype: "string", role: "label", nullable: true }, anyHas(nodeRecords, "label"));
2322
2420
  if (isJsonObject(nodesRaw)) {
2323
- for (const key of Object.keys(nodesRaw)) {
2421
+ for (const key of jgfNodeKeys(ctx, root, graph, nodesRaw)) {
2324
2422
  pushJgfKeyedNode(ctx, key, nodesRaw[key], labelColumn);
2325
2423
  }
2326
2424
  }
@@ -2426,7 +2524,7 @@ function jgfEdgeWriter(ctx, edges, hyperedges) {
2426
2524
  element,
2427
2525
  });
2428
2526
  }
2429
- ctx.writeNested(ctx.edges, edge, record, dict, structural, ctx.options.weightFrom, element);
2527
+ ctx.writeNested(ctx.edges, edge, record, dict, structural, element, { weightFrom: ctx.options.weightFrom });
2430
2528
  };
2431
2529
  }
2432
2530
  /**
@@ -2460,6 +2558,25 @@ function readJgfEdge(ctx, record, element, directed, writeJgfEdge) {
2460
2558
  ctx.skip(err, "edge", element);
2461
2559
  }
2462
2560
  }
2561
+ /**
2562
+ * The keys of a JGF nodes object in the file's order, which is the nodes' order: JSON.parse puts integer-like keys
2563
+ * first, so then they are read from the text.
2564
+ * @param ctx - the context, holding the text
2565
+ * @param root - the document
2566
+ * @param graph - the graph object read
2567
+ * @param nodes - its nodes object
2568
+ * @returns the keys
2569
+ */
2570
+ function jgfNodeKeys(ctx, root, graph, nodes) {
2571
+ const keys = Object.keys(nodes);
2572
+ if (!keys.some((k) => ARRAY_INDEX.test(k))) {
2573
+ return keys;
2574
+ }
2575
+ const path = isJsonObject(root.graph)
2576
+ ? ["graph", "nodes"]
2577
+ : ["graphs", root.graphs.indexOf(graph), "nodes"];
2578
+ return keysInTextOrder(ctx.text, path) ?? keys;
2579
+ }
2463
2580
  /**
2464
2581
  * The graph object of a JGF document: `graph`, or the graph of `graphs` that graphIndex /
2465
2582
  * graphName choose (chooseGraph()).
@@ -2613,7 +2730,7 @@ function pushJgfNode(ctx, id, record, labelColumn, element) {
2613
2730
  element,
2614
2731
  });
2615
2732
  }
2616
- ctx.writeNested(ctx.nodes, index, record, isJsonObject(metadata) ? metadata : {}, JGF_NODE_KEYS, null, element);
2733
+ ctx.writeNested(ctx.nodes, index, record, isJsonObject(metadata) ? metadata : {}, JGF_NODE_KEYS, element);
2617
2734
  }
2618
2735
  catch (err) {
2619
2736
  ctx.report.recordError(err, { element: String(id) });
@@ -2768,42 +2885,44 @@ function distinctMembers(ctx, members, element) {
2768
2885
  function importCytoscape(ctx, root) {
2769
2886
  const { elements, extra } = cytoscapeRoot(ctx, root);
2770
2887
  const { nodes, edges } = cytoscapeSections(ctx, elements);
2888
+ const generatedIds = cytoscapeGeneratedIds(nodes);
2889
+ ctx.styleSource = {
2890
+ dialect: "cytoscape",
2891
+ block: blockOf(root),
2892
+ elements: elements ?? [],
2893
+ style: isJsonObject(root) ? root.style : undefined,
2894
+ generatedBy: isJsonObject(root) && typeof root.generated_by === "string" ? root.generated_by : null,
2895
+ generatedIds,
2896
+ };
2771
2897
  ctx.setHeader(ctx.defaultDirected("cytoscape"));
2772
2898
  ctx.reportNodeIdFrom("cytoscape", "data.id");
2773
2899
  ctx.sink.reserve(nodes.length, edges.length);
2774
- const dataHas = (items, key) => items.some((item) => isJsonObject(item) && isJsonObject(item.data) && item.data[key] !== undefined);
2775
- const reading = {
2776
- positionColumn: ctx.nodes.declareIf({
2777
- name: POSITION_COLUMN,
2778
- dtype: "f64",
2779
- components: 3,
2780
- role: "position",
2781
- mutable: true,
2782
- nullable: true,
2783
- // 3D layouts written by Cytoscape-compatible tools carry a z
2784
- extra: { sourceDims: nodes.some(hasPositionZ) ? 3 : 2, units: "file" },
2785
- origin: { format: "json", namespace: "cytoscape" },
2786
- }, anyHas(nodes, "position")),
2787
- classesColumn: ctx.nodes.declareIf({ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true }, anyHas(nodes, "classes")),
2788
- parents: [],
2789
- point: [0, 0, 0],
2790
- };
2791
- const parentColumn = ctx.nodes.declareIf({ name: PARENT_COLUMN, dtype: "u32", role: "parent", refersTo: "node", nullable: true }, dataHas(nodes, "parent"));
2900
+ const columns = cytoscapeNodeColumns(ctx, nodes);
2792
2901
  const edgeClassesColumn = ctx.edges.declareIf({ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true }, anyHas(edges, "classes"));
2902
+ const parents = [];
2793
2903
  for (let i = 0; i < nodes.length; i++) {
2794
- readCytoscapeNode(ctx, reading, nodes[i], `nodes[${i}]`);
2904
+ const element = `nodes[${i}]`;
2905
+ const record = nodes[i];
2906
+ if (isJsonObject(record) && isJsonObject(record.data)) {
2907
+ // Cytoscape.js gives a node without data.id an id of its own
2908
+ const raw = record.data.id === undefined ? generatedIds.get(record) : record.data.id;
2909
+ importCytoscapeNode(ctx, record, record.data, raw, element, columns, parents);
2910
+ }
2911
+ else {
2912
+ ctx.badElement("node", element, "an element with a data object");
2913
+ }
2795
2914
  }
2796
- resolveCytoscapeParents(ctx, reading.parents, parentColumn);
2915
+ resolveCytoscapeParents(ctx, parents, columns.parent);
2797
2916
  throwIfAborted(ctx.options.signal);
2798
2917
  importCytoscapeEdges(ctx, edges, edgeClassesColumn);
2799
2918
  ctx.setMeta({ dialect: "cytoscape", cytoscape: extra }, ctx.weightOriginPatch());
2800
2919
  }
2801
2920
  /**
2802
- * The elements of a Cytoscape document (the document itself when it is an array) and, for an
2803
- * object, its graph `data` written and its other top-level keys kept.
2921
+ * A Cytoscape document's elements, and its other top-level keys (kept in meta); its `data` is written as graph
2922
+ * attributes.
2804
2923
  * @param ctx - the context
2805
- * @param root - the document
2806
- * @returns the elements and the other top-level keys, if any
2924
+ * @param root - the document: an object, or an array of elements
2925
+ * @returns the elements and the other keys (undefined when there are none)
2807
2926
  */
2808
2927
  function cytoscapeRoot(ctx, root) {
2809
2928
  if (Array.isArray(root)) {
@@ -2812,31 +2931,53 @@ function cytoscapeRoot(ctx, root) {
2812
2931
  if (!isJsonObject(root)) {
2813
2932
  ctx.report.fail(JSON_ISSUE.SHAPE, `a Cytoscape document must be an object or an array, found ${describe(root)}`);
2814
2933
  }
2815
- ctx.writeGraphDict(root.data, "data");
2934
+ const object = root;
2935
+ ctx.writeGraphDict(object.data, "data");
2816
2936
  const rest = {};
2817
- for (const key of Object.keys(root)) {
2818
- if (key !== "elements" && key !== "data") {
2819
- rest[key] = root[key];
2937
+ for (const key of Object.keys(object)) {
2938
+ if (key !== "elements" && key !== "data" && key !== STYLE_BLOCK_NAME) {
2939
+ rest[key] = object[key];
2820
2940
  }
2821
2941
  }
2822
- return Object.keys(rest).length > 0 ? { elements: root.elements, extra: rest } : { elements: root.elements };
2942
+ return Object.keys(rest).length > 0 ? { elements: object.elements, extra: rest } : { elements: object.elements };
2823
2943
  }
2824
2944
  /**
2825
- * Read one Cytoscape node element: `data.id` the id, the other `data` keys attributes (`parent`
2826
- * kept for later), `position`, `classes` and the element-level keys.
2945
+ * Declare the position, classes and parent columns the node records use.
2946
+ * @param ctx - the context
2947
+ * @param nodes - the node records
2948
+ * @returns the columns
2949
+ */
2950
+ function cytoscapeNodeColumns(ctx, nodes) {
2951
+ const dataHas = (items, key) => items.some((item) => isJsonObject(item) && isJsonObject(item.data) && item.data[key] !== undefined);
2952
+ return {
2953
+ position: ctx.nodes.declareIf({
2954
+ name: POSITION_COLUMN,
2955
+ dtype: "f64",
2956
+ components: 3,
2957
+ role: "position",
2958
+ mutable: true,
2959
+ nullable: true,
2960
+ // 3D layouts written by Cytoscape-compatible tools carry a z
2961
+ extra: { sourceDims: nodes.some(hasPositionZ) ? 3 : 2, units: "file" },
2962
+ origin: { format: "json", namespace: "cytoscape" },
2963
+ }, anyHas(nodes, "position")),
2964
+ classes: ctx.nodes.declareIf({ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true }, anyHas(nodes, "classes")),
2965
+ parent: ctx.nodes.declareIf({ name: PARENT_COLUMN, dtype: "u32", role: "parent", refersTo: "node", nullable: true }, dataHas(nodes, "parent")),
2966
+ };
2967
+ }
2968
+ /**
2969
+ * Add one Cytoscape node: its data as attributes (its parent kept for later), its position, classes and element keys.
2827
2970
  * @param ctx - the context
2828
- * @param reading - the read's state
2829
2971
  * @param record - the element
2972
+ * @param data - its data object
2973
+ * @param raw - its raw id
2830
2974
  * @param element - the element name
2975
+ * @param columns - the node columns
2976
+ * @param parents - receives its parent link
2831
2977
  */
2832
- function readCytoscapeNode(ctx, reading, record, element) {
2978
+ function importCytoscapeNode(ctx, record, data, raw, element, columns, parents) {
2833
2979
  const { report } = ctx;
2834
- if (!isJsonObject(record) || !isJsonObject(record.data)) {
2835
- ctx.badElement("node", element, "an element with a data object");
2836
- return;
2837
- }
2838
- const { data } = record;
2839
- const id = ctx.coerceId(hasKey(data, "id") ? data.id : undefined, element);
2980
+ const id = ctx.coerceId(raw, element);
2840
2981
  if (id === null) {
2841
2982
  ctx.countSkipped("node");
2842
2983
  return;
@@ -2849,9 +2990,18 @@ function readCytoscapeNode(ctx, reading, record, element) {
2849
2990
  report.warning("validation-error", JSON_ISSUE.INCONSISTENT, `${element} has data.source and data.target, the shape of an edge, but its section or group says node; read as a node`, { element });
2850
2991
  }
2851
2992
  try {
2852
- writeCytoscapeData(ctx, reading, index, data, element);
2853
- writeCytoscapePosition(ctx, reading, index, record.position, element);
2854
- writeClasses(ctx, ctx.nodes, reading.classesColumn, index, record.classes, element);
2993
+ for (const key of Object.keys(data).filter((k) => k !== "id")) {
2994
+ if (key !== "parent") {
2995
+ ctx.nodes.write(index, key, data[key], SUFFIX.data, element);
2996
+ continue;
2997
+ }
2998
+ const parent = data.parent === null ? null : ctx.coerceId(data.parent, `${element}.data.parent`);
2999
+ if (parent !== null) {
3000
+ parents.push({ index, parent, element });
3001
+ }
3002
+ }
3003
+ writeCytoscapePosition(ctx, columns.position, index, record.position, element);
3004
+ writeClasses(ctx, ctx.nodes, columns.classes, index, record.classes, element);
2855
3005
  writeElementKeys(ctx.nodes, index, record, element);
2856
3006
  }
2857
3007
  catch (err) {
@@ -2859,53 +3009,21 @@ function readCytoscapeNode(ctx, reading, record, element) {
2859
3009
  }
2860
3010
  }
2861
3011
  /**
2862
- * Write a Cytoscape node's `data` keys as attributes; `parent` is kept as a link to resolve later.
2863
- * @param ctx - the context
2864
- * @param reading - the read's state
2865
- * @param index - the node index
2866
- * @param data - the element's data object
2867
- * @param element - the element name
2868
- */
2869
- function writeCytoscapeData(ctx, reading, index, data, element) {
2870
- for (const key of Object.keys(data)) {
2871
- if (key === "id") {
2872
- continue;
2873
- }
2874
- if (key !== "parent") {
2875
- ctx.nodes.write(index, key, data[key], SUFFIX.data, element);
2876
- continue;
2877
- }
2878
- const raw = data.parent;
2879
- if (raw === undefined || raw === null) {
2880
- continue;
2881
- }
2882
- const parent = ctx.coerceId(raw, `${element}.data.parent`);
2883
- if (parent !== null) {
2884
- reading.parents.push({ index, parent, element });
2885
- }
2886
- }
2887
- }
2888
- /**
2889
- * Write a Cytoscape node's `position` ({ x, y, z? }, y flipped to grow upward); any other value is
2890
- * reported.
3012
+ * Write a Cytoscape node's position, y flipped (Cytoscape's y grows downward; positions are stored y-up and the
3013
+ * exporter flips back).
2891
3014
  * @param ctx - the context
2892
- * @param reading - the read's state
2893
- * @param index - the node index
3015
+ * @param column - the position column
3016
+ * @param index - the node
2894
3017
  * @param position - the element's position
2895
3018
  * @param element - the element name
2896
3019
  */
2897
- function writeCytoscapePosition(ctx, reading, index, position, element) {
3020
+ function writeCytoscapePosition(ctx, column, index, position, element) {
2898
3021
  if (position === undefined || position === null) {
2899
3022
  return;
2900
3023
  }
2901
3024
  const z = isJsonObject(position) ? (position.z ?? 0) : 0;
2902
3025
  if (isJsonObject(position) && isCoordinate(position.x) && isCoordinate(position.y) && isCoordinate(z)) {
2903
- const { point } = reading;
2904
- point[0] = position.x;
2905
- // Cytoscape's y grows downward; positions are stored y-up (the exporter flips back)
2906
- point[1] = flipY(position.y);
2907
- point[2] = z;
2908
- ctx.nodes.set(reading.positionColumn, index, point);
3026
+ ctx.nodes.set(column, index, [position.x, flipY(position.y), z]);
2909
3027
  }
2910
3028
  else {
2911
3029
  ctx.report.error("validation-error", JSON_ISSUE.BAD_VALUE, `${element}: position must be an object with numeric x and y (and z) that are finite numbers`, { element });
@@ -2924,7 +3042,11 @@ function resolveCytoscapeParents(ctx, parents, parentColumn) {
2924
3042
  // the last link per child (a duplicate node's later parent wins), child -> its link
2925
3043
  const linkOf = new Map();
2926
3044
  for (let order = 0; order < parents.length; order++) {
2927
- const { index, parent, element } = parents[order];
3045
+ const { index, element } = parents[order];
3046
+ let { parent } = parents[order];
3047
+ if (ctx.sink.indexOf(parent) === INVALID_INDEX) {
3048
+ parent = ctx.textualId(parent, element) ?? parent;
3049
+ }
2928
3050
  const parentIndex = ctx.sink.indexOf(parent);
2929
3051
  if (parentIndex === INVALID_INDEX) {
2930
3052
  report.error("missing-value", JSON_ISSUE.UNKNOWN_PARENT, `${element}: parent ${JSON.stringify(parent)} is not a node`, { element });
@@ -3015,8 +3137,8 @@ function importCytoscapeEdges(ctx, edges, edgeClassesColumn) {
3015
3137
  function importCytoscapeEdge(ctx, shared, record, data, element) {
3016
3138
  const { ids, kind, edgeClassesColumn } = shared;
3017
3139
  try {
3018
- const u = ctx.coerceId(data.source, `${element}.data.source`);
3019
- const v = ctx.coerceId(data.target, `${element}.data.target`);
3140
+ const u = ctx.textualId(data.source, `${element}.data.source`);
3141
+ const v = ctx.textualId(data.target, `${element}.data.target`);
3020
3142
  if (u === null || v === null) {
3021
3143
  ctx.countSkipped("edge");
3022
3144
  return;
@@ -3072,6 +3194,45 @@ function cytoscapeSections(ctx, elements) {
3072
3194
  }
3073
3195
  return report.fail(JSON_ISSUE.SHAPE, `elements must be an object or an array, found ${describe(elements)}`);
3074
3196
  }
3197
+ /**
3198
+ * The ids Cytoscape.js would give the nodes without a `data.id`: `n<k>` by their place among the nodes, made unique
3199
+ * against every id the file states.
3200
+ * @param nodes - the node elements
3201
+ * @returns the generated id of each node element without one
3202
+ */
3203
+ function cytoscapeGeneratedIds(nodes) {
3204
+ const out = new Map();
3205
+ const missing = nodes.filter((n) => isJsonObject(n) && isJsonObject(n.data) && n.data.id === undefined);
3206
+ if (missing.length === 0) {
3207
+ return out;
3208
+ }
3209
+ const taken = new Set();
3210
+ for (const n of nodes) {
3211
+ const id = isJsonObject(n) && isJsonObject(n.data) ? n.data.id : undefined;
3212
+ if (typeof id === "string" || typeof id === "number") {
3213
+ taken.add(String(id));
3214
+ }
3215
+ }
3216
+ for (let k = 0; k < missing.length; k++) {
3217
+ let id = `n${String(k)}`;
3218
+ let j = 2;
3219
+ while (taken.has(id)) {
3220
+ id = `n${String(k)}_${String(j)}`;
3221
+ j++;
3222
+ }
3223
+ taken.add(id);
3224
+ out.set(missing[k], id);
3225
+ }
3226
+ return out;
3227
+ }
3228
+ /**
3229
+ * A `graphty.styles` block a document holds as a top-level key, or null.
3230
+ * @param root - the document
3231
+ * @returns the block's text
3232
+ */
3233
+ function blockOf(root) {
3234
+ return isJsonObject(root) && typeof root[STYLE_BLOCK_NAME] === "string" ? root[STYLE_BLOCK_NAME] : null;
3235
+ }
3075
3236
  /**
3076
3237
  * Whether a Cytoscape element's position has a numeric z.
3077
3238
  * @param element - the element
@@ -3166,6 +3327,7 @@ export const jsonImporter = Object.freeze({
3166
3327
  "typedefs",
3167
3328
  ]),
3168
3329
  extensions: Object.freeze([".json"]),
3330
+ positions: jsonPositions,
3169
3331
  mimeTypes: Object.freeze(["application/json"]),
3170
3332
  /**
3171
3333
  * Confidence that the head is a JSON graph document: 0 unless it starts with `{` or `[`, 0.5
@@ -3199,7 +3361,7 @@ export const jsonImporter = Object.freeze({
3199
3361
  const report = new ImportReportBuilder("json", resolved.errorLimit);
3200
3362
  const text = await readText(input, report, { ...resolved, bomlessUtf16: true });
3201
3363
  const { root, dialect } = documentOf(parseDocument(text, report), json, report);
3202
- readGraph(root, dialect, sink, report, resolved, json, options);
3364
+ readGraph({ root, dialect, text }, sink, report, resolved, json, options);
3203
3365
  return report.finish();
3204
3366
  },
3205
3367
  /**
@@ -3236,28 +3398,41 @@ export const jsonImporter = Object.freeze({
3236
3398
  const reports = [];
3237
3399
  for (let i = 0; i < Math.max(graphs, 1); i++) {
3238
3400
  const report = i === 0 ? first : new ImportReportBuilder("json", resolved.errorLimit);
3239
- readGraph(root, dialect, sinkFor(i), report, resolved, { ...json, graphIndex: i, all: true }, options);
3401
+ readGraph({ root, dialect, text }, sinkFor(i), report, resolved, { ...json, graphIndex: i, all: true }, options);
3240
3402
  reports.push(report.finish());
3241
3403
  }
3242
3404
  return reports;
3243
3405
  },
3406
+ /**
3407
+ * The document's styles, read by its dialect's reader (Cytoscape.js stylesheets, graphology and vis.js keys,
3408
+ * NetworkX's defaults); a `graphty.styles` block gives back the rules graphty wrote.
3409
+ * @param snapshot - the graph the import built
3410
+ * @param context - the work budget and what the import kept
3411
+ * @returns the styles, or null when the dialect has none
3412
+ */
3413
+ styles: jsonStyles,
3244
3414
  });
3245
3415
  /**
3246
3416
  * Read one graph of a parsed document into a sink.
3247
- * @param root - the parsed document
3248
- * @param dialect - its dialect
3417
+ * @param doc - the parsed document, its dialect and its text
3418
+ * @param doc.root - the parsed document
3419
+ * @param doc.dialect - its dialect
3420
+ * @param doc.text - the document's text
3249
3421
  * @param sink - the sink
3250
3422
  * @param report - the graph's report
3251
3423
  * @param resolved - the resolved common options
3252
3424
  * @param json - the resolved JSON options (graphIndex picks the JGF graph)
3253
3425
  * @param options - the caller's options, for the sink and unused-option checks
3254
3426
  */
3255
- function readGraph(root, dialect, sink, report, resolved, json, options) {
3427
+ function readGraph(doc, sink, report, resolved, json, options) {
3428
+ const { root, dialect, text } = doc;
3256
3429
  const ctx = new ImportContext(sink, report, resolved, json, options?.defaultDirected !== undefined);
3430
+ ctx.text = text;
3257
3431
  // the obographs reader refuses missing endpoints itself, so addMissingNodes false holds on any sink
3258
3432
  reportSinkOptions(sink, options, report, dialect === "obographs");
3259
3433
  reportUnusedOptions(options, report, USED_OPTIONS);
3260
3434
  readDialect(ctx, root, dialect);
3435
+ report.keepForStyles(ctx.styleSource ?? { dialect, block: blockOf(root) });
3261
3436
  ctx.reportDangling();
3262
3437
  ctx.nodes.reportNullOnly();
3263
3438
  ctx.edges.reportNullOnly();