@graphty/graph-io 0.3.42 → 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-CpRyHZ3H.js → exporter-quPUCBiC.js} +1807 -288
  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 +295 -414
  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 +23 -7
  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 +441 -265
  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 +30 -13
  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 +543 -323
  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-CpRyHZ3H.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
  }
@@ -1284,7 +1326,8 @@ function arraySection(value, what, report) {
1284
1326
  if (!Array.isArray(value)) {
1285
1327
  return report.fail(JSON_ISSUE.SHAPE, `${what} must be an array, found ${describe(value)}`, { element: what });
1286
1328
  }
1287
- return value;
1329
+ const items = value;
1330
+ return items;
1288
1331
  }
1289
1332
  /**
1290
1333
  * A boolean flag with a default and a warning when it has another type.
@@ -1351,9 +1394,15 @@ function reportUnreadKeys(ctx, record, known, prefix) {
1351
1394
  }
1352
1395
  }
1353
1396
  /** The keys of a vis.js document the reader reads. */
1354
- const VIS_DOCUMENT_KEYS = new Set(["nodes", "edges"]);
1397
+ const VIS_DOCUMENT_KEYS = new Set(["nodes", "edges", STYLE_BLOCK_NAME]);
1355
1398
  /** The keys of a graphology serialization the reader reads. */
1356
- 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
+ ]);
1357
1406
  /** The keys of a JGF graph object the reader reads. */
1358
1407
  const JGF_GRAPH_KEYS = new Set([
1359
1408
  "id",
@@ -1392,94 +1441,33 @@ function isIndexBelow(value, bound) {
1392
1441
  */
1393
1442
  function importNodeLink(ctx, root, dialect) {
1394
1443
  const { report, json } = ctx;
1395
- let { edgesKey } = json;
1396
- if (edgesKey === null) {
1397
- edgesKey = hasKey(root, "edges") || !hasKey(root, "links") ? "edges" : "links";
1398
- }
1399
- const nodes = arraySection(root.nodes, "nodes", report);
1400
- const edges = arraySection(root[edgesKey], edgesKey, report);
1401
- if (nodes === null) {
1402
- report.warning("missing-value", JSON_ISSUE.MISSING_SECTION, "the document has no nodes array; nodes come from the edges", { element: "nodes" });
1403
- ctx.nodesFromEdges = true;
1404
- }
1405
- if (edges === null) {
1406
- report.warning("missing-value", JSON_ISSUE.MISSING_SECTION, `the document has no ${edgesKey} array; the graph has no edges`, { element: edgesKey });
1407
- }
1444
+ const edgesKey = json.edgesKey ?? (hasKey(root, "edges") || !hasKey(root, "links") ? "edges" : "links");
1445
+ const { nodeList, edgeList } = nodeLinkSections(ctx, root, edgesKey);
1408
1446
  const directed = flagOf(root.directed, "directed", ctx.defaultDirected(dialect), report);
1409
1447
  const multigraph = hasKey(root, "multigraph") ? flagOf(root.multigraph, "multigraph", false, report) : null;
1410
1448
  ctx.setHeader(directed);
1411
1449
  ctx.writeGraphDict(root.graph, "graph");
1412
- reportUnreadKeys(ctx, root, new Set(["nodes", edgesKey, "directed", "multigraph", "graph"]), "");
1413
- const nodeList = nodes ?? [];
1414
- 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 };
1415
1454
  ctx.sink.reserve(nodeList.length, edgeList.length);
1416
- // the id key: the caller's, else "id" when any node has it, else "name" (d3); nodes without any
1417
- // id key are positional (nodeIdFrom "index", or a d3 v3 file whose nodes carry no id at all)
1418
- let { nodeIdKey } = json;
1419
- let positional = ctx.options.nodeIdFrom === "index";
1420
- if (!positional && nodeIdKey === null) {
1421
- const candidates = ctx.options.nodeIdFrom === "label" ? ["label", "name", "id"] : ["id", "name"];
1422
- nodeIdKey = candidates.find((key) => anyHas(nodeList, key)) ?? null;
1423
- if (ctx.options.nodeIdFrom === "label" && (nodeIdKey === "id" || nodeIdKey === "name")) {
1424
- report.warning("unsupported", JSON_ISSUE.OPTION_IGNORED, nodeIdKey === "id"
1425
- ? 'nodeIdFrom "label": no node has a label or name key; ids are read from "id"'
1426
- : 'nodeIdFrom "label": no node has a "label" key; ids are read from "name"', { element: "nodeIdFrom" });
1427
- }
1428
- if (nodeIdKey === null) {
1429
- if (nodeList.length > 0) {
1430
- positional = true;
1431
- report.warning("missing-value", JSON_ISSUE.POSITIONAL_NODES, "no node has an id or name key; array positions are the node ids", { element: "nodes" });
1432
- }
1433
- else {
1434
- nodeIdKey = "id";
1435
- }
1436
- }
1437
- }
1438
- if (positional) {
1439
- nodeIdKey = null;
1440
- }
1455
+ const nodeIdKey = nodeLinkIdKey(ctx, nodeList);
1456
+ const positional = nodeIdKey === null;
1441
1457
  let indexLinks;
1442
1458
  if (positional) {
1443
1459
  indexLinks = true;
1444
1460
  }
1445
1461
  else if (json.indexLinks === "auto") {
1446
- indexLinks = nodeIdKey !== null && looksIndexLinked(nodeList, edgeList, nodeIdKey, json);
1462
+ indexLinks = looksIndexLinked(nodeList, edgeList, nodeIdKey, json);
1447
1463
  }
1448
1464
  else {
1449
1465
  ({ indexLinks } = json);
1450
1466
  }
1451
- const positionIds = indexLinks ? [] : null;
1452
- if (indexLinks && !positional && json.indexLinks === "auto" && nodeIdKey !== null) {
1467
+ if (indexLinks && !positional && json.indexLinks === "auto") {
1453
1468
  reportAmbiguousIndexLinks(ctx, nodeList, edgeList, nodeIdKey);
1454
1469
  }
1455
- for (let i = 0; i < nodeList.length; i++) {
1456
- const element = `nodes[${i}]`;
1457
- const record = nodeList[i];
1458
- let pushed = null;
1459
- if (!isJsonObject(record)) {
1460
- ctx.badElement("node", element);
1461
- }
1462
- else {
1463
- let id;
1464
- if (nodeIdKey === null) {
1465
- id = i;
1466
- }
1467
- else {
1468
- id = ctx.coerceId(hasKey(record, nodeIdKey) ? record[nodeIdKey] : undefined, element);
1469
- }
1470
- if (id === null) {
1471
- ctx.countSkipped("node");
1472
- }
1473
- else {
1474
- const index = ctx.pushNode(id, element);
1475
- if (index >= 0) {
1476
- pushed = id;
1477
- writeFlat(ctx.nodes, index, record, id, (key) => key !== nodeIdKey);
1478
- }
1479
- }
1480
- }
1481
- positionIds?.push(pushed);
1482
- }
1470
+ const positionIds = importNodeLinkNodes(ctx, nodeList, nodeIdKey, indexLinks);
1483
1471
  throwIfAborted(ctx.options.signal);
1484
1472
  const endpointKeys = importNodeLinkEdges(ctx, edgeList, edgesKey, positionIds, multigraph);
1485
1473
  ctx.setMeta({
@@ -1491,6 +1479,103 @@ function importNodeLink(ctx, root, dialect) {
1491
1479
  targetKey: endpointKeys.target ?? undefined,
1492
1480
  }, { declaredMultigraph: multigraph, ...ctx.weightOriginPatch() });
1493
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
+ }
1494
1579
  /**
1495
1580
  * Read the node-link / d3 edge records: endpoints by id or array position, the weight, the other
1496
1581
  * keys as attributes.
@@ -1769,6 +1854,7 @@ function importVis(ctx, root) {
1769
1854
  if (arrows > 0) {
1770
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" });
1771
1856
  }
1857
+ ctx.styleSource = { dialect: "vis", block: blockOf(root), options: root.options, groups: root.groups };
1772
1858
  ctx.setMeta({ dialect: "vis", nodeIdKey, sourceKey: sourceKey ?? undefined, targetKey: targetKey ?? undefined }, ctx.weightOriginPatch());
1773
1859
  }
1774
1860
  // ============================================================ NetworkX adjacency_data / tree_data
@@ -2082,36 +2168,29 @@ function importGraphology(ctx, root) {
2082
2168
  });
2083
2169
  }
2084
2170
  const type = graphologyType(ctx, options);
2085
- ctx.setHeader(graphologyDirected(ctx, type, edges));
2171
+ ctx.setHeader(type === "mixed" ? mostlyDirected(ctx, edges) : type === "directed");
2086
2172
  const multi = hasKey(options, "multi") ? flagOf(options.multi, "options.multi", false, report) : null;
2087
2173
  const allowSelfLoops = hasKey(options, "allowSelfLoops")
2088
2174
  ? flagOf(options.allowSelfLoops, "options.allowSelfLoops", true, report)
2089
2175
  : undefined;
2090
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 };
2091
2182
  ctx.reportNodeIdFrom("graphology", "the node key");
2092
2183
  ctx.sink.reserve(nodes.length, edges.length);
2093
- for (let i = 0; i < nodes.length; i++) {
2094
- pushGraphologyNode(ctx, nodes[i], `nodes[${i}]`);
2095
- }
2184
+ importGraphologyNodes(ctx, nodes, gephi);
2096
2185
  throwIfAborted(ctx.options.signal);
2097
- const reading = {
2098
- ids: ctx.declareEdgeIds(edges, (edge) => edge.key, "key", true),
2099
- type,
2100
- allowSelfLoops,
2101
- // what the edges do against the declared options: graphology itself refuses such a document
2102
- violations: { selfLoops: 0, parallels: 0, flags: 0 },
2103
- pairs: multi === false ? new Set() : null,
2104
- };
2105
- for (let i = 0; i < edges.length; i++) {
2106
- readGraphologyEdge(ctx, reading, edges[i], `edges[${i}]`);
2107
- }
2108
- reportGraphologyViolations(ctx, reading.violations, type);
2186
+ const violations = importGraphologyEdges(ctx, edges, { type, gephi, multi, allowSelfLoops });
2187
+ reportGraphologyViolations(ctx, violations, type);
2109
2188
  ctx.setMeta({ dialect: "graphology", allowSelfLoops }, { declaredMultigraph: multi, ...ctx.weightOriginPatch() });
2110
2189
  }
2111
2190
  /**
2112
- * 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).
2113
2192
  * @param ctx - the context
2114
- * @param options - the options object
2193
+ * @param options - the document's options
2115
2194
  * @returns the type
2116
2195
  */
2117
2196
  function graphologyType(ctx, options) {
@@ -2124,114 +2203,133 @@ function graphologyType(ctx, options) {
2124
2203
  return "mixed";
2125
2204
  }
2126
2205
  /**
2127
- * The header direction of a graphology document: its type, or for "mixed" directed unless every
2128
- * 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.
2129
2207
  * @param ctx - the context
2130
- * @param type - options.type
2131
2208
  * @param edges - the edge records
2132
- * @returns whether the header is directed
2209
+ * @returns true for directed
2133
2210
  */
2134
- function graphologyDirected(ctx, type, edges) {
2135
- if (type !== "mixed") {
2136
- return type === "directed";
2137
- }
2211
+ function mostlyDirected(ctx, edges) {
2138
2212
  const objects = edges.filter((edge) => isJsonObject(edge));
2139
2213
  const undirectedEdges = objects.filter((edge) => edge.undirected === true).length;
2140
2214
  return objects.length === 0 ? ctx.defaultDirected("graphology") : undirectedEdges < objects.length;
2141
2215
  }
2142
2216
  /**
2143
- * Push one graphology node record: `key` the id, `attributes` the columns.
2217
+ * Read the graphology node records.
2144
2218
  * @param ctx - the context
2145
- * @param record - the node record
2146
- * @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)
2147
2221
  */
2148
- function pushGraphologyNode(ctx, record, element) {
2149
- if (!isJsonObject(record)) {
2150
- ctx.badElement("node", element);
2151
- return;
2152
- }
2153
- const id = ctx.coerceId(hasKey(record, "key") ? record.key : undefined, element);
2154
- if (id === null) {
2155
- ctx.countSkipped("node");
2156
- 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);
2157
2237
  }
2158
- pushNestedNode(ctx, id, record, "attributes", GRAPHOLOGY_NODE_KEYS, element);
2159
2238
  }
2160
2239
  /**
2161
- * Read one graphology edge record, counting what it does against the declared options.
2240
+ * Read the graphology edge records.
2162
2241
  * @param ctx - the context
2163
- * @param reading - the read's state
2164
- * @param record - the edge record
2165
- * @param element - the element name
2166
- */
2167
- function readGraphologyEdge(ctx, reading, record, element) {
2168
- if (!isJsonObject(record)) {
2169
- ctx.badElement("edge", element);
2170
- return;
2171
- }
2172
- if (!present(record, "source") || !present(record, "target")) {
2173
- ctx.missingEndpoint(element, present(record, "source") ? "target" : "source");
2174
- return;
2175
- }
2176
- try {
2177
- const u = ctx.coerceId(record.source, `${element}.source`);
2178
- const v = ctx.coerceId(record.target, `${element}.target`);
2179
- if (u === null || v === null) {
2180
- ctx.countSkipped("edge");
2181
- 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;
2182
2256
  }
2183
- const kind = graphologyEdgeKind(ctx, reading, record, element);
2184
- const attributes = graphologyEdgeAttributes(ctx, record, element);
2185
- const idValue = ctx.edgeIdValue(reading.ids, record.key);
2186
- const edge = ctx.pushEdge(u, v, kind, ctx.weightOf(attributes), element);
2187
- ctx.setEdgeId(reading.ids, edge, idValue);
2188
- ctx.writeNested(ctx.edges, edge, record, attributes, GRAPHOLOGY_EDGE_KEYS, ctx.options.weightFrom, element);
2189
- if (reading.allowSelfLoops === false && u === v) {
2190
- 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
+ }
2191
2263
  }
2192
- if (reading.pairs !== null) {
2193
- const pair = `${kind} ${pairKey(u, v, kind === "directed")}`;
2194
- reading.violations.parallels += reading.pairs.has(pair) ? 1 : 0;
2195
- reading.pairs.add(pair);
2264
+ catch (err) {
2265
+ ctx.skip(err, "edge", element);
2196
2266
  }
2197
2267
  }
2198
- catch (err) {
2199
- ctx.skip(err, "edge", element);
2200
- }
2268
+ return violations;
2201
2269
  }
2202
2270
  /**
2203
- * The direction of one graphology edge: its `undirected` flag under type "mixed", else the type
2204
- * (a contrary flag is counted as a violation).
2271
+ * One graphology edge record, when it is an object with both endpoints.
2205
2272
  * @param ctx - the context
2206
- * @param reading - the read's state
2207
2273
  * @param record - the edge record
2208
2274
  * @param element - the element name
2209
- * @returns the edge's kind
2275
+ * @returns the record, or null when it was reported and is skipped
2210
2276
  */
2211
- function graphologyEdgeKind(ctx, reading, record, element) {
2212
- const { type } = reading;
2213
- if (type === "mixed") {
2214
- 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;
2215
2281
  }
2216
- if (typeof record.undirected === "boolean" && record.undirected !== (type === "undirected")) {
2217
- reading.violations.flags++;
2282
+ if (!present(record, "source") || !present(record, "target")) {
2283
+ ctx.missingEndpoint(element, present(record, "source") ? "target" : "source");
2284
+ return null;
2218
2285
  }
2219
- return type;
2286
+ return record;
2220
2287
  }
2221
2288
  /**
2222
- * The `attributes` object of a graphology edge; any other non-null value is reported and read as
2223
- * no attributes.
2289
+ * Add one graphology edge with its id and attributes.
2224
2290
  * @param ctx - the context
2225
2291
  * @param record - the edge record
2226
2292
  * @param element - the element name
2227
- * @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
2228
2297
  */
2229
- function graphologyEdgeAttributes(ctx, record, element) {
2230
- const { attributes } = record;
2231
- if (attributes !== undefined && attributes !== null && !isJsonObject(attributes)) {
2232
- 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";
2233
2310
  }
2234
- 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")}`;
2235
2333
  }
2236
2334
  /**
2237
2335
  * Report the edges that contradict a graphology document's declared options (W_JSON_INCONSISTENT,
@@ -2269,9 +2367,10 @@ function reportGraphologyViolations(ctx, violations, type) {
2269
2367
  * @param dictKey - the key of the nested dict
2270
2368
  * @param structural - the element keys that are not attributes
2271
2369
  * @param element - the element name
2370
+ * @param plain - write the element-level keys under their own names
2272
2371
  * @returns the node index, or -1 when skipped
2273
2372
  */
2274
- function pushNestedNode(ctx, id, record, dictKey, structural, element) {
2373
+ function pushNestedNode(ctx, id, record, dictKey, structural, element, plain = false) {
2275
2374
  const index = ctx.pushNode(id, element);
2276
2375
  if (index < 0) {
2277
2376
  return index;
@@ -2281,7 +2380,7 @@ function pushNestedNode(ctx, id, record, dictKey, structural, element) {
2281
2380
  if (dict !== undefined && dict !== null && !isJsonObject(dict)) {
2282
2381
  ctx.report.error("validation-error", JSON_ISSUE.BAD_VALUE, `${element}: ${dictKey} must be an object, found ${describe(dict)}`, { element });
2283
2382
  }
2284
- 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 });
2285
2384
  }
2286
2385
  catch (err) {
2287
2386
  ctx.report.recordError(err, { element: String(id) });
@@ -2319,7 +2418,7 @@ function importJgf(ctx, root) {
2319
2418
  ctx.sink.reserve(nodeRecords.length, edges.length);
2320
2419
  const labelColumn = ctx.nodes.declareIf({ name: "label", dtype: "string", role: "label", nullable: true }, anyHas(nodeRecords, "label"));
2321
2420
  if (isJsonObject(nodesRaw)) {
2322
- for (const key of Object.keys(nodesRaw)) {
2421
+ for (const key of jgfNodeKeys(ctx, root, graph, nodesRaw)) {
2323
2422
  pushJgfKeyedNode(ctx, key, nodesRaw[key], labelColumn);
2324
2423
  }
2325
2424
  }
@@ -2425,7 +2524,7 @@ function jgfEdgeWriter(ctx, edges, hyperedges) {
2425
2524
  element,
2426
2525
  });
2427
2526
  }
2428
- 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 });
2429
2528
  };
2430
2529
  }
2431
2530
  /**
@@ -2459,6 +2558,25 @@ function readJgfEdge(ctx, record, element, directed, writeJgfEdge) {
2459
2558
  ctx.skip(err, "edge", element);
2460
2559
  }
2461
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
+ }
2462
2580
  /**
2463
2581
  * The graph object of a JGF document: `graph`, or the graph of `graphs` that graphIndex /
2464
2582
  * graphName choose (chooseGraph()).
@@ -2612,7 +2730,7 @@ function pushJgfNode(ctx, id, record, labelColumn, element) {
2612
2730
  element,
2613
2731
  });
2614
2732
  }
2615
- 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);
2616
2734
  }
2617
2735
  catch (err) {
2618
2736
  ctx.report.recordError(err, { element: String(id) });
@@ -2767,42 +2885,44 @@ function distinctMembers(ctx, members, element) {
2767
2885
  function importCytoscape(ctx, root) {
2768
2886
  const { elements, extra } = cytoscapeRoot(ctx, root);
2769
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
+ };
2770
2897
  ctx.setHeader(ctx.defaultDirected("cytoscape"));
2771
2898
  ctx.reportNodeIdFrom("cytoscape", "data.id");
2772
2899
  ctx.sink.reserve(nodes.length, edges.length);
2773
- const dataHas = (items, key) => items.some((item) => isJsonObject(item) && isJsonObject(item.data) && item.data[key] !== undefined);
2774
- const reading = {
2775
- positionColumn: ctx.nodes.declareIf({
2776
- name: POSITION_COLUMN,
2777
- dtype: "f64",
2778
- components: 3,
2779
- role: "position",
2780
- mutable: true,
2781
- nullable: true,
2782
- // 3D layouts written by Cytoscape-compatible tools carry a z
2783
- extra: { sourceDims: nodes.some(hasPositionZ) ? 3 : 2, units: "file" },
2784
- origin: { format: "json", namespace: "cytoscape" },
2785
- }, anyHas(nodes, "position")),
2786
- classesColumn: ctx.nodes.declareIf({ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true }, anyHas(nodes, "classes")),
2787
- parents: [],
2788
- point: [0, 0, 0],
2789
- };
2790
- 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);
2791
2901
  const edgeClassesColumn = ctx.edges.declareIf({ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true }, anyHas(edges, "classes"));
2902
+ const parents = [];
2792
2903
  for (let i = 0; i < nodes.length; i++) {
2793
- 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
+ }
2794
2914
  }
2795
- resolveCytoscapeParents(ctx, reading.parents, parentColumn);
2915
+ resolveCytoscapeParents(ctx, parents, columns.parent);
2796
2916
  throwIfAborted(ctx.options.signal);
2797
2917
  importCytoscapeEdges(ctx, edges, edgeClassesColumn);
2798
2918
  ctx.setMeta({ dialect: "cytoscape", cytoscape: extra }, ctx.weightOriginPatch());
2799
2919
  }
2800
2920
  /**
2801
- * The elements of a Cytoscape document (the document itself when it is an array) and, for an
2802
- * 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.
2803
2923
  * @param ctx - the context
2804
- * @param root - the document
2805
- * @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)
2806
2926
  */
2807
2927
  function cytoscapeRoot(ctx, root) {
2808
2928
  if (Array.isArray(root)) {
@@ -2811,31 +2931,53 @@ function cytoscapeRoot(ctx, root) {
2811
2931
  if (!isJsonObject(root)) {
2812
2932
  ctx.report.fail(JSON_ISSUE.SHAPE, `a Cytoscape document must be an object or an array, found ${describe(root)}`);
2813
2933
  }
2814
- ctx.writeGraphDict(root.data, "data");
2934
+ const object = root;
2935
+ ctx.writeGraphDict(object.data, "data");
2815
2936
  const rest = {};
2816
- for (const key of Object.keys(root)) {
2817
- if (key !== "elements" && key !== "data") {
2818
- 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];
2819
2940
  }
2820
2941
  }
2821
- 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 };
2822
2943
  }
2823
2944
  /**
2824
- * Read one Cytoscape node element: `data.id` the id, the other `data` keys attributes (`parent`
2825
- * 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.
2826
2970
  * @param ctx - the context
2827
- * @param reading - the read's state
2828
2971
  * @param record - the element
2972
+ * @param data - its data object
2973
+ * @param raw - its raw id
2829
2974
  * @param element - the element name
2975
+ * @param columns - the node columns
2976
+ * @param parents - receives its parent link
2830
2977
  */
2831
- function readCytoscapeNode(ctx, reading, record, element) {
2978
+ function importCytoscapeNode(ctx, record, data, raw, element, columns, parents) {
2832
2979
  const { report } = ctx;
2833
- if (!isJsonObject(record) || !isJsonObject(record.data)) {
2834
- ctx.badElement("node", element, "an element with a data object");
2835
- return;
2836
- }
2837
- const { data } = record;
2838
- const id = ctx.coerceId(hasKey(data, "id") ? data.id : undefined, element);
2980
+ const id = ctx.coerceId(raw, element);
2839
2981
  if (id === null) {
2840
2982
  ctx.countSkipped("node");
2841
2983
  return;
@@ -2848,9 +2990,18 @@ function readCytoscapeNode(ctx, reading, record, element) {
2848
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 });
2849
2991
  }
2850
2992
  try {
2851
- writeCytoscapeData(ctx, reading, index, data, element);
2852
- writeCytoscapePosition(ctx, reading, index, record.position, element);
2853
- 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);
2854
3005
  writeElementKeys(ctx.nodes, index, record, element);
2855
3006
  }
2856
3007
  catch (err) {
@@ -2858,53 +3009,21 @@ function readCytoscapeNode(ctx, reading, record, element) {
2858
3009
  }
2859
3010
  }
2860
3011
  /**
2861
- * Write a Cytoscape node's `data` keys as attributes; `parent` is kept as a link to resolve later.
2862
- * @param ctx - the context
2863
- * @param reading - the read's state
2864
- * @param index - the node index
2865
- * @param data - the element's data object
2866
- * @param element - the element name
2867
- */
2868
- function writeCytoscapeData(ctx, reading, index, data, element) {
2869
- for (const key of Object.keys(data)) {
2870
- if (key === "id") {
2871
- continue;
2872
- }
2873
- if (key !== "parent") {
2874
- ctx.nodes.write(index, key, data[key], SUFFIX.data, element);
2875
- continue;
2876
- }
2877
- const raw = data.parent;
2878
- if (raw === undefined || raw === null) {
2879
- continue;
2880
- }
2881
- const parent = ctx.coerceId(raw, `${element}.data.parent`);
2882
- if (parent !== null) {
2883
- reading.parents.push({ index, parent, element });
2884
- }
2885
- }
2886
- }
2887
- /**
2888
- * Write a Cytoscape node's `position` ({ x, y, z? }, y flipped to grow upward); any other value is
2889
- * 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).
2890
3014
  * @param ctx - the context
2891
- * @param reading - the read's state
2892
- * @param index - the node index
3015
+ * @param column - the position column
3016
+ * @param index - the node
2893
3017
  * @param position - the element's position
2894
3018
  * @param element - the element name
2895
3019
  */
2896
- function writeCytoscapePosition(ctx, reading, index, position, element) {
3020
+ function writeCytoscapePosition(ctx, column, index, position, element) {
2897
3021
  if (position === undefined || position === null) {
2898
3022
  return;
2899
3023
  }
2900
3024
  const z = isJsonObject(position) ? (position.z ?? 0) : 0;
2901
3025
  if (isJsonObject(position) && isCoordinate(position.x) && isCoordinate(position.y) && isCoordinate(z)) {
2902
- const { point } = reading;
2903
- point[0] = position.x;
2904
- // Cytoscape's y grows downward; positions are stored y-up (the exporter flips back)
2905
- point[1] = flipY(position.y);
2906
- point[2] = z;
2907
- ctx.nodes.set(reading.positionColumn, index, point);
3026
+ ctx.nodes.set(column, index, [position.x, flipY(position.y), z]);
2908
3027
  }
2909
3028
  else {
2910
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 });
@@ -2923,7 +3042,11 @@ function resolveCytoscapeParents(ctx, parents, parentColumn) {
2923
3042
  // the last link per child (a duplicate node's later parent wins), child -> its link
2924
3043
  const linkOf = new Map();
2925
3044
  for (let order = 0; order < parents.length; order++) {
2926
- 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
+ }
2927
3050
  const parentIndex = ctx.sink.indexOf(parent);
2928
3051
  if (parentIndex === INVALID_INDEX) {
2929
3052
  report.error("missing-value", JSON_ISSUE.UNKNOWN_PARENT, `${element}: parent ${JSON.stringify(parent)} is not a node`, { element });
@@ -3014,8 +3137,8 @@ function importCytoscapeEdges(ctx, edges, edgeClassesColumn) {
3014
3137
  function importCytoscapeEdge(ctx, shared, record, data, element) {
3015
3138
  const { ids, kind, edgeClassesColumn } = shared;
3016
3139
  try {
3017
- const u = ctx.coerceId(data.source, `${element}.data.source`);
3018
- 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`);
3019
3142
  if (u === null || v === null) {
3020
3143
  ctx.countSkipped("edge");
3021
3144
  return;
@@ -3071,6 +3194,45 @@ function cytoscapeSections(ctx, elements) {
3071
3194
  }
3072
3195
  return report.fail(JSON_ISSUE.SHAPE, `elements must be an object or an array, found ${describe(elements)}`);
3073
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
+ }
3074
3236
  /**
3075
3237
  * Whether a Cytoscape element's position has a numeric z.
3076
3238
  * @param element - the element
@@ -3165,6 +3327,7 @@ export const jsonImporter = Object.freeze({
3165
3327
  "typedefs",
3166
3328
  ]),
3167
3329
  extensions: Object.freeze([".json"]),
3330
+ positions: jsonPositions,
3168
3331
  mimeTypes: Object.freeze(["application/json"]),
3169
3332
  /**
3170
3333
  * Confidence that the head is a JSON graph document: 0 unless it starts with `{` or `[`, 0.5
@@ -3198,7 +3361,7 @@ export const jsonImporter = Object.freeze({
3198
3361
  const report = new ImportReportBuilder("json", resolved.errorLimit);
3199
3362
  const text = await readText(input, report, { ...resolved, bomlessUtf16: true });
3200
3363
  const { root, dialect } = documentOf(parseDocument(text, report), json, report);
3201
- readGraph(root, dialect, sink, report, resolved, json, options);
3364
+ readGraph({ root, dialect, text }, sink, report, resolved, json, options);
3202
3365
  return report.finish();
3203
3366
  },
3204
3367
  /**
@@ -3235,28 +3398,41 @@ export const jsonImporter = Object.freeze({
3235
3398
  const reports = [];
3236
3399
  for (let i = 0; i < Math.max(graphs, 1); i++) {
3237
3400
  const report = i === 0 ? first : new ImportReportBuilder("json", resolved.errorLimit);
3238
- 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);
3239
3402
  reports.push(report.finish());
3240
3403
  }
3241
3404
  return reports;
3242
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,
3243
3414
  });
3244
3415
  /**
3245
3416
  * Read one graph of a parsed document into a sink.
3246
- * @param root - the parsed document
3247
- * @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
3248
3421
  * @param sink - the sink
3249
3422
  * @param report - the graph's report
3250
3423
  * @param resolved - the resolved common options
3251
3424
  * @param json - the resolved JSON options (graphIndex picks the JGF graph)
3252
3425
  * @param options - the caller's options, for the sink and unused-option checks
3253
3426
  */
3254
- function readGraph(root, dialect, sink, report, resolved, json, options) {
3427
+ function readGraph(doc, sink, report, resolved, json, options) {
3428
+ const { root, dialect, text } = doc;
3255
3429
  const ctx = new ImportContext(sink, report, resolved, json, options?.defaultDirected !== undefined);
3430
+ ctx.text = text;
3256
3431
  // the obographs reader refuses missing endpoints itself, so addMissingNodes false holds on any sink
3257
3432
  reportSinkOptions(sink, options, report, dialect === "obographs");
3258
3433
  reportUnusedOptions(options, report, USED_OPTIONS);
3259
3434
  readDialect(ctx, root, dialect);
3435
+ report.keepForStyles(ctx.styleSource ?? { dialect, block: blockOf(root) });
3260
3436
  ctx.reportDangling();
3261
3437
  ctx.nodes.reportNullOnly();
3262
3438
  ctx.edges.reportNullOnly();