@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
@@ -0,0 +1,4310 @@
1
+ import { p as plural, V as agree, a8 as SYNTAX_CODE, ad as formatInteger, aP as formatGmlReal, R as ROLE_TAKEN_CODE$1, a7 as WIDENED_CODE$1, P as PRECISION_CODE$1, j as DUPLICATE_NODE_CODE$1, m as MISSING_ENDPOINT_CODE$1, n as MISSING_ID_CODE$1, h as MULTIPLE_GRAPHS_CODE, N as NO_GRAPH_CODE$1, Z as ImportReportBuilder, C as COLUMN_RENAMED_CODE, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, a as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, I as ID_MERGED_CODE, a2 as AMBIGUOUS_GRAPH_NAME_CODE, a3 as GRAPH_NOT_FOUND_CODE, r as UNKNOWN_ENCODING_CODE, s as ENCODING_FALLBACK_CODE, t as INVALID_ENCODING_CODE, u as INVALID_UTF8_CODE, x as ELEMENT_ISSUE, y as INPUT_ISSUE } from "./format-GDmO6m6Q.js";
2
+ import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
3
+ import { c as capabilities, a as checkCapabilities, L as LOSS, p as pairFolding, G as sanitizeIds, ai as countMixedEdges, e as explicitWeights, r as resolveExportOptions, D as DictHeuristic, ag as DICT_SAMPLE_ROWS, g as resolveImportOptions, i as reportUnusedOptions, ab as readText, h as throwIfAborted, j as reportSinkOptions, k as graphChosen, I as IdCoercer, l as DirectionResolver, u as uniqueColumnName, w as weightFromValue, al as reportWeightPrecision, s as parseWeightText, d as declareResolved } from "./export-D7IGzTG4.js";
4
+ import { o as decodeGmlString, p as quoteGmlString } from "./escape-B1rMftd9.js";
5
+ import { j as joinText, e as encodeChunks } from "./writer-DxjgHwSe.js";
6
+ import { d as yedValue, f as yedColor, h as yedSpelled, j as bendsToPoints, Y as YED_SHAPES, k as YED_LINES, v as valueColumns, m as canonBends, n as flowchartShape, F as FONT_STYLES, o as YED_ARROWS } from "./styles-yed-DAfcIkf7.js";
7
+ import { k as styleFingerprint, a as STYLE_ISSUE, R as styleTag, x as writeStyleBlock, y as styleBlockLosses, r as resolveStyles, S as STYLE_BLOCK_NAME, O as canonicalFor, U as degreeValues, Q as dataCell, m as plainCell, j as StyleNotes, p as parseStyleBlock, l as blockStyles, h as canonText, b as canonNumber, f as canonEnum, n as noteAt, M as MAX_STYLE_VALUE_LENGTH, c as canonColor, o as colorOpacity, s as scalarText } from "./block-JQiwViEt.js";
8
+ import { C as CYTOSCAPE_LEXICON } from "./lexicon-B-knUi-l.js";
9
+ import { S as StyleApproximations } from "./approximations-3Q4TsdTh.js";
10
+ import { r as gephiRules } from "./styles-Brt9JMfs.js";
11
+ import { i as importChosenGraph } from "./choose-BemygfH8.js";
12
+ import { t as toolOf } from "./units-XMZFIFe1.js";
13
+ import { m as median } from "./convert-BcQ8_jMo.js";
14
+ const STYLE_ROLES = /* @__PURE__ */ new Set(["color", "size", "shape", "thickness", "hidden"]);
15
+ const TARGETS = ["node", "edge"];
16
+ function prepareStyleExport(snapshot, exporter, given, plain) {
17
+ const fp = styleFingerprint(snapshot);
18
+ const want = given.fingerprint;
19
+ if (fp.nodes !== want.nodes || fp.edges !== want.edges || fp.ids !== want.ids) {
20
+ return {
21
+ snapshot,
22
+ input: null,
23
+ notes: [],
24
+ refusal: Object.freeze({
25
+ code: STYLE_ISSUE.MISMATCH,
26
+ message: `the styles were read from or built for a different graph (${String(want.nodes)} nodes and ${String(want.edges)} edges, this graph has ${String(fp.nodes)} and ${String(fp.edges)}, or other node ids); nothing is written`,
27
+ column: null,
28
+ count: null
29
+ })
30
+ };
31
+ }
32
+ const caps = exporter.styles?.capabilities ?? null;
33
+ let plan = writePlan(snapshot, exporter, given);
34
+ if (keepsStyleBlock(exporter)) {
35
+ let checked;
36
+ try {
37
+ checked = exporter.check(plan.snapshot, plan.input === null ? plain : { ...plain, styles: plan.input });
38
+ } catch {
39
+ checked = [];
40
+ }
41
+ const carried = withLostInputsCarried(given, snapshot, checked);
42
+ if (carried !== given) {
43
+ plan = writePlan(snapshot, exporter, carried);
44
+ }
45
+ }
46
+ return {
47
+ snapshot: plan.snapshot,
48
+ input: plan.input,
49
+ notes: lossNotes(plan.extracted, exporter, caps, plan.picture, snapshot),
50
+ refusal: null
51
+ };
52
+ }
53
+ function writePlan(snapshot, exporter, extracted) {
54
+ const caps = exporter.styles?.capabilities ?? null;
55
+ const pictures = /* @__PURE__ */ new Map();
56
+ const picture = (target, property) => {
57
+ const key = `${target}\0${property}`;
58
+ let values = pictures.get(key);
59
+ if (values === void 0) {
60
+ values = resolveStyles(extracted, target, property, snapshot);
61
+ pictures.set(key, values);
62
+ }
63
+ return values;
64
+ };
65
+ const tags = { node: /* @__PURE__ */ new Map(), edge: /* @__PURE__ */ new Map() };
66
+ for (const target of TARGETS) {
67
+ for (const [key, col] of extracted.columns[target]) {
68
+ tags[target].set(styleTag(target, key), col);
69
+ }
70
+ }
71
+ const block = writeStyleBlock(extracted, { snapshot });
72
+ return {
73
+ snapshot: styledSnapshot(snapshot, caps, tags, block, extracted.styles.rules),
74
+ input: exporter.styles === void 0 ? null : { picture, block, tags, rules: extracted },
75
+ extracted,
76
+ picture
77
+ };
78
+ }
79
+ const VALUES_KEPT = /NODE_TABLE|NAME_CHANGED|ROLE_ASSUMED|STORAGE_CLASS|NUMBER_TYPE|INTEGRAL|WIDENED/;
80
+ function withLostInputsCarried(extracted, snapshot, notes) {
81
+ const lost = /* @__PURE__ */ new Set();
82
+ let direction = false;
83
+ for (const note2 of notes) {
84
+ if (!VALUES_KEPT.test(note2.code)) {
85
+ for (const name of note2.columns ?? (note2.column === null ? [] : [note2.column])) {
86
+ lost.add(name);
87
+ }
88
+ }
89
+ direction ||= /DIRECTION/.test(note2.code);
90
+ }
91
+ const carrier = new LostInputs(snapshot, lost, direction, extracted.columns);
92
+ const rules = extracted.styles.rules.map((rule) => carrier.carry(rule));
93
+ const changed = rules.some((rule, i) => rule !== extracted.styles.rules[i]);
94
+ return changed ? { ...extracted, styles: { ...extracted.styles, rules }, columns: carrier.columns } : extracted;
95
+ }
96
+ class LostInputs {
97
+ /**
98
+ * A carrier for one export.
99
+ * @param snapshot - the graph
100
+ * @param lost - the data columns the export drops or changes
101
+ * @param direction - whether the file loses the edges' direction (and so the in and out degrees)
102
+ * @param columns - the styles' own style columns
103
+ * @param columns.node - the node columns
104
+ * @param columns.edge - the edge columns
105
+ */
106
+ constructor(snapshot, lost, direction, columns) {
107
+ this.snapshot = snapshot;
108
+ this.lost = lost;
109
+ this.direction = direction;
110
+ this.touched = false;
111
+ this.columns = { node: new Map(columns.node), edge: new Map(columns.edge) };
112
+ }
113
+ /**
114
+ * A rule with every lost input it reads replaced by a copy of its values.
115
+ * @param rule - the rule
116
+ * @returns the rule read from copies, or the rule itself when it reads no lost input
117
+ */
118
+ carry(rule) {
119
+ this.touched = false;
120
+ const { ext, ...given } = rule;
121
+ const map = this.carryMap(rule);
122
+ const where = this.walk(rule, given.where);
123
+ if (!this.touched) {
124
+ return rule;
125
+ }
126
+ const { map: _given, ...unmapped } = given;
127
+ const plain = Object.keys(map).length === 0 ? { ...unmapped, where } : { ...unmapped, where, map };
128
+ const moved = JSON.parse(JSON.stringify(plain));
129
+ return ext === void 0 ? moved : { ...moved, ext };
130
+ }
131
+ /**
132
+ * A rule's mappings with their lost inputs replaced. A passthrough reads a data column as the property's value: its
133
+ * copy holds those values; one that gives no element a value (a list, text that is not a color) is left out, so
134
+ * no other value the file reads back in the column (a list written as text) is given either.
135
+ * @param rule - the rule
136
+ * @returns the mappings
137
+ */
138
+ carryMap(rule) {
139
+ const map = /* @__PURE__ */ Object.create(null);
140
+ const { table } = this.tableOf(rule.target);
141
+ for (const [property, mapping] of Object.entries(rule.map ?? {})) {
142
+ if (mapping === void 0) {
143
+ continue;
144
+ }
145
+ if (mapping.kind !== "passthrough") {
146
+ map[property] = this.walk(rule, mapping);
147
+ continue;
148
+ }
149
+ const name = mapping.by.data;
150
+ const lostColumn = typeof name === "string" && this.lost.has(name) && table.has(name);
151
+ const values = lostColumn ? this.cells(rule.target, name, canonicalFor(rule.target, property)) : null;
152
+ if (values !== null && !values.some((v) => v !== null)) {
153
+ this.touched = true;
154
+ } else if (values !== null && this.isLost(rule.target, name)) {
155
+ this.touched = true;
156
+ map[property] = { ...mapping, by: this.copy(rule.target, `${property}.data.${name}`, () => values) };
157
+ } else {
158
+ map[property] = mapping;
159
+ }
160
+ }
161
+ return map;
162
+ }
163
+ /**
164
+ * A value with every other reference to a lost input reading a copy of its values as they are.
165
+ * @param rule - the rule it belongs to
166
+ * @param value - the value
167
+ * @returns the value, references replaced
168
+ */
169
+ walk(rule, value) {
170
+ return JSON.parse(JSON.stringify(value, (_k, v) => this.replaced(rule, v)));
171
+ }
172
+ /**
173
+ * One reference with a lost input replaced by its copy: a data column, or a degree the file does not keep.
174
+ * @param rule - the rule it belongs to
175
+ * @param v - a value of the rule
176
+ * @returns the value, or the reference to the copy
177
+ */
178
+ replaced(rule, v) {
179
+ if (typeof v !== "object" || v === null || Array.isArray(v) || Object.keys(v).length !== 1) {
180
+ return v;
181
+ }
182
+ const ref = v;
183
+ if (this.isLost(rule.target, ref.data)) {
184
+ const name = ref.data;
185
+ this.touched = true;
186
+ return this.copy(
187
+ rule.target,
188
+ `data.${name}`,
189
+ () => this.cells(rule.target, name, (raw) => isStyleScalar(raw) ? raw : null)
190
+ );
191
+ }
192
+ const which = ref.computed;
193
+ if ((which === "indegree" || which === "outdegree") && this.direction && rule.target === "node") {
194
+ this.touched = true;
195
+ return this.copy(rule.target, `computed.${which}`, () => degreeValues(this.snapshot, which));
196
+ }
197
+ return v;
198
+ }
199
+ /**
200
+ * The reference to a copy of an input, made on first use and shared by every rule that reads it.
201
+ * @param target - nodes or edges
202
+ * @param base - what is copied
203
+ * @param values - its values
204
+ * @returns the reference
205
+ */
206
+ copy(target, base, values) {
207
+ const key = `input.${base}`;
208
+ const held = this.columns[target];
209
+ if (!held.has(key)) {
210
+ held.set(key, values());
211
+ }
212
+ return { style: key };
213
+ }
214
+ /**
215
+ * Whether a name is a lost input that can be copied: only a column of plain values can, and a list, or a column with
216
+ * no value, stays as it is.
217
+ * @param target - nodes or edges
218
+ * @param name - the name
219
+ * @returns true for a lost input
220
+ */
221
+ isLost(target, name) {
222
+ const { table, n } = this.tableOf(target);
223
+ return typeof name === "string" && this.lost.has(name) && table.has(name) && carriable(table, n, name);
224
+ }
225
+ /**
226
+ * Every element's value of a data column.
227
+ * @param target - nodes or edges
228
+ * @param name - the column
229
+ * @param read - the value of a cell
230
+ * @returns the values
231
+ */
232
+ cells(target, name, read) {
233
+ const { table, n } = this.tableOf(target);
234
+ return Array.from({ length: n }, (_u, i) => read(dataCell(table, name, i)));
235
+ }
236
+ /**
237
+ * A target's table and row count.
238
+ * @param target - nodes or edges
239
+ * @returns the table and its row count
240
+ */
241
+ tableOf(target) {
242
+ const { snapshot } = this;
243
+ return target === "node" ? { table: snapshot.nodes, n: snapshot.nodeCount } : { table: snapshot.edges, n: snapshot.edgeCount };
244
+ }
245
+ }
246
+ function carriable(table, n, name) {
247
+ let some = false;
248
+ for (let i = 0; i < n; i++) {
249
+ const cell = plainCell(table.value(name, i));
250
+ if (cell !== null && cell !== void 0) {
251
+ if (!isStyleScalar(cell)) {
252
+ return false;
253
+ }
254
+ some = true;
255
+ }
256
+ }
257
+ return some;
258
+ }
259
+ function isStyleScalar(v) {
260
+ return typeof v === "string" || typeof v === "boolean" || typeof v === "number" && Number.isFinite(v);
261
+ }
262
+ function keepsStyleBlock(exporter) {
263
+ const caps = exporter.styles?.capabilities;
264
+ return caps !== void 0 && (caps.block === "native" || caps.block === "attributes" && exporter.capabilities.graphAttributes);
265
+ }
266
+ function isKeptGraphtyProperty(note2, exporter) {
267
+ return note2.style?.property.startsWith("graphty:") === true && keepsStyleBlock(exporter);
268
+ }
269
+ function lossNotes(extracted, exporter, caps, picture, snapshot) {
270
+ const notes = [];
271
+ const kept = keepsStyleBlock(exporter);
272
+ for (const target of TARGETS) {
273
+ const written = new Set(caps?.[target] ?? []);
274
+ for (const property of ruleProperties(extracted.styles.rules, target)) {
275
+ if (written.has(property) || kept && property.startsWith("graphty:")) {
276
+ if (target === "edge") {
277
+ notes.push(...pairNotes(snapshot, exporter, property, picture("edge", property)));
278
+ }
279
+ } else {
280
+ notes.push(notWrittenNote(target, property, exporter, picture));
281
+ }
282
+ }
283
+ }
284
+ notes.push(...graphLossNotes(extracted, exporter, caps));
285
+ if (kept) {
286
+ notes.push(...styleBlockLosses(writeStyleBlock(extracted, { snapshot }), extracted.styles.rules.length));
287
+ } else if (extracted.styles.rules.length > 0) {
288
+ notes.push(
289
+ Object.freeze({
290
+ code: STYLE_ISSUE.RULES_NOT_KEPT,
291
+ message: `the style rules are not kept: the ${exporter.format} format has no place for them; importing the file infers them again from what it holds`,
292
+ column: null,
293
+ count: extracted.styles.rules.length
294
+ })
295
+ );
296
+ }
297
+ return notes;
298
+ }
299
+ function graphLossNotes(extracted, exporter, caps) {
300
+ const graphWritten = new Set(caps?.graph ?? []);
301
+ return Object.keys(extracted.styles.graph).filter((property) => !graphWritten.has(property)).map(
302
+ (property) => Object.freeze({
303
+ code: STYLE_ISSUE.NOT_WRITTEN,
304
+ message: `the graph's ${property} is not written: the ${exporter.format} format has no place for it`,
305
+ column: null,
306
+ count: 1,
307
+ style: Object.freeze({ target: "graph", property })
308
+ })
309
+ );
310
+ }
311
+ function ruleProperties(rules, target) {
312
+ return new Set(
313
+ rules.filter((rule) => rule.target === target).flatMap((rule) => [...Object.keys(rule.set ?? {}), ...Object.keys(rule.map ?? {})])
314
+ );
315
+ }
316
+ function notWrittenNote(target, property, exporter, picture) {
317
+ const count = picture(target, property).filter((v) => v !== null).length;
318
+ return Object.freeze({
319
+ code: STYLE_ISSUE.NOT_WRITTEN,
320
+ message: `${target} ${property} is not written: the ${exporter.format} format has no place for it (${String(count)} ${target}${plural(count)} ${agree(count, "has", "have")} a value)`,
321
+ column: null,
322
+ count,
323
+ style: Object.freeze({ target, property })
324
+ });
325
+ }
326
+ function pairNotes(snapshot, exporter, property, values) {
327
+ const pair = snapshot.edges.byRole("pair");
328
+ let count = 0;
329
+ for (let i = 0; pair !== null && i < values.length; i++) {
330
+ const other = pair.isSet(i) ? pair.value(i) : i;
331
+ if (other < i && values[i] !== values[other]) {
332
+ count++;
333
+ }
334
+ }
335
+ if (count === 0) {
336
+ return [];
337
+ }
338
+ return [
339
+ Object.freeze({
340
+ code: STYLE_ISSUE.APPROXIMATED,
341
+ message: `edge ${property}: ${String(count)} undirected edge${plural(count)} in a directed graph ${agree(count, "has", "have")} another value on each half; the ${exporter.format} format writes the edge once, with the first half's value`,
342
+ column: null,
343
+ count,
344
+ style: Object.freeze({ target: "edge", property })
345
+ })
346
+ ];
347
+ }
348
+ function styledSnapshot(snapshot, caps, tags, block, rules) {
349
+ const attributes = caps?.block === "attributes";
350
+ const stale = { node: staleTags(snapshot.nodes, rules, "node"), edge: staleTags(snapshot.edges, rules, "edge") };
351
+ const roles = { node: roledNames(snapshot.nodes, caps, "node"), edge: roledNames(snapshot.edges, caps, "edge") };
352
+ const staleBlock = snapshot.graph.has(STYLE_BLOCK_NAME);
353
+ const changes = [stale.node, stale.edge, roles.node, roles.edge].some((names) => names.length > 0);
354
+ if (!attributes && !staleBlock && !changes) {
355
+ return snapshot;
356
+ }
357
+ const out = snapshot.withColumns();
358
+ if (staleBlock) {
359
+ out.graph.remove(STYLE_BLOCK_NAME);
360
+ }
361
+ for (const target of TARGETS) {
362
+ const table = target === "node" ? out.nodes : out.edges;
363
+ for (const name of stale[target]) {
364
+ table.remove(name);
365
+ }
366
+ for (const name of roles[target]) {
367
+ const col = table.get(name);
368
+ if (col !== null) {
369
+ table.set(name, col, { role: void 0 });
370
+ }
371
+ }
372
+ }
373
+ if (attributes) {
374
+ setTagAttributes(out, tags);
375
+ out.graph.set(STYLE_BLOCK_NAME, [block], { dtype: "string" });
376
+ }
377
+ return out;
378
+ }
379
+ function staleTags(table, rules, target) {
380
+ const read = dataRead(rules, target);
381
+ return table.names().filter((name) => isStyleTagName(name) && !read.has(name));
382
+ }
383
+ function roledNames(table, caps, target) {
384
+ const writesLabel = (caps?.[target] ?? []).includes("label");
385
+ return table.names().filter((name) => {
386
+ const role = table.get(name)?.meta.role ?? "";
387
+ return STYLE_ROLES.has(role) || role === "label" && writesLabel;
388
+ });
389
+ }
390
+ function setTagAttributes(out, tags) {
391
+ for (const target of TARGETS) {
392
+ const table = target === "node" ? out.nodes : out.edges;
393
+ const prefix = styleTag(target, "");
394
+ for (const name of table.names().filter((c) => c.startsWith(prefix))) {
395
+ table.remove(name);
396
+ }
397
+ for (const [name, col] of tags[target]) {
398
+ const dtype = tagDtype(col);
399
+ table.set(name, dtype === "string" ? col.map((v) => v === null ? null : String(v)) : col, { dtype });
400
+ }
401
+ }
402
+ }
403
+ function isStyleTagName(name) {
404
+ return name.startsWith("graphty.style.") || name.startsWith("graphty_style_");
405
+ }
406
+ function dataRead(rules, target) {
407
+ const out = /* @__PURE__ */ new Set();
408
+ for (const { ext: _ext, ...rule } of rules.filter((r) => r.target === target)) {
409
+ JSON.stringify(rule, (_k, v) => {
410
+ const data = typeof v === "object" && v !== null && !Array.isArray(v) ? v.data : null;
411
+ if (typeof data === "string" && Object.keys(v).length === 1) {
412
+ out.add(data);
413
+ }
414
+ return v;
415
+ });
416
+ }
417
+ return out;
418
+ }
419
+ function tagDtype(col) {
420
+ let kind = null;
421
+ for (const v of col) {
422
+ if (v === null) {
423
+ continue;
424
+ }
425
+ if (kind === null) {
426
+ kind = typeof v;
427
+ } else if (kind !== typeof v) {
428
+ return "string";
429
+ }
430
+ }
431
+ if (kind === "number") {
432
+ return "f64";
433
+ }
434
+ return kind === "boolean" ? "bool" : "string";
435
+ }
436
+ const TOKEN_WORD = 0;
437
+ const TOKEN_INT = 1;
438
+ const TOKEN_REAL = 2;
439
+ const TOKEN_STRING = 3;
440
+ const TOKEN_OPEN = 4;
441
+ const TOKEN_CLOSE = 5;
442
+ const LIST_START_MARKER = "_networkx_list_start";
443
+ const EMPTY_LIST_TEXT = "[]";
444
+ const EMPTY_TUPLE_TEXT = "()";
445
+ const ORIGINAL_ID_KEY = "graphty_originalId";
446
+ const SYNTAX_STRUCTURE_CODE = SYNTAX_CODE;
447
+ const SYNTAX_TOKEN_CODE = SYNTAX_CODE;
448
+ const SYNTAX_STRING_CODE = SYNTAX_CODE;
449
+ const SYNTAX_BRACKET_CODE = SYNTAX_CODE;
450
+ const KEY_TEXT = /^[A-Za-z_]\w*$/;
451
+ const STRICT_KEY_TEXT = /^[A-Za-z]\w*$/;
452
+ const INT_TEXT = /^[+-]?\d+$/;
453
+ const REAL_TEXT = /^[+-]?(?:(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+)$/;
454
+ const NON_FINITE_TEXT = /^[+-]?(?:inf|infinity|nan)$/i;
455
+ const NON_FINITE_WORD = /^(?:inf|infinity|nan)$/i;
456
+ class GmlSyntaxError extends Error {
457
+ /**
458
+ * Create a syntax error.
459
+ * @param code - the issue code
460
+ * @param message - a plain-ASCII message
461
+ * @param line - the 1-based line
462
+ */
463
+ constructor(code, message, line) {
464
+ super(message);
465
+ this.name = "GmlSyntaxError";
466
+ this.code = code;
467
+ this.line = line;
468
+ }
469
+ }
470
+ class GmlTokens {
471
+ /**
472
+ * Create an empty token list over a text.
473
+ * @param text - the source text
474
+ * @param capacity - the initial capacity
475
+ */
476
+ constructor(text, capacity = 1024) {
477
+ this.count = 0;
478
+ this.backslashEscapes = false;
479
+ this.onUnknownEntity = null;
480
+ this.text = text;
481
+ this.kind = new Uint8Array(capacity);
482
+ this.start = new Uint32Array(capacity);
483
+ this.end = new Uint32Array(capacity);
484
+ this.line = new Uint32Array(capacity);
485
+ this.match = new Uint32Array(0);
486
+ }
487
+ /**
488
+ * Append a token.
489
+ * @param kind - the kind
490
+ * @param start - the start offset
491
+ * @param end - the end offset
492
+ * @param line - the 1-based line
493
+ */
494
+ push(kind, start, end, line) {
495
+ if (this.count === this.kind.length) {
496
+ this.grow();
497
+ }
498
+ const i = this.count++;
499
+ this.kind[i] = kind;
500
+ this.start[i] = start;
501
+ this.end[i] = end;
502
+ this.line[i] = line;
503
+ }
504
+ /**
505
+ * The raw text of a token (a string's body without the quotes, entities undecoded).
506
+ * @param i - the token index
507
+ * @returns the text
508
+ */
509
+ textOf(i) {
510
+ return this.text.slice(this.start[i], this.end[i]);
511
+ }
512
+ /**
513
+ * The decoded value of a string token.
514
+ * @param i - the token index
515
+ * @returns the body with character references decoded
516
+ */
517
+ stringOf(i) {
518
+ let text = this.textOf(i);
519
+ if (text.includes("\r")) {
520
+ text = text.replaceAll(/\r\n?/g, "\n");
521
+ }
522
+ if (this.backslashEscapes && text.includes("\\")) {
523
+ text = text.replaceAll(/\\([\s\S])/g, "$1");
524
+ }
525
+ if (!text.includes("&")) {
526
+ return text;
527
+ }
528
+ const report = this.onUnknownEntity;
529
+ return decodeGmlString(
530
+ text,
531
+ report === null ? void 0 : (entity) => {
532
+ report(entity, this.line[i]);
533
+ }
534
+ );
535
+ }
536
+ /**
537
+ * Whether a token is a `[` or `]`.
538
+ * @param i - the token index
539
+ * @returns true for a bracket
540
+ */
541
+ isBracket(i) {
542
+ return this.kind[i] >= TOKEN_OPEN;
543
+ }
544
+ /**
545
+ * The index of the pair following the pair whose key is at `key`.
546
+ * @param key - the index of a key token
547
+ * @returns the index after the pair's value (after the matching `]` for a record)
548
+ */
549
+ nextPair(key) {
550
+ const value = key + 1;
551
+ return this.kind[value] === TOKEN_OPEN ? this.match[value] + 1 : value + 1;
552
+ }
553
+ /** Double every array. */
554
+ grow() {
555
+ const capacity = this.kind.length * 2;
556
+ const kind = new Uint8Array(capacity);
557
+ kind.set(this.kind);
558
+ this.kind = kind;
559
+ const start = new Uint32Array(capacity);
560
+ start.set(this.start);
561
+ this.start = start;
562
+ const end = new Uint32Array(capacity);
563
+ end.set(this.end);
564
+ this.end = end;
565
+ const line = new Uint32Array(capacity);
566
+ line.set(this.line);
567
+ this.line = line;
568
+ }
569
+ }
570
+ function tokenizeGml(text) {
571
+ const tokens = new GmlTokens(text, Math.max(1024, text.length >>> 3));
572
+ tokens.backslashEscapes = writtenByOgdf(text);
573
+ const lex = { text, tokens, i: 0, line: 1, spanningLine: 0 };
574
+ while (lex.i < text.length) {
575
+ const c = text.codePointAt(lex.i) ?? 0;
576
+ if (c === 34) {
577
+ lexString(lex);
578
+ } else if (c === 91 || c === 93) {
579
+ tokens.push(c === 91 ? TOKEN_OPEN : TOKEN_CLOSE, lex.i, lex.i + 1, lex.line);
580
+ lex.i++;
581
+ } else if (!skipSpace(lex, c)) {
582
+ lexBare(lex);
583
+ }
584
+ }
585
+ validateStructure(tokens);
586
+ return tokens;
587
+ }
588
+ function skipSpace(lex, c) {
589
+ const { text } = lex;
590
+ if (c === 10 || c === 13) {
591
+ lex.line++;
592
+ lex.i++;
593
+ if (c === 13 && lex.i < text.length && text.codePointAt(lex.i) === 10) {
594
+ lex.i++;
595
+ }
596
+ } else if (c === 32 || c === 9 || c === 11 || c === 12 || c === 65279) {
597
+ lex.i++;
598
+ } else if (c === 35) {
599
+ lex.i = endOfLine(text, lex.i);
600
+ } else {
601
+ return false;
602
+ }
603
+ return true;
604
+ }
605
+ function lexString(lex) {
606
+ const { text, tokens, i } = lex;
607
+ const close = tokens.backslashEscapes ? escapedClose(text, i + 1) : text.indexOf('"', i + 1);
608
+ if (close < 0) {
609
+ const hint = lex.spanningLine === 0 ? "" : `; the string opened at line ${lex.spanningLine} spans lines, so a quote may be missing there`;
610
+ throw new GmlSyntaxError(SYNTAX_STRING_CODE, `unclosed string opened at line ${lex.line}${hint}`, lex.line);
611
+ }
612
+ tokens.push(TOKEN_STRING, i + 1, close, lex.line);
613
+ const opened = lex.line;
614
+ for (let k = i + 1; k < close; k++) {
615
+ const d = text.codePointAt(k);
616
+ if (d === 10 || d === 13 && text.codePointAt(k + 1) !== 10) {
617
+ lex.line++;
618
+ }
619
+ }
620
+ if (lex.line !== opened && lex.spanningLine === 0) {
621
+ lex.spanningLine = opened;
622
+ }
623
+ lex.i = close + 1;
624
+ }
625
+ function lexBare(lex) {
626
+ const { text, i } = lex;
627
+ let j = i + 1;
628
+ while (j < text.length && !isDelimiter(text.codePointAt(j) ?? 0)) {
629
+ j++;
630
+ }
631
+ lex.tokens.push(classifyBare(text, i, j, lex.line), i, j, lex.line);
632
+ lex.i = j;
633
+ }
634
+ function writtenByOgdf(text) {
635
+ const end = Math.min(text.length, 512);
636
+ let i = 0;
637
+ while (i < end) {
638
+ const c = text.codePointAt(i);
639
+ if (c === 35) {
640
+ while (i < end && text.codePointAt(i) !== 10 && text.codePointAt(i) !== 13) {
641
+ i++;
642
+ }
643
+ } else if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12 || c === 11) {
644
+ i++;
645
+ } else {
646
+ break;
647
+ }
648
+ }
649
+ return /^Creator\s+"ogdf::/.test(text.slice(i, i + 64));
650
+ }
651
+ function escapedClose(text, from) {
652
+ let k = from;
653
+ while (k < text.length) {
654
+ const c = text.codePointAt(k);
655
+ if (c === 34) {
656
+ return k;
657
+ }
658
+ k += c === 92 ? 2 : 1;
659
+ }
660
+ return -1;
661
+ }
662
+ function endOfLine(text, from) {
663
+ const n = text.length;
664
+ for (let i = from; i < n; i++) {
665
+ const c = text.charCodeAt(i);
666
+ if (c === 10 || c === 13) {
667
+ return i;
668
+ }
669
+ }
670
+ return n;
671
+ }
672
+ function isDelimiter(c) {
673
+ return c === 32 || c === 9 || c === 10 || c === 13 || c === 11 || c === 12 || c === 91 || c === 93 || c === 34 || c === 35;
674
+ }
675
+ function classifyBare(text, start, end, line) {
676
+ const token = text.slice(start, end);
677
+ if (KEY_TEXT.test(token)) {
678
+ return TOKEN_WORD;
679
+ }
680
+ if (INT_TEXT.test(token)) {
681
+ return TOKEN_INT;
682
+ }
683
+ if (REAL_TEXT.test(token) || NON_FINITE_TEXT.test(token)) {
684
+ return TOKEN_REAL;
685
+ }
686
+ const hex = (c) => (c.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0");
687
+ const head = token.length > 20 ? `${token.slice(0, 20)}...` : token;
688
+ const shown = head.replaceAll(/[^\x20-\x7e]/gu, (c) => String.raw`\u` + hex(c));
689
+ const odd = /[^\x21-\x7e]/u.exec(token);
690
+ const note2 = odd === null ? "" : ` (it holds U+${hex(odd[0])}, which GML allows only inside a string)`;
691
+ throw new GmlSyntaxError(SYNTAX_TOKEN_CODE, `cannot tokenize "${shown}" at line ${line}${note2}`, line);
692
+ }
693
+ function validateStructure(tokens) {
694
+ const { count, kind } = tokens;
695
+ const match = new Uint32Array(count);
696
+ const stack = [];
697
+ let i = 0;
698
+ while (i < count) {
699
+ const k = kind[i];
700
+ if (k === TOKEN_CLOSE) {
701
+ const open = stack.pop();
702
+ if (open === void 0) {
703
+ throw new GmlSyntaxError(
704
+ SYNTAX_STRUCTURE_CODE,
705
+ `unexpected "]" at line ${tokens.line[i]}: no open "["`,
706
+ tokens.line[i]
707
+ );
708
+ }
709
+ match[open] = i;
710
+ i++;
711
+ continue;
712
+ }
713
+ if (k !== TOKEN_WORD) {
714
+ throw new GmlSyntaxError(
715
+ SYNTAX_STRUCTURE_CODE,
716
+ `expected a key at line ${tokens.line[i]}, found ${describeToken(tokens, i)}`,
717
+ tokens.line[i]
718
+ );
719
+ }
720
+ const value = i + 1;
721
+ if (value >= count) {
722
+ throw new GmlSyntaxError(
723
+ SYNTAX_STRUCTURE_CODE,
724
+ `key "${tokens.textOf(i)}" at line ${tokens.line[i]} has no value`,
725
+ tokens.line[i]
726
+ );
727
+ }
728
+ if (checkValue(tokens, i) === TOKEN_OPEN) {
729
+ stack.push(value);
730
+ }
731
+ i = value + 1;
732
+ }
733
+ if (stack.length > 0) {
734
+ const open = stack[stack.length - 1];
735
+ throw new GmlSyntaxError(
736
+ SYNTAX_BRACKET_CODE,
737
+ `unexpected end of input: ${stack.length} bracket${plural(stack.length)} still open, the last opened at line ${tokens.line[open]}`,
738
+ tokens.line[open]
739
+ );
740
+ }
741
+ tokens.match = match;
742
+ }
743
+ function checkValue(tokens, key) {
744
+ const { kind } = tokens;
745
+ const value = key + 1;
746
+ const word = kind[value] === TOKEN_WORD && !NON_FINITE_WORD.test(tokens.textOf(value));
747
+ if (word && tokens.textOf(key) === "Creator") {
748
+ kind[value] = TOKEN_STRING;
749
+ return TOKEN_STRING;
750
+ }
751
+ if (kind[value] === TOKEN_CLOSE || word) {
752
+ throw new GmlSyntaxError(
753
+ SYNTAX_STRUCTURE_CODE,
754
+ `expected a value for key "${tokens.textOf(key)}" at line ${tokens.line[key]}, found ${describeToken(tokens, value)}`,
755
+ tokens.line[value]
756
+ );
757
+ }
758
+ return kind[value];
759
+ }
760
+ function describeToken(tokens, i) {
761
+ switch (tokens.kind[i]) {
762
+ case TOKEN_OPEN:
763
+ return '"["';
764
+ case TOKEN_CLOSE:
765
+ return '"]"';
766
+ case TOKEN_STRING:
767
+ return `the string "${tokens.textOf(i)}"`;
768
+ default:
769
+ return `"${tokens.textOf(i)}"`;
770
+ }
771
+ }
772
+ function numberOfText(text) {
773
+ if (NON_FINITE_TEXT.test(text)) {
774
+ const lower = text.toLowerCase();
775
+ if (lower.endsWith("nan")) {
776
+ return Number.NaN;
777
+ }
778
+ return lower.startsWith("-") ? -Infinity : Infinity;
779
+ }
780
+ return Number(text);
781
+ }
782
+ function isGmlKey(text) {
783
+ return STRICT_KEY_TEXT.test(text);
784
+ }
785
+ const KEY_MANGLED_CODE = "W_GML_KEY_MANGLED";
786
+ function mangleGmlKey(text) {
787
+ const body = text.replaceAll(/\W/g, "_");
788
+ return /^[A-Za-z]/.test(body) ? body : `x${body}`;
789
+ }
790
+ function parseRecord(tokens, open) {
791
+ const root = /* @__PURE__ */ Object.create(null);
792
+ const stack = [
793
+ { record: root, listKeys: null, close: tokens.match[open], next: open + 1, key: null }
794
+ ];
795
+ while (stack.length > 0) {
796
+ const frame = stack[stack.length - 1];
797
+ if (frame.next >= frame.close) {
798
+ stack.pop();
799
+ if (frame.key !== null) {
800
+ storeValue(stack[stack.length - 1], frame.key, frame.record);
801
+ }
802
+ continue;
803
+ }
804
+ const p = frame.next;
805
+ frame.next = tokens.nextPair(p);
806
+ const key = tokens.textOf(p);
807
+ const v = p + 1;
808
+ if (tokens.kind[v] === TOKEN_STRING) {
809
+ storeString(frame, key, tokens.stringOf(v));
810
+ } else if (tokens.kind[v] === TOKEN_OPEN) {
811
+ stack.push({
812
+ record: /* @__PURE__ */ Object.create(null),
813
+ listKeys: null,
814
+ close: tokens.match[v],
815
+ next: v + 1,
816
+ key
817
+ });
818
+ } else {
819
+ storeValue(frame, key, numberOfText(tokens.textOf(v)));
820
+ }
821
+ }
822
+ return root;
823
+ }
824
+ function storeString(frame, key, text) {
825
+ if (text === LIST_START_MARKER) {
826
+ frame.record[key] = [];
827
+ frame.listKeys ??= /* @__PURE__ */ new Set();
828
+ frame.listKeys.add(key);
829
+ return;
830
+ }
831
+ storeValue(frame, key, text === EMPTY_LIST_TEXT || text === EMPTY_TUPLE_TEXT ? [] : text);
832
+ }
833
+ function storeValue(frame, key, value) {
834
+ const { record } = frame;
835
+ if (frame.listKeys?.has(key)) {
836
+ record[key].push(value);
837
+ } else if (key in record) {
838
+ record[key] = [record[key], value];
839
+ frame.listKeys ??= /* @__PURE__ */ new Set();
840
+ frame.listKeys.add(key);
841
+ } else {
842
+ record[key] = value;
843
+ }
844
+ }
845
+ function scalarOrRecord(tokens, v) {
846
+ return tokens.kind[v] === TOKEN_OPEN ? parseRecord(tokens, v) : numberOfText(tokens.textOf(v));
847
+ }
848
+ const WRITERS = [
849
+ [
850
+ /^\s*(yfiles|yed)\b/i,
851
+ { dialect: "yed", tool: "yed", vocabulary: "yed", spelling: "yed", units: "px", labelKey: false }
852
+ ],
853
+ [
854
+ /^\s*cytoscape\b/i,
855
+ {
856
+ dialect: "cytoscape",
857
+ tool: "cytoscape",
858
+ vocabulary: "cytoscape",
859
+ spelling: "cytoscape",
860
+ units: "px",
861
+ labelKey: true
862
+ }
863
+ ],
864
+ [
865
+ /^\s*gephi\b/i,
866
+ {
867
+ dialect: "gephi",
868
+ tool: "gephi",
869
+ vocabulary: "graphlet",
870
+ spelling: "css",
871
+ units: "relative",
872
+ labelKey: false
873
+ }
874
+ ],
875
+ [
876
+ /^\s*ogdf::/i,
877
+ { dialect: "ogdf", tool: "ogdf", vocabulary: "ogdf", spelling: "css", units: "px", labelKey: true }
878
+ ],
879
+ // igraph writes a Creator line, but no drawing defaults of igraph's are stated by a primary source
880
+ [
881
+ /^\s*igraph\b/i,
882
+ { dialect: "igraph", tool: null, vocabulary: "graphlet", spelling: "css", units: "px", labelKey: true }
883
+ ]
884
+ ];
885
+ const UNKNOWN_WRITER = {
886
+ dialect: "gml",
887
+ tool: null,
888
+ vocabulary: "graphlet",
889
+ spelling: "css",
890
+ units: "px",
891
+ labelKey: true
892
+ };
893
+ const GRAPHTY_WRITER = { ...WRITERS[0][1], tool: null };
894
+ const GML_STYLE_BLOCK_KEY = mangleGmlKey(STYLE_BLOCK_NAME);
895
+ const TAG_PREFIX = mangleGmlKey("graphty.style.");
896
+ function tagKey(target, key) {
897
+ return mangleGmlKey(styleTag(target, key));
898
+ }
899
+ function graphText(snapshot, name) {
900
+ if (!snapshot.graph.has(name)) {
901
+ return null;
902
+ }
903
+ const v = plainCell(snapshot.graph.value(name, 0));
904
+ if (typeof v === "string") {
905
+ return v;
906
+ }
907
+ return typeof v === "number" ? String(v) : null;
908
+ }
909
+ function writerOf(snapshot) {
910
+ if (snapshot.graph.has(GML_STYLE_BLOCK_KEY)) {
911
+ return GRAPHTY_WRITER;
912
+ }
913
+ const creator = snapshot.meta.creator ?? graphText(snapshot, "Creator") ?? "";
914
+ for (const [pattern, writer] of WRITERS) {
915
+ if (pattern.test(creator)) {
916
+ return writer;
917
+ }
918
+ }
919
+ return UNKNOWN_WRITER;
920
+ }
921
+ const ARROW_ENDS = /* @__PURE__ */ new Map([
922
+ ["none", ["none", "none"]],
923
+ ["last", ["none", "triangle"]],
924
+ ["first", ["triangle", "none"]],
925
+ ["both", ["triangle", "triangle"]]
926
+ ]);
927
+ const OGDF_STROKES = ["none", "solid", "dashed", "dotted", "dash-dot", "dash-dot"];
928
+ const OGDF_SHAPES = /* @__PURE__ */ new Map([
929
+ ["rect", "rectangle"],
930
+ ["rhomb", "diamond"],
931
+ ["trapeze", "trapezoid"],
932
+ ["invtrapeze", "trapezoid"],
933
+ ["invtriangle", "triangle"],
934
+ ["invparallelogram", "parallelogram"]
935
+ ]);
936
+ const YED_LABEL_KEYS = [
937
+ "outline",
938
+ "autoSizePolicy",
939
+ "model",
940
+ "configuration",
941
+ "borderDistance",
942
+ "modelName",
943
+ "modelPosition"
944
+ ];
945
+ const cytoscapeArrow = CYTOSCAPE_LEXICON.edge.get("targetArrow")?.[0]?.[1];
946
+ function sampleOf(raw) {
947
+ return typeof raw === "string" || typeof raw === "number" ? String(raw).slice(0, MAX_STYLE_VALUE_LENGTH) : "a record";
948
+ }
949
+ function isRec(v) {
950
+ return typeof v === "object" && v !== null && !Array.isArray(v);
951
+ }
952
+ function first(r, ...keys) {
953
+ for (const k of keys) {
954
+ if (Object.hasOwn(r, k) && r[k] !== void 0) {
955
+ return r[k];
956
+ }
957
+ }
958
+ return void 0;
959
+ }
960
+ function readColor(raw, property, opacity, writer, put, at) {
961
+ if (raw === void 0) {
962
+ return;
963
+ }
964
+ const c = canonColor(raw, writer.spelling, at(property));
965
+ if (c === null) {
966
+ return;
967
+ }
968
+ put(property, c.color);
969
+ const alpha = colorOpacity(c, null, at(property), opacity !== null);
970
+ if (opacity !== null) {
971
+ put(opacity, alpha);
972
+ }
973
+ }
974
+ function strokeStyle(raw, kind, writer, at) {
975
+ if (raw === void 0) {
976
+ return null;
977
+ }
978
+ if (writer.vocabulary === "ogdf") {
979
+ const stroke = OGDF_STROKES[Number(raw)];
980
+ if (stroke === void 0 || stroke === "none") {
981
+ noteAt(at, STYLE_ISSUE.UNKNOWN_VALUE, sampleOf(raw));
982
+ return null;
983
+ }
984
+ return stroke;
985
+ }
986
+ return canonEnum(kind, raw, writer.spelling, at);
987
+ }
988
+ function readNodeGraphics(g, writer, put, at) {
989
+ const v = writer.vocabulary;
990
+ readNodeFill(g, writer, put, at);
991
+ readNodeOutline(g, writer, put, at);
992
+ put(
993
+ "borderStyle",
994
+ strokeStyle(v === "ogdf" ? g.stipple : g.outlineStyle, "borderStyle", writer, at("borderStyle"))
995
+ );
996
+ put("width", g.w === void 0 ? null : canonNumber(g.w, at("width")));
997
+ put("height", g.h === void 0 ? null : canonNumber(g.h, at("height")));
998
+ if (g.type !== void 0) {
999
+ const raw = v === "ogdf" && typeof g.type === "string" ? OGDF_SHAPES.get(g.type.toLowerCase()) ?? g.type : g.type;
1000
+ put("shape", canonEnum("shape", raw, writer.spelling, at("shape")));
1001
+ } else if (v === "yed" && typeof g.customconfiguration === "string") {
1002
+ put("shape", flowchartShape(g.customconfiguration));
1003
+ }
1004
+ }
1005
+ function readNodeFill(g, writer, put, at) {
1006
+ if (writer.vocabulary === "yed" && Number(g.hasFill) === 0) {
1007
+ noteAt(at("fill"), STYLE_ISSUE.NO_FILL, "hasFill 0");
1008
+ put("fillOpacity", 0);
1009
+ return;
1010
+ }
1011
+ readColor(g.fill, "fill", "fillOpacity", writer, put, at);
1012
+ if (g.fill2 !== void 0) {
1013
+ noteAt(at("fill"), STYLE_ISSUE.GRADIENT, sampleOf(g.fill2));
1014
+ }
1015
+ }
1016
+ function readNodeOutline(g, writer, put, at) {
1017
+ const v = writer.vocabulary;
1018
+ if (v === "yed" && Number(g.hasOutline) === 0) {
1019
+ put("borderWidth", 0);
1020
+ return;
1021
+ }
1022
+ readColor(v === "ogdf" ? g.line : g.outline, "borderColor", null, writer, put, at);
1023
+ const width = outlineWidthOf(g, v);
1024
+ put("borderWidth", width === void 0 ? null : canonNumber(width, at("borderWidth")));
1025
+ }
1026
+ function outlineWidthOf(g, v) {
1027
+ switch (v) {
1028
+ case "yed":
1029
+ return g.outlineWidth;
1030
+ case "cytoscape":
1031
+ return first(g, "outline_width", "width");
1032
+ case "ogdf":
1033
+ return g.lineWidth;
1034
+ default:
1035
+ return first(g, "width", "outlineWidth");
1036
+ }
1037
+ }
1038
+ function readEdgeGraphics(g, writer, put, at) {
1039
+ const v = writer.vocabulary;
1040
+ readColor(g.fill, "color", "opacity", writer, put, at);
1041
+ const width = v === "ogdf" ? first(g, "lineWidth", "width") : g.width;
1042
+ put("width", width === void 0 ? null : canonNumber(width, at("width")));
1043
+ put("lineStyle", strokeStyle(v === "ogdf" ? g.stipple : g.style, "lineStyle", writer, at("lineStyle")));
1044
+ if (typeof g.arrow === "string") {
1045
+ const ends = ARROW_ENDS.get(g.arrow.trim().toLowerCase());
1046
+ if (ends === void 0) {
1047
+ noteAt(at("targetArrow"), STYLE_ISSUE.UNKNOWN_VALUE, g.arrow.slice(0, MAX_STYLE_VALUE_LENGTH));
1048
+ } else {
1049
+ put("sourceArrow", ends[0]);
1050
+ put("targetArrow", ends[1]);
1051
+ }
1052
+ }
1053
+ for (const [keys, property] of [
1054
+ [["sourceArrow", "source_arrow"], "sourceArrow"],
1055
+ [["targetArrow", "target_arrow"], "targetArrow"]
1056
+ ]) {
1057
+ const raw = first(g, ...keys);
1058
+ if (raw === void 0) {
1059
+ continue;
1060
+ }
1061
+ put(
1062
+ property,
1063
+ v === "cytoscape" && cytoscapeArrow !== void 0 ? cytoscapeArrow(raw, at(property)) : canonEnum("arrow", raw, writer.spelling, at(property))
1064
+ );
1065
+ }
1066
+ if (v === "yed") {
1067
+ readYedCurve(g, put);
1068
+ }
1069
+ }
1070
+ function readYedCurve(g, put) {
1071
+ if (g.smoothBends !== void 0) {
1072
+ const points = isRec(g.Line) ? g.Line.point : void 0;
1073
+ put("curved", Number(g.smoothBends) === 1 && Array.isArray(points) && points.length > 2);
1074
+ }
1075
+ }
1076
+ function readLabelGraphics(raw, writer, put, at) {
1077
+ const visible = (Array.isArray(raw) ? raw : [raw]).filter((l) => isRec(l) && Number(l.visible) !== 0);
1078
+ const lg = visible.at(0);
1079
+ if (lg === void 0) {
1080
+ return;
1081
+ }
1082
+ const extra = visible.slice(1).filter((l) => l.text !== void 0).length;
1083
+ if (extra > 0) {
1084
+ noteAt(at("label"), STYLE_ISSUE.EXTRA_LABELS, `${String(extra + 1)} labels`);
1085
+ }
1086
+ if (lg.text !== void 0) {
1087
+ put("label", canonText(lg.text, at("label")));
1088
+ }
1089
+ const yedLabel = writer.vocabulary === "yed" || YED_LABEL_KEYS.some((k) => Object.hasOwn(lg, k));
1090
+ if (lg.color !== void 0) {
1091
+ readColor(lg.color, "labelColor", null, writer, put, at);
1092
+ } else if (lg.fill !== void 0 && !yedLabel) {
1093
+ readColor(lg.fill, "labelColor", null, writer, put, at);
1094
+ }
1095
+ if (lg.fill !== void 0 && yedLabel) {
1096
+ noteAt(at("label"), STYLE_ISSUE.NOT_CARRIED, "label background");
1097
+ }
1098
+ readLabelFont(lg, put, at);
1099
+ }
1100
+ function readLabelFont(lg, put, at) {
1101
+ put("labelSize", lg.fontSize === void 0 ? null : canonNumber(lg.fontSize, at("labelSize")));
1102
+ put("labelFont", lg.fontName === void 0 ? null : canonText(lg.fontName, at("labelFont")));
1103
+ const style = scalarText(lg.fontStyle, at("labelWeight"))?.trim().toLowerCase();
1104
+ if (style !== void 0) {
1105
+ const known = FONT_STYLES.get(style);
1106
+ if (known === void 0) {
1107
+ noteAt(at("labelWeight"), STYLE_ISSUE.UNKNOWN_VALUE, style.slice(0, MAX_STYLE_VALUE_LENGTH));
1108
+ } else {
1109
+ put("labelWeight", known[0]);
1110
+ put("labelItalic", known[1]);
1111
+ }
1112
+ }
1113
+ }
1114
+ function readElement(target, g, lg, label, writer, put, at) {
1115
+ let text = false;
1116
+ const putLabel = (property, value) => {
1117
+ text ||= property === "label" && value !== null;
1118
+ put(property, value);
1119
+ };
1120
+ if (isRec(g)) {
1121
+ (target === "node" ? readNodeGraphics : readEdgeGraphics)(g, writer, put, at);
1122
+ }
1123
+ if (lg !== null && lg !== void 0) {
1124
+ readLabelGraphics(lg, writer, putLabel, at);
1125
+ }
1126
+ const drawn = writer.vocabulary === "cytoscape" ? target === "node" : writer.labelKey && (isRec(g) || lg !== void 0);
1127
+ if (!text && drawn && label !== null && label !== void 0) {
1128
+ put("label", canonText(label, at("label")));
1129
+ }
1130
+ }
1131
+ function cellOf(table, name, i) {
1132
+ if (!table.has(name)) {
1133
+ return void 0;
1134
+ }
1135
+ const v = plainCell(table.value(name, i));
1136
+ return v ?? void 0;
1137
+ }
1138
+ function centerOf(snapshot, i) {
1139
+ const column = snapshot.nodes.byRole("position");
1140
+ const v = column?.isSet(i) === true ? column.value(i) : null;
1141
+ return v === null ? [Number.NaN, Number.NaN] : [v[0], v[1]];
1142
+ }
1143
+ function lineBends(snapshot, g, e) {
1144
+ const raw = isRec(g) && isRec(g.Line) ? g.Line.point : void 0;
1145
+ if (!Array.isArray(raw) || raw.length < 3) {
1146
+ return null;
1147
+ }
1148
+ const points = raw.slice(1, -1).map((p) => isRec(p) ? [Number(p.x), Number(p.y)] : [Number.NaN, 0]);
1149
+ return canonBends(
1150
+ centerOf(snapshot, snapshot.edgeSource(e)),
1151
+ centerOf(snapshot, snapshot.edgeTarget(e)),
1152
+ points
1153
+ );
1154
+ }
1155
+ function readValues(snapshot, writer, notes, context) {
1156
+ let found = false;
1157
+ const read = (target) => {
1158
+ const table = target === "node" ? snapshot.nodes : snapshot.edges;
1159
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
1160
+ const out = valueColumns(n);
1161
+ if (!table.has("graphics") && !table.has("LabelGraphics") && writer.vocabulary !== "cytoscape") {
1162
+ return out.values;
1163
+ }
1164
+ for (let i = 0; i < n; i++) {
1165
+ context.budget.spend(target, 1);
1166
+ const at = (property) => ({ notes, target, property, element: i, budget: context.budget });
1167
+ readElement(
1168
+ target,
1169
+ cellOf(table, "graphics", i),
1170
+ cellOf(table, "LabelGraphics", i),
1171
+ cellOf(table, "label", i),
1172
+ writer,
1173
+ (property, value) => {
1174
+ if (value !== null) {
1175
+ found = true;
1176
+ out.put(i, property, value);
1177
+ }
1178
+ },
1179
+ at
1180
+ );
1181
+ if (target === "edge") {
1182
+ const bends = lineBends(snapshot, cellOf(table, "graphics", i), i);
1183
+ if (bends !== null) {
1184
+ found = true;
1185
+ out.put(i, "bends", bends);
1186
+ }
1187
+ }
1188
+ }
1189
+ return out.values;
1190
+ };
1191
+ const node = read("node");
1192
+ const edge = read("edge");
1193
+ return { node, edge, found };
1194
+ }
1195
+ function blockWithValues(snapshot, text) {
1196
+ const parsed = parseStyleBlock(text, styleFingerprint(snapshot));
1197
+ if (!parsed.ok || parsed.block.values !== void 0) {
1198
+ return text;
1199
+ }
1200
+ const node = tagValues(snapshot, "node", parsed.block.columns.node);
1201
+ const edge = tagValues(snapshot, "edge", parsed.block.columns.edge);
1202
+ if (node === null || edge === null) {
1203
+ return text;
1204
+ }
1205
+ const raw = JSON.parse(text);
1206
+ return JSON.stringify({ ...raw, values: { node, edge } });
1207
+ }
1208
+ function tagValues(snapshot, target, keys) {
1209
+ const table = target === "node" ? snapshot.nodes : snapshot.edges;
1210
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
1211
+ const values = {};
1212
+ for (const key of keys) {
1213
+ const name = tagKey(target, key);
1214
+ if (!table.has(name)) {
1215
+ return null;
1216
+ }
1217
+ values[key] = Array.from({ length: n }, (_, i) => {
1218
+ const v = plainCell(table.value(name, i));
1219
+ return typeof v === "string" || typeof v === "number" || typeof v === "boolean" ? v : null;
1220
+ });
1221
+ }
1222
+ return values;
1223
+ }
1224
+ function styleColumns(table) {
1225
+ return [...table.names()].filter((c) => c === "graphics" || c === "LabelGraphics" || c.startsWith(TAG_PREFIX));
1226
+ }
1227
+ function gmlStyles(snapshot, context) {
1228
+ const notes = new StyleNotes();
1229
+ const writer = writerOf(snapshot);
1230
+ const values = readValues(snapshot, writer, notes, context);
1231
+ const blockText = graphText(snapshot, GML_STYLE_BLOCK_KEY);
1232
+ const block = blockText === null ? null : blockWithValues(snapshot, blockText);
1233
+ if (block !== null) {
1234
+ exactFromBlock(snapshot, block, values);
1235
+ }
1236
+ if (writer.tool === null && !values.found && block === null) {
1237
+ return null;
1238
+ }
1239
+ const gephi = writer.tool === "gephi" ? gephiRules(snapshot, values.node.fill, values.edge.color, context.budget, values.edge.width) : { rules: [], columns: /* @__PURE__ */ new Map() };
1240
+ return {
1241
+ canonical: true,
1242
+ dialect: writer.dialect,
1243
+ tool: writer.tool,
1244
+ units: writer.units,
1245
+ node: { values: values.node },
1246
+ edge: { values: values.edge, rules: gephi.rules, columns: gephi.columns },
1247
+ notes: notes.list(),
1248
+ opacityInColor: true,
1249
+ block,
1250
+ // the graphics records and the per-element style values of a file graphty wrote hold styles, not data
1251
+ readFrom: { node: styleColumns(snapshot.nodes), edge: styleColumns(snapshot.edges) }
1252
+ };
1253
+ }
1254
+ const GML_STYLE_CAPABILITIES = Object.freeze({
1255
+ node: Object.freeze([
1256
+ "fill",
1257
+ "fillOpacity",
1258
+ "borderColor",
1259
+ "borderWidth",
1260
+ "borderStyle",
1261
+ "shape",
1262
+ "width",
1263
+ "height",
1264
+ "label",
1265
+ "labelColor",
1266
+ "labelSize",
1267
+ "labelFont",
1268
+ "labelWeight",
1269
+ "labelItalic"
1270
+ ]),
1271
+ edge: Object.freeze([
1272
+ "color",
1273
+ "opacity",
1274
+ "width",
1275
+ "lineStyle",
1276
+ "sourceArrow",
1277
+ "targetArrow",
1278
+ "curved",
1279
+ "bends",
1280
+ "label",
1281
+ "labelColor",
1282
+ "labelSize",
1283
+ "labelFont",
1284
+ "labelWeight",
1285
+ "labelItalic"
1286
+ ]),
1287
+ block: "native"
1288
+ });
1289
+ const OWNED = {
1290
+ node: /* @__PURE__ */ new Set([
1291
+ "fill",
1292
+ "fill2",
1293
+ "hasFill",
1294
+ "outline",
1295
+ "hasOutline",
1296
+ "outlineWidth",
1297
+ "outline_width",
1298
+ "outlineStyle",
1299
+ "width",
1300
+ "line",
1301
+ "lineWidth",
1302
+ "stipple",
1303
+ "w",
1304
+ "h",
1305
+ "type"
1306
+ ]),
1307
+ edge: /* @__PURE__ */ new Set([
1308
+ "fill",
1309
+ "width",
1310
+ "lineWidth",
1311
+ "style",
1312
+ "stipple",
1313
+ "arrow",
1314
+ "sourceArrow",
1315
+ "targetArrow",
1316
+ "source_arrow",
1317
+ "target_arrow",
1318
+ "smoothBends"
1319
+ ]),
1320
+ label: /* @__PURE__ */ new Set(["text", "color", "fill", "fontSize", "fontName", "fontStyle", "visible"])
1321
+ };
1322
+ function nodeGraphics(get, approximate) {
1323
+ const g = {};
1324
+ const width = get("width");
1325
+ const height = get("height");
1326
+ if (typeof width === "number") {
1327
+ g.w = width;
1328
+ }
1329
+ if (typeof height === "number") {
1330
+ g.h = height;
1331
+ }
1332
+ const shape = get("shape");
1333
+ if (typeof shape === "string") {
1334
+ const w = yedValue(YED_SHAPES, shape, shape === "vee" ? "triangle" : "rectangle");
1335
+ if (!w.exact) {
1336
+ approximate("shape", shape);
1337
+ }
1338
+ g.type = w.value;
1339
+ }
1340
+ const fill = get("fill");
1341
+ const fillOpacity = get("fillOpacity");
1342
+ if (typeof fill === "string") {
1343
+ g.fill = yedColor(fill, fillOpacity);
1344
+ } else if (fillOpacity === 0) {
1345
+ g.hasFill = 0;
1346
+ }
1347
+ const borderColor = get("borderColor");
1348
+ const borderWidth = get("borderWidth");
1349
+ const borderStyle = get("borderStyle");
1350
+ if (typeof borderColor === "string") {
1351
+ g.outline = borderColor.toUpperCase();
1352
+ }
1353
+ if (typeof borderStyle === "string") {
1354
+ const w = yedValue(YED_LINES, borderStyle, "line");
1355
+ if (!w.exact) {
1356
+ approximate("borderStyle", borderStyle);
1357
+ }
1358
+ g.outlineStyle = w.value;
1359
+ }
1360
+ if (borderWidth === 0) {
1361
+ g.hasOutline = 0;
1362
+ } else if (typeof borderWidth === "number") {
1363
+ g.outlineWidth = borderWidth;
1364
+ }
1365
+ return g;
1366
+ }
1367
+ function edgeGraphics(get, approximate) {
1368
+ const g = {};
1369
+ const color = get("color");
1370
+ if (typeof color === "string") {
1371
+ g.fill = yedColor(color, get("opacity"));
1372
+ }
1373
+ const width = get("width");
1374
+ if (typeof width === "number") {
1375
+ g.width = width;
1376
+ }
1377
+ const lineStyle = get("lineStyle");
1378
+ if (typeof lineStyle === "string") {
1379
+ g.style = yedSpelled(
1380
+ YED_LINES,
1381
+ lineStyle,
1382
+ lineStyle === "long-dash" ? "dashed" : "line",
1383
+ "lineStyle",
1384
+ approximate
1385
+ );
1386
+ }
1387
+ for (const property of ["sourceArrow", "targetArrow"]) {
1388
+ const arrow = get(property);
1389
+ if (typeof arrow === "string") {
1390
+ g[property] = yedSpelled(YED_ARROWS, arrow, "standard", property, approximate);
1391
+ }
1392
+ }
1393
+ const curved = get("curved");
1394
+ if (typeof curved === "boolean") {
1395
+ g.smoothBends = curved ? 1 : 0;
1396
+ }
1397
+ return g;
1398
+ }
1399
+ function labelGraphics(get) {
1400
+ const text = get("label");
1401
+ if (typeof text !== "string" || text.trim().length === 0) {
1402
+ return null;
1403
+ }
1404
+ const lg = { text };
1405
+ const color = get("labelColor");
1406
+ if (typeof color === "string") {
1407
+ lg.color = color.toUpperCase();
1408
+ }
1409
+ const size = get("labelSize");
1410
+ if (typeof size === "number") {
1411
+ lg.fontSize = size;
1412
+ }
1413
+ const font = get("labelFont");
1414
+ if (typeof font === "string") {
1415
+ lg.fontName = font;
1416
+ }
1417
+ const weight = get("labelWeight");
1418
+ const italic = get("labelItalic");
1419
+ if (weight !== null || italic !== null) {
1420
+ const style = (weight === "bold" ? "bold" : "") + (italic === true ? "italic" : "");
1421
+ lg.fontStyle = style.length === 0 ? "plain" : style;
1422
+ }
1423
+ return lg;
1424
+ }
1425
+ function merged(old, owned, written) {
1426
+ const out = {};
1427
+ if (isRec(old)) {
1428
+ for (const [k, v] of Object.entries(old)) {
1429
+ if (!owned.has(k)) {
1430
+ out[k] = v;
1431
+ }
1432
+ }
1433
+ }
1434
+ Object.assign(out, written);
1435
+ return Object.keys(out).length === 0 ? void 0 : out;
1436
+ }
1437
+ function mergedLabels(old, written) {
1438
+ const labels = Array.isArray(old) ? old : [old];
1439
+ const head = merged(labels[0], OWNED.label, written ?? {});
1440
+ const rest = labels.slice(1).filter((l) => l !== null && l !== void 0);
1441
+ if (rest.length === 0) {
1442
+ return head;
1443
+ }
1444
+ return head === void 0 ? rest : [head, ...rest];
1445
+ }
1446
+ function getter(picture, target) {
1447
+ const columns = /* @__PURE__ */ new Map();
1448
+ for (const p of GML_STYLE_CAPABILITIES[target]) {
1449
+ columns.set(p, picture(target, p));
1450
+ }
1451
+ return (i) => (property) => columns.get(property)?.[i] ?? null;
1452
+ }
1453
+ function exactFromBlock(snapshot, text, read) {
1454
+ const parsed = parseStyleBlock(text, styleFingerprint(snapshot));
1455
+ const ex = parsed.ok ? blockStyles(parsed.block, snapshot, "gml") : null;
1456
+ if (ex === null) {
1457
+ return;
1458
+ }
1459
+ const notes = new StyleNotes();
1460
+ for (const target of ["node", "edge"]) {
1461
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
1462
+ const get = getter((t, p) => resolveStyles(ex, t, p, snapshot), target);
1463
+ const values = read[target];
1464
+ for (let i = 0; i < n; i++) {
1465
+ const want = get(i);
1466
+ const back = writtenAndReadBack(target, want, notes);
1467
+ for (const property of GML_STYLE_CAPABILITIES[target]) {
1468
+ const native = values[property]?.[i] ?? null;
1469
+ const w = want(property);
1470
+ if (native !== w && native === (back.get(property) ?? null)) {
1471
+ values[property] ??= new Array(n).fill(null);
1472
+ values[property][i] = w;
1473
+ }
1474
+ }
1475
+ }
1476
+ }
1477
+ }
1478
+ function writtenAndReadBack(target, want, notes) {
1479
+ const ignore = () => void 0;
1480
+ const g = target === "node" ? nodeGraphics(want, ignore) : edgeGraphics(want, ignore);
1481
+ const back = /* @__PURE__ */ new Map();
1482
+ readElement(
1483
+ target,
1484
+ g,
1485
+ labelGraphics(want) ?? void 0,
1486
+ void 0,
1487
+ GRAPHTY_WRITER,
1488
+ (property, value) => {
1489
+ if (value !== null) {
1490
+ back.set(property, value);
1491
+ }
1492
+ },
1493
+ (property) => ({ notes, target, property })
1494
+ );
1495
+ return back;
1496
+ }
1497
+ function gmlStyledExport(snapshot, input) {
1498
+ if (input === void 0) {
1499
+ return null;
1500
+ }
1501
+ const approximated = new StyleApproximations();
1502
+ const out = snapshot.withColumns();
1503
+ const renames = [];
1504
+ for (const target of ["node", "edge"]) {
1505
+ const table = target === "node" ? out.nodes : out.edges;
1506
+ writeGraphicsColumns(snapshot, input, target, table, approximated);
1507
+ const read = dataRead(input.rules.styles.rules, target);
1508
+ for (const name of table.names().filter((c) => mangleGmlKey(c).startsWith(TAG_PREFIX))) {
1509
+ const col = table.get(name);
1510
+ table.remove(name);
1511
+ if (col !== null && !(name.startsWith(TAG_PREFIX) && !read.has(name))) {
1512
+ renames.push(renameDataColumn(table, target, name, col));
1513
+ }
1514
+ }
1515
+ }
1516
+ let { block } = input;
1517
+ const keys = [...input.rules.columns.node.keys()].map((k) => tagKey("node", k));
1518
+ const edgeKeys = [...input.rules.columns.edge.keys()].map((k) => tagKey("edge", k));
1519
+ if (new Set(keys).size !== keys.length || new Set(edgeKeys).size !== edgeKeys.length) {
1520
+ block = writeStyleBlock(input.rules, { inlineColumns: true, snapshot });
1521
+ } else {
1522
+ setTagColumns(out, input);
1523
+ }
1524
+ out.graph.set(GML_STYLE_BLOCK_KEY, [block], { dtype: "string" });
1525
+ const notes = [
1526
+ ...renames,
1527
+ ...block === input.block ? [] : styleBlockLosses(block, input.rules.styles.rules.length, "size"),
1528
+ ...approximated.notes("yEd's GML")
1529
+ ];
1530
+ return { snapshot: out, notes };
1531
+ }
1532
+ function writeGraphicsColumns(snapshot, input, target, table, approximated) {
1533
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
1534
+ const approximate = (property, value) => {
1535
+ approximated.add(target, property, value);
1536
+ };
1537
+ const get = getter((t, p) => input.picture(t, p), target);
1538
+ const graphics = [];
1539
+ const labels = [];
1540
+ for (let i = 0; i < n; i++) {
1541
+ const g = target === "node" ? nodeGraphics(get(i), approximate) : edgeGraphics(get(i), approximate);
1542
+ if (target === "edge") {
1543
+ const line = bendsLine(snapshot, get(i)("bends"), i);
1544
+ if (line !== null) {
1545
+ g.Line = line;
1546
+ }
1547
+ }
1548
+ graphics.push(merged(cellOf(table, "graphics", i), OWNED[target], g));
1549
+ labels.push(mergedLabels(cellOf(table, "LabelGraphics", i), labelGraphics(get(i))));
1550
+ }
1551
+ for (const [name, cells] of [
1552
+ ["graphics", graphics],
1553
+ ["LabelGraphics", labels]
1554
+ ]) {
1555
+ if (cells.some((c) => c !== void 0)) {
1556
+ table.set(name, cells, { dtype: "json", nullable: true });
1557
+ } else if (table.has(name)) {
1558
+ table.remove(name);
1559
+ }
1560
+ }
1561
+ }
1562
+ function bendsLine(snapshot, bends, e) {
1563
+ const source = centerOf(snapshot, snapshot.edgeSource(e));
1564
+ const target = centerOf(snapshot, snapshot.edgeTarget(e));
1565
+ const points = typeof bends === "string" ? bendsToPoints(source, target, bends) : null;
1566
+ if (points === null || ![...source, ...target].every(Number.isFinite)) {
1567
+ return null;
1568
+ }
1569
+ return { point: [source, ...points, target].map(([x, y]) => ({ x, y })) };
1570
+ }
1571
+ function renameDataColumn(table, target, name, col) {
1572
+ let renamed = `data_${name}`;
1573
+ let k = 2;
1574
+ while (table.has(renamed)) {
1575
+ renamed = `data_${name}_${String(k)}`;
1576
+ k++;
1577
+ }
1578
+ table.set(renamed, col);
1579
+ return Object.freeze({
1580
+ code: KEY_MANGLED_CODE,
1581
+ message: `${target} column "${name}" is written as "${renamed}": keys starting with ${TAG_PREFIX} hold graphty's style values`,
1582
+ column: name,
1583
+ count: null
1584
+ });
1585
+ }
1586
+ function setTagColumns(out, input) {
1587
+ for (const target of ["node", "edge"]) {
1588
+ const table = target === "node" ? out.nodes : out.edges;
1589
+ for (const [key, col] of input.rules.columns[target]) {
1590
+ const kinds = new Set(col.filter((v) => v !== null).map((v) => typeof v));
1591
+ const text = kinds.size > 1 || kinds.has("string");
1592
+ let dtype = kinds.has("boolean") ? "bool" : "f64";
1593
+ if (text) {
1594
+ dtype = "string";
1595
+ }
1596
+ table.set(tagKey(target, key), text ? col.map((v) => v === null ? null : String(v)) : col, { dtype });
1597
+ }
1598
+ }
1599
+ }
1600
+ const RECORD_NUMBER_TYPE_CODE = "W_GML_RECORD_NUMBER_TYPE";
1601
+ const RECORD_BOOLEAN_CODE = "W_GML_RECORD_BOOLEAN";
1602
+ const RECORD_NULL_CODE = "W_GML_RECORD_NULL";
1603
+ const NESTED_ARRAY_CODE = "E_GML_NESTED_ARRAY";
1604
+ const JSON_ARRAY_CODE = "W_GML_JSON_ARRAY";
1605
+ const INVALID_KEY_CODE = "E_GML_INVALID_KEY";
1606
+ const RESERVED_KEY_CODE = "E_GML_RESERVED_KEY";
1607
+ const POSITION_COMPONENTS_CODE = "W_GML_POSITION_COMPONENTS";
1608
+ const GRAPHICS_OVERRIDDEN_CODE = "W_GML_GRAPHICS_OVERRIDDEN";
1609
+ const GRAPHICS_CONFLICT_CODE = "E_GML_GRAPHICS_CONFLICT";
1610
+ const DEFAULT_WEIGHT_KEY = "value";
1611
+ const KEPT_ROLES = /* @__PURE__ */ new Set(["label", "id", "key"]);
1612
+ const ROLE_NAMES = Object.freeze({
1613
+ label: "label",
1614
+ id: "id",
1615
+ key: "key"
1616
+ });
1617
+ const GML_CAPABILITIES = capabilities({
1618
+ mixedDirection: false,
1619
+ multiEdges: true,
1620
+ selfLoops: true,
1621
+ edgeIds: "optional",
1622
+ idCharset: "integer",
1623
+ dtypes: ["i32", "f64", "string", "dict", "json"],
1624
+ components: false,
1625
+ lists: true,
1626
+ json: true,
1627
+ defaults: false,
1628
+ options: false,
1629
+ hierarchy: false,
1630
+ temporal: "none",
1631
+ graphAttributes: true,
1632
+ positions: true,
1633
+ viz: false
1634
+ });
1635
+ const SKIPPED_ROLES = /* @__PURE__ */ new Set([
1636
+ "directed",
1637
+ "pair",
1638
+ "mutual",
1639
+ "weight",
1640
+ "timeText",
1641
+ "originalId",
1642
+ "position",
1643
+ "color",
1644
+ "size",
1645
+ "shape",
1646
+ "thickness",
1647
+ "parent",
1648
+ "parents",
1649
+ "start",
1650
+ "end",
1651
+ "timestamp",
1652
+ "timestamps",
1653
+ "spells",
1654
+ "open",
1655
+ "spellsOpen"
1656
+ ]);
1657
+ const NODE_RESERVED = /* @__PURE__ */ new Set(["id"]);
1658
+ const EDGE_RESERVED = /* @__PURE__ */ new Set(["source", "target", "directed"]);
1659
+ const GRAPH_RESERVED = /* @__PURE__ */ new Set(["node", "edge", "directed", "multigraph"]);
1660
+ const TOP_RESERVED = /* @__PURE__ */ new Set(["graph"]);
1661
+ const NUMBER_TEXT = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
1662
+ function planOf(snapshot, options) {
1663
+ const common = resolveExportOptions(options);
1664
+ const mode = options?.sanitizeKeys ?? "error";
1665
+ if (mode !== "error" && mode !== "mangle") {
1666
+ throw new GraphFormatError(
1667
+ "E_UNSUPPORTED",
1668
+ `option sanitizeKeys: ${JSON.stringify(mode)} is not "error" or "mangle"`,
1669
+ {
1670
+ option: "sanitizeKeys",
1671
+ found: mode
1672
+ }
1673
+ );
1674
+ }
1675
+ let weightKey = options?.weightKey;
1676
+ if (weightKey === void 0) {
1677
+ const origin = snapshot.meta.weightOrigin;
1678
+ weightKey = origin !== null && origin.format === "gml" && origin.id !== null ? origin.id : DEFAULT_WEIGHT_KEY;
1679
+ } else if (typeof weightKey !== "string" || !isGmlKey(weightKey)) {
1680
+ throw new GraphFormatError("E_UNSUPPORTED", `option weightKey: ${JSON.stringify(weightKey)} is not a GML key`, {
1681
+ option: "weightKey",
1682
+ found: weightKey
1683
+ });
1684
+ }
1685
+ return { common, weightKey, mangle: mode === "mangle" };
1686
+ }
1687
+ function isWritten(column) {
1688
+ const { role } = column.meta;
1689
+ return role === null || !SKIPPED_ROLES.has(role);
1690
+ }
1691
+ function preferredKey(meta) {
1692
+ const { origin, name } = meta;
1693
+ if (origin?.format !== "gml" || origin.id === null) {
1694
+ return name;
1695
+ }
1696
+ return name === origin.id || name.startsWith(`${origin.id}#`) ? origin.id : name;
1697
+ }
1698
+ function hasWeights(snapshot) {
1699
+ return snapshot.flags.weighted;
1700
+ }
1701
+ function isTopLevel(column) {
1702
+ return column.meta.extra.gmlTopLevel === true;
1703
+ }
1704
+ function versionText(text) {
1705
+ return NUMBER_TEXT.test(text) ? text : quoteGmlString(text);
1706
+ }
1707
+ function labelRoleColumn(columns, label, used) {
1708
+ if (label !== "node" && label !== "edge") {
1709
+ return void 0;
1710
+ }
1711
+ const column = columns.find((c) => c.meta.role === "label");
1712
+ if (column !== void 0) {
1713
+ used.add("label");
1714
+ }
1715
+ return column;
1716
+ }
1717
+ function selectColumns(table, reserved, label, plan, notes, filter = () => true) {
1718
+ const used = new Set(reserved);
1719
+ const out = [];
1720
+ const columns = [...table].filter((c) => isWritten(c) && filter(c));
1721
+ const labelRole = labelRoleColumn(columns, label, used);
1722
+ for (const column of columns) {
1723
+ const preferred = column === labelRole ? "label" : preferredKey(column.meta);
1724
+ let key = preferred;
1725
+ const valid = isGmlKey(preferred);
1726
+ const taken = column !== labelRole && used.has(preferred);
1727
+ if (!valid || taken) {
1728
+ const code = valid ? RESERVED_KEY_CODE : INVALID_KEY_CODE;
1729
+ let why = "is not a GML key";
1730
+ if (valid) {
1731
+ why = reserved.has(preferred) ? "collides with a structural GML key" : "repeats another column's key";
1732
+ }
1733
+ if (!plan.mangle) {
1734
+ if (notes === null) {
1735
+ throw new GraphFormatError(
1736
+ "E_UNSUPPORTED",
1737
+ `${label} column "${column.meta.name}" ${why}; pass sanitizeKeys: "mangle" to rewrite it`,
1738
+ {
1739
+ reason: "gml key",
1740
+ column: column.meta.name,
1741
+ key: preferred
1742
+ }
1743
+ );
1744
+ }
1745
+ notes.push(
1746
+ note(
1747
+ code,
1748
+ `${label} column "${column.meta.name}" ${why}; the save fails unless sanitizeKeys is "mangle"`,
1749
+ column.meta.name
1750
+ )
1751
+ );
1752
+ continue;
1753
+ }
1754
+ key = uniqueKey(valid ? preferred : mangleGmlKey(preferred), used);
1755
+ notes?.push(
1756
+ note(
1757
+ KEY_MANGLED_CODE,
1758
+ `${label} column "${column.meta.name}" is written as "${key}"`,
1759
+ column.meta.name
1760
+ )
1761
+ );
1762
+ }
1763
+ used.add(key);
1764
+ out.push({ column, key });
1765
+ }
1766
+ return out;
1767
+ }
1768
+ function uniqueKey(base, used) {
1769
+ let candidate = base;
1770
+ for (let k = 2; used.has(candidate); k++) {
1771
+ candidate = `${base}_${k}`;
1772
+ }
1773
+ return candidate;
1774
+ }
1775
+ function note(code, message, column = null, count = null) {
1776
+ return Object.freeze({ code, message, column, count });
1777
+ }
1778
+ function countOnce(seen, flag, stats, counter) {
1779
+ if (!seen[flag]) {
1780
+ seen[flag] = true;
1781
+ stats[counter]++;
1782
+ }
1783
+ }
1784
+ function inspectJson(value, stats, inArray, seen) {
1785
+ if (value === null) {
1786
+ countOnce(seen, "nulls", stats, "nulls");
1787
+ return;
1788
+ }
1789
+ switch (typeof value) {
1790
+ case "number":
1791
+ countOnce(seen, "numbers", stats, "numbers");
1792
+ return;
1793
+ case "boolean":
1794
+ countOnce(seen, "booleans", stats, "booleans");
1795
+ return;
1796
+ case "string":
1797
+ return;
1798
+ }
1799
+ if (Array.isArray(value)) {
1800
+ if (inArray) {
1801
+ countOnce(seen, "nested", stats, "nestedArrays");
1802
+ }
1803
+ for (const item of value) {
1804
+ inspectJson(item, stats, true, seen);
1805
+ }
1806
+ return;
1807
+ }
1808
+ const record = value;
1809
+ for (const key of Object.keys(record)) {
1810
+ if (!isGmlKey(key)) {
1811
+ stats.invalidKeys++;
1812
+ }
1813
+ inspectJson(record[key], stats, false, seen);
1814
+ }
1815
+ }
1816
+ function rowCount(count) {
1817
+ return `${count} row${plural(count)}`;
1818
+ }
1819
+ function pushCounted(notes, count, code, text, column) {
1820
+ if (count > 0) {
1821
+ notes.push(note(code, text, column, count));
1822
+ }
1823
+ }
1824
+ function jsonNotes(columns, label, plan, notes) {
1825
+ for (const { column } of columns) {
1826
+ const stats = jsonStatsOf(column);
1827
+ if (stats === null) {
1828
+ continue;
1829
+ }
1830
+ const { name } = column.meta;
1831
+ const where = `${label} column "${name}"`;
1832
+ const counted = (count, code, text) => {
1833
+ pushCounted(notes, count, code, text, name);
1834
+ };
1835
+ counted(
1836
+ stats.numbers,
1837
+ RECORD_NUMBER_TYPE_CODE,
1838
+ `${where} holds numbers in ${rowCount(stats.numbers)}; GML records cannot keep the int / real distinction`
1839
+ );
1840
+ counted(
1841
+ stats.booleans,
1842
+ RECORD_BOOLEAN_CODE,
1843
+ `${where} holds booleans in ${rowCount(stats.booleans)}; written as 1 / 0`
1844
+ );
1845
+ counted(
1846
+ stats.nulls,
1847
+ RECORD_NULL_CODE,
1848
+ `${where} holds nulls in ${rowCount(stats.nulls)}; GML has no null, the key is omitted`
1849
+ );
1850
+ counted(
1851
+ stats.arrays,
1852
+ JSON_ARRAY_CODE,
1853
+ `${where} holds arrays as values in ${rowCount(stats.arrays)}; written as repeated keys, they re-import as a list`
1854
+ );
1855
+ counted(
1856
+ stats.nestedArrays,
1857
+ NESTED_ARRAY_CODE,
1858
+ `${where} holds arrays nested in arrays in ${rowCount(stats.nestedArrays)}; GML cannot write them and the save fails`
1859
+ );
1860
+ const keys = `${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys`;
1861
+ counted(
1862
+ stats.invalidKeys,
1863
+ plan.mangle ? KEY_MANGLED_CODE : INVALID_KEY_CODE,
1864
+ plan.mangle ? `${where}: ${keys} and are rewritten` : `${where}: ${keys}; the save fails unless sanitizeKeys is "mangle"`
1865
+ );
1866
+ }
1867
+ }
1868
+ function jsonStatsOf(column) {
1869
+ const stats = { numbers: 0, booleans: 0, nulls: 0, nestedArrays: 0, arrays: 0, invalidKeys: 0 };
1870
+ if (column.dtype === "json") {
1871
+ for (let r = 0; r < column.length; r++) {
1872
+ if (!column.isSet(r)) {
1873
+ continue;
1874
+ }
1875
+ const value = column.values[r];
1876
+ const seen = { numbers: false, booleans: false, nulls: false, nested: false };
1877
+ if (Array.isArray(value)) {
1878
+ stats.arrays++;
1879
+ for (const item of value) {
1880
+ inspectJson(item, stats, true, seen);
1881
+ }
1882
+ } else {
1883
+ inspectJson(value, stats, false, seen);
1884
+ }
1885
+ }
1886
+ return stats;
1887
+ }
1888
+ if (column.dtype === "list" && column.meta.itemDtype === "json") {
1889
+ for (let r = 0; r < column.length; r++) {
1890
+ if (!column.isSet(r)) {
1891
+ continue;
1892
+ }
1893
+ const seen = { numbers: false, booleans: false, nulls: false, nested: false };
1894
+ for (const item of column.sliceOf(r)) {
1895
+ inspectJson(item, stats, true, seen);
1896
+ }
1897
+ }
1898
+ return stats;
1899
+ }
1900
+ return null;
1901
+ }
1902
+ function positionColumn(snapshot) {
1903
+ const column = snapshot.nodes.byRole("position");
1904
+ if (column === null) {
1905
+ return null;
1906
+ }
1907
+ switch (column.dtype) {
1908
+ case "f32":
1909
+ case "f64":
1910
+ case "i32":
1911
+ case "u32":
1912
+ case "u8":
1913
+ return column;
1914
+ default:
1915
+ return null;
1916
+ }
1917
+ }
1918
+ function graphicsColumn(snapshot) {
1919
+ return snapshot.nodes.typed("graphics", "json");
1920
+ }
1921
+ function graphicsNotes(snapshot, notes) {
1922
+ const position = positionColumn(snapshot);
1923
+ if (position === null) {
1924
+ return;
1925
+ }
1926
+ if (position.meta.components > 3) {
1927
+ notes.push(
1928
+ note(
1929
+ POSITION_COMPONENTS_CODE,
1930
+ `node column "${position.meta.name}" has ${position.meta.components} components; only x, y and z are written`,
1931
+ position.meta.name,
1932
+ position.length - position.nullCount
1933
+ )
1934
+ );
1935
+ }
1936
+ const graphics = graphicsColumn(snapshot);
1937
+ if (graphics === null) {
1938
+ return;
1939
+ }
1940
+ let overridden = 0;
1941
+ let conflicts = 0;
1942
+ for (let r = 0; r < snapshot.nodeCount; r++) {
1943
+ if (!position.isSet(r) || !graphics.isSet(r)) {
1944
+ continue;
1945
+ }
1946
+ const value = graphics.values[r];
1947
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1948
+ conflicts++;
1949
+ } else if ("x" in value || "y" in value || "z" in value) {
1950
+ overridden++;
1951
+ }
1952
+ }
1953
+ if (overridden > 0) {
1954
+ notes.push(
1955
+ note(
1956
+ GRAPHICS_OVERRIDDEN_CODE,
1957
+ `${overridden} node graphics record${plural(overridden)} ${agree(overridden, "has", "have")} x / y / z keys the position column replaces`,
1958
+ "graphics",
1959
+ overridden
1960
+ )
1961
+ );
1962
+ }
1963
+ if (conflicts > 0) {
1964
+ notes.push(
1965
+ note(
1966
+ GRAPHICS_CONFLICT_CODE,
1967
+ `${conflicts} node graphics value${plural(conflicts)} ${agree(conflicts, "is", "are")} not records and cannot hold the position; the save fails`,
1968
+ "graphics",
1969
+ conflicts
1970
+ )
1971
+ );
1972
+ }
1973
+ }
1974
+ function reimportNotes(columns, domain, notes) {
1975
+ for (const { column, key } of columns) {
1976
+ const { meta } = column;
1977
+ const set = column.length - column.nullCount;
1978
+ if (set === 0) {
1979
+ notes.push(
1980
+ note(
1981
+ LOSS.EMPTY_COLUMN,
1982
+ `${domain} column "${meta.name}" has no set cell and is not written: GML writes cells, never declarations`,
1983
+ meta.name,
1984
+ 0
1985
+ )
1986
+ );
1987
+ continue;
1988
+ }
1989
+ if (meta.role === null && (key === "label" || domain === "edge" && (key === "id" || key === "key"))) {
1990
+ notes.push(
1991
+ note(
1992
+ LOSS.ROLE_ASSUMED,
1993
+ `${domain} column "${meta.name}" is written under "${key}" and reads back with the ${key === "label" ? "label" : key} role`,
1994
+ meta.name,
1995
+ set
1996
+ )
1997
+ );
1998
+ continue;
1999
+ }
2000
+ if (column.dtype === "list" || !(column.dtype === "string" || column.dtype === "dict")) {
2001
+ continue;
2002
+ }
2003
+ const heuristic = new DictHeuristic(DICT_SAMPLE_ROWS);
2004
+ for (let r = 0; r < column.length && !heuristic.decided; r++) {
2005
+ if (column.isSet(r)) {
2006
+ heuristic.observe(column.value(r));
2007
+ }
2008
+ }
2009
+ const readsAsDict = heuristic.decide() === "dict";
2010
+ if (column.dtype === "string" && readsAsDict) {
2011
+ notes.push(
2012
+ note(
2013
+ LOSS.STORAGE_CLASS,
2014
+ `${domain} column "${meta.name}" reads back as dict (low cardinality)`,
2015
+ meta.name,
2016
+ null
2017
+ )
2018
+ );
2019
+ } else if (column.dtype === "dict" && !readsAsDict) {
2020
+ notes.push(
2021
+ note(
2022
+ LOSS.STORAGE_CLASS,
2023
+ `${domain} column "${meta.name}" reads back as string (cardinality too high for a dict)`,
2024
+ meta.name,
2025
+ null
2026
+ )
2027
+ );
2028
+ }
2029
+ }
2030
+ }
2031
+ function positionNameNote(snapshot, nodeColumns, notes) {
2032
+ const position = positionColumn(snapshot);
2033
+ if (position === null) {
2034
+ return;
2035
+ }
2036
+ const { name } = position.meta;
2037
+ const plainPosition = nodeColumns.some((c) => c.key === "position");
2038
+ const reimportName = plainPosition ? "position#graphics" : "position";
2039
+ if (name !== reimportName) {
2040
+ notes.push(
2041
+ note(
2042
+ LOSS.COLUMN_NAME_CHANGED,
2043
+ `node column "${name}" (position) is written as the graphics x / y / z keys and reads back as "${reimportName}"`,
2044
+ name,
2045
+ position.length - position.nullCount
2046
+ )
2047
+ );
2048
+ }
2049
+ }
2050
+ function check(snapshot, options) {
2051
+ const plan = planOf(snapshot, options);
2052
+ const notes = checkCapabilities(snapshot, GML_CAPABILITIES, plan.common, {
2053
+ positionDtype: "f64",
2054
+ roles: KEPT_ROLES,
2055
+ roleNames: ROLE_NAMES
2056
+ });
2057
+ const nodeReserved = new Set(NODE_RESERVED);
2058
+ if (plan.common.sanitizeIds === "mangle" && countMangled(snapshot) > 0 || snapshot.nodes.byRole("originalId") !== null) {
2059
+ nodeReserved.add(ORIGINAL_ID_KEY);
2060
+ }
2061
+ const edgeIds = snapshot.edges.byRole("id");
2062
+ const edgeReserved = edgeReservedKeys(snapshot, plan, edgeIds);
2063
+ const topReserved = topReservedKeys(snapshot);
2064
+ const nodeColumns = selectColumns(snapshot.nodes, nodeReserved, "node", plan, notes);
2065
+ const edgeColumns = selectColumns(snapshot.edges, edgeReserved, "edge", plan, notes, (c) => c !== edgeIds);
2066
+ const graphColumns = selectColumns(snapshot.graph, GRAPH_RESERVED, "graph", plan, notes, (c) => !isTopLevel(c));
2067
+ const topColumns = selectColumns(snapshot.graph, topReserved, "top-level", plan, notes, isTopLevel);
2068
+ jsonNotes(nodeColumns, "node", plan, notes);
2069
+ jsonNotes(edgeColumns, "edge", plan, notes);
2070
+ jsonNotes(graphColumns, "graph", plan, notes);
2071
+ jsonNotes(topColumns, "top-level", plan, notes);
2072
+ graphicsNotes(snapshot, notes);
2073
+ positionNameNote(snapshot, nodeColumns, notes);
2074
+ reimportNotes(nodeColumns, "node", notes);
2075
+ reimportNotes(edgeColumns, "edge", notes);
2076
+ if (!hasWeights(snapshot)) {
2077
+ const clash = edgeColumns.find((c) => c.key === plan.weightKey);
2078
+ if (clash !== void 0) {
2079
+ notes.push(
2080
+ note(
2081
+ LOSS.WEIGHT_KEY_CLASH,
2082
+ `edge column "${clash.column.meta.name}" is written under "${plan.weightKey}", the key the importer reads edge weights from; it reads back as the weight, not as a column`,
2083
+ clash.column.meta.name,
2084
+ clash.column.length - clash.column.nullCount
2085
+ )
2086
+ );
2087
+ }
2088
+ }
2089
+ const folding = pairFolding(snapshot);
2090
+ if (folding.mutualCount > 0) {
2091
+ notes.push(
2092
+ note(
2093
+ LOSS.MUTUAL_EXPANDED,
2094
+ `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two directed edges; the mutual mark is lost`,
2095
+ null,
2096
+ folding.mutualCount
2097
+ )
2098
+ );
2099
+ }
2100
+ return Object.freeze(notes);
2101
+ }
2102
+ function edgeReservedKeys(snapshot, plan, edgeIds) {
2103
+ const reserved = new Set(EDGE_RESERVED);
2104
+ if (hasWeights(snapshot)) {
2105
+ reserved.add(plan.weightKey);
2106
+ }
2107
+ if (edgeIds !== null) {
2108
+ reserved.add("id");
2109
+ }
2110
+ return reserved;
2111
+ }
2112
+ function topReservedKeys(snapshot) {
2113
+ const reserved = new Set(TOP_RESERVED);
2114
+ if (snapshot.meta.creator !== null) {
2115
+ reserved.add("Creator");
2116
+ }
2117
+ if (snapshot.meta.sourceFormat === "gml" && snapshot.meta.sourceVersion !== null) {
2118
+ reserved.add("Version");
2119
+ }
2120
+ return reserved;
2121
+ }
2122
+ function countMangled(snapshot) {
2123
+ let count = 0;
2124
+ for (let i = 0; i < snapshot.nodeCount; i++) {
2125
+ const id = snapshot.ids.idOf(i);
2126
+ if (typeof id !== "number" || !Number.isSafeInteger(id)) {
2127
+ count++;
2128
+ }
2129
+ }
2130
+ return count;
2131
+ }
2132
+ function numberText(value, meta) {
2133
+ switch (meta.dtype) {
2134
+ case "i32":
2135
+ case "u32":
2136
+ case "u8":
2137
+ return formatInteger(value);
2138
+ case "f32":
2139
+ return formatGmlReal(value, "f32");
2140
+ default:
2141
+ if (meta.origin?.type === "int" && Number.isInteger(value)) {
2142
+ return formatInteger(value);
2143
+ }
2144
+ return formatGmlReal(value, "f64");
2145
+ }
2146
+ }
2147
+ function jsonScalarText(value) {
2148
+ switch (typeof value) {
2149
+ case "number":
2150
+ return Number.isInteger(value) ? formatInteger(value) : formatGmlReal(value, "f64");
2151
+ case "boolean":
2152
+ return value ? "1" : "0";
2153
+ default:
2154
+ return quoteGmlString(value);
2155
+ }
2156
+ }
2157
+ function itemText(item, itemDtype, meta) {
2158
+ switch (typeof item) {
2159
+ case "number":
2160
+ switch (itemDtype) {
2161
+ case "i32":
2162
+ case "u32":
2163
+ case "u8":
2164
+ return formatInteger(item);
2165
+ case "f32":
2166
+ return formatGmlReal(item, "f32");
2167
+ case "json":
2168
+ return jsonScalarText(item);
2169
+ default:
2170
+ return meta.origin?.type === "int" && Number.isInteger(item) ? formatInteger(item) : formatGmlReal(item, "f64");
2171
+ }
2172
+ case "boolean":
2173
+ return item ? "1" : "0";
2174
+ case "string":
2175
+ return quoteGmlString(item);
2176
+ default:
2177
+ throw new GraphFormatError(
2178
+ "E_COLUMN_TYPE",
2179
+ `column "${meta.name}": a ${typeof item} list item cannot be written as GML`,
2180
+ {
2181
+ column: meta.name,
2182
+ found: typeof item
2183
+ }
2184
+ );
2185
+ }
2186
+ }
2187
+ class GmlWriter {
2188
+ /**
2189
+ * Create a writer.
2190
+ * @param mangle - whether record keys are rewritten rather than refused
2191
+ */
2192
+ constructor(mangle) {
2193
+ this.lines = [];
2194
+ this.mangle = mangle;
2195
+ }
2196
+ /**
2197
+ * Take the lines written so far as one string.
2198
+ * @returns the text; the writer is empty afterwards
2199
+ */
2200
+ take() {
2201
+ const text = this.lines.join("");
2202
+ this.lines.length = 0;
2203
+ return text;
2204
+ }
2205
+ /**
2206
+ * Write one `key value` line.
2207
+ * @param indent - the indentation
2208
+ * @param key - the key
2209
+ * @param value - the value text
2210
+ */
2211
+ line(indent, key, value) {
2212
+ this.lines.push(`${indent}${key} ${value}
2213
+ `);
2214
+ }
2215
+ /**
2216
+ * Write a raw line.
2217
+ * @param text - the line without its terminator
2218
+ */
2219
+ raw(text) {
2220
+ this.lines.push(`${text}
2221
+ `);
2222
+ }
2223
+ /**
2224
+ * Write one column cell of a row.
2225
+ * @param indent - the indentation
2226
+ * @param key - the key
2227
+ * @param column - the column
2228
+ * @param row - the row
2229
+ */
2230
+ cell(indent, key, column, row) {
2231
+ const { meta, dtype } = column;
2232
+ switch (dtype) {
2233
+ case "f32":
2234
+ case "f64":
2235
+ case "i32":
2236
+ case "u32":
2237
+ case "u8": {
2238
+ const { components } = meta;
2239
+ if (components === 1) {
2240
+ this.line(indent, key, numberText(column.data[row], meta));
2241
+ } else {
2242
+ for (let k = 0; k < components; k++) {
2243
+ this.line(indent, key, numberText(column.data[row * components + k], meta));
2244
+ }
2245
+ }
2246
+ return;
2247
+ }
2248
+ case "bool":
2249
+ this.line(indent, key, column.value(row) === true ? "1" : "0");
2250
+ return;
2251
+ case "dict":
2252
+ case "string":
2253
+ this.line(indent, key, quoteGmlString(column.value(row) ?? ""));
2254
+ return;
2255
+ case "list":
2256
+ this.list(indent, key, column.sliceOf(row), meta.itemDtype ?? "string", meta);
2257
+ return;
2258
+ case "json":
2259
+ this.json(indent, key, column.values[row], meta.name);
2260
+ return;
2261
+ default: {
2262
+ const name = dtype;
2263
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
2264
+ }
2265
+ }
2266
+ }
2267
+ /**
2268
+ * Write a list as repeated keys with the NetworkX conventions.
2269
+ * @param indent - the indentation
2270
+ * @param key - the key
2271
+ * @param items - the items
2272
+ * @param itemDtype - the item dtype
2273
+ * @param meta - the column metadata
2274
+ */
2275
+ list(indent, key, items, itemDtype, meta) {
2276
+ if (items.length === 0) {
2277
+ this.line(indent, key, quoteGmlString(EMPTY_LIST_TEXT));
2278
+ return;
2279
+ }
2280
+ if (items.length === 1) {
2281
+ this.line(indent, key, quoteGmlString(LIST_START_MARKER));
2282
+ }
2283
+ for (const item of items) {
2284
+ if (itemDtype === "json" && typeof item === "object") {
2285
+ this.json(indent, key, item, meta.name, true);
2286
+ } else {
2287
+ this.line(indent, key, itemText(item, itemDtype, meta));
2288
+ }
2289
+ }
2290
+ }
2291
+ /**
2292
+ * Write a JSON value: a record for an object, repeated keys for an array, a scalar otherwise;
2293
+ * null writes nothing.
2294
+ * @param indent - the indentation
2295
+ * @param key - the key
2296
+ * @param value - the value
2297
+ * @param column - the column name, for errors
2298
+ * @param inArray - whether the value is an array item (an array here has no GML spelling)
2299
+ */
2300
+ json(indent, key, value, column, inArray = false) {
2301
+ if (value === null || value === void 0) {
2302
+ return;
2303
+ }
2304
+ if (Array.isArray(value)) {
2305
+ if (inArray) {
2306
+ throw new GraphFormatError(
2307
+ "E_UNSUPPORTED",
2308
+ `column "${column}": an array nested in an array cannot be written as GML`,
2309
+ {
2310
+ reason: "nested array",
2311
+ column
2312
+ }
2313
+ );
2314
+ }
2315
+ if (value.length === 0) {
2316
+ this.line(indent, key, quoteGmlString(EMPTY_LIST_TEXT));
2317
+ return;
2318
+ }
2319
+ if (value.length === 1) {
2320
+ this.line(indent, key, quoteGmlString(LIST_START_MARKER));
2321
+ }
2322
+ for (const item of value) {
2323
+ this.json(indent, key, item, column, true);
2324
+ }
2325
+ return;
2326
+ }
2327
+ if (typeof value === "object") {
2328
+ this.record(indent, key, value, column);
2329
+ return;
2330
+ }
2331
+ if (typeof value === "number" || typeof value === "string" || typeof value === "boolean") {
2332
+ this.line(indent, key, jsonScalarText(value));
2333
+ return;
2334
+ }
2335
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${column}": a ${typeof value} cannot be written as GML`, {
2336
+ column,
2337
+ found: typeof value
2338
+ });
2339
+ }
2340
+ /**
2341
+ * Write a record `key [ ... ]`.
2342
+ * @param indent - the indentation
2343
+ * @param key - the key
2344
+ * @param record - the object
2345
+ * @param column - the column name, for errors
2346
+ * @param extra - lines to write first (the position of a graphics record), or null
2347
+ */
2348
+ record(indent, key, record, column, extra = null) {
2349
+ this.raw(`${indent}${key} [`);
2350
+ const inner = `${indent} `;
2351
+ const used = /* @__PURE__ */ new Set();
2352
+ if (extra !== null) {
2353
+ for (const [k, v] of extra) {
2354
+ this.line(inner, k, v);
2355
+ used.add(k);
2356
+ }
2357
+ }
2358
+ for (const name of Object.keys(record)) {
2359
+ if (used.has(name) && extra !== null && (name === "x" || name === "y" || name === "z")) {
2360
+ continue;
2361
+ }
2362
+ const written = this.recordKey(name, used, column);
2363
+ used.add(written);
2364
+ this.json(inner, written, record[name], column);
2365
+ }
2366
+ this.raw(`${indent}]`);
2367
+ }
2368
+ /**
2369
+ * The key a record field is written under.
2370
+ * @param name - the field name
2371
+ * @param used - the keys already written in the record
2372
+ * @param column - the column name, for errors
2373
+ * @returns a valid, unused key; E_UNSUPPORTED for an invalid key unless mangling
2374
+ */
2375
+ recordKey(name, used, column) {
2376
+ if (isGmlKey(name) && !used.has(name)) {
2377
+ return name;
2378
+ }
2379
+ if (!this.mangle) {
2380
+ throw new GraphFormatError(
2381
+ "E_UNSUPPORTED",
2382
+ `column "${column}": record key "${name}" ${isGmlKey(name) ? "repeats a key" : "is not a GML key"}; pass sanitizeKeys: "mangle" to rewrite it`,
2383
+ {
2384
+ reason: "gml key",
2385
+ column,
2386
+ key: name
2387
+ }
2388
+ );
2389
+ }
2390
+ return uniqueKey(isGmlKey(name) ? name : mangleGmlKey(name), used);
2391
+ }
2392
+ }
2393
+ function contextOf(snapshot, options) {
2394
+ const plan = planOf(snapshot, options);
2395
+ const ids = sanitizeIds(snapshot, "integer", plan.common.sanitizeIds);
2396
+ const mixed = countMixedEdges(snapshot);
2397
+ if (mixed > 0 && plan.common.onMixedDirection === "error") {
2398
+ throw new GraphFormatError(
2399
+ "E_DIRECTED",
2400
+ `${mixed} undirected edge${plural(mixed)} in a directed graph, and GML holds one direction per file, so the save fails unless onMixedDirection is "directed" or "undirected"`,
2401
+ {
2402
+ reason: "mixed direction",
2403
+ count: mixed
2404
+ }
2405
+ );
2406
+ }
2407
+ const foldPairs = mixed > 0;
2408
+ const writeDirected = snapshot.directed && plan.common.onMixedDirection !== "undirected";
2409
+ const originalIds = snapshot.nodes.byRole("originalId");
2410
+ const nodeReserved = new Set(NODE_RESERVED);
2411
+ if (ids.changed > 0 || originalIds !== null) {
2412
+ nodeReserved.add(ORIGINAL_ID_KEY);
2413
+ }
2414
+ const edgeIds = snapshot.edges.byRole("id");
2415
+ const edgeReserved = edgeReservedKeys(snapshot, plan, edgeIds);
2416
+ const topReserved = topReservedKeys(snapshot);
2417
+ return {
2418
+ snapshot,
2419
+ plan,
2420
+ ids,
2421
+ writeDirected,
2422
+ foldPairs,
2423
+ nodeColumns: selectColumns(
2424
+ snapshot.nodes,
2425
+ nodeReserved,
2426
+ "node",
2427
+ plan,
2428
+ null,
2429
+ (c) => !(c.meta.name === "graphics" && c.dtype === "json")
2430
+ ),
2431
+ edgeColumns: selectColumns(snapshot.edges, edgeReserved, "edge", plan, null, (c) => c !== edgeIds),
2432
+ graphColumns: selectColumns(snapshot.graph, GRAPH_RESERVED, "graph", plan, null, (c) => !isTopLevel(c)),
2433
+ topColumns: selectColumns(snapshot.graph, topReserved, "top-level", plan, null, isTopLevel),
2434
+ position: positionColumn(snapshot),
2435
+ graphics: graphicsColumn(snapshot),
2436
+ originalIds,
2437
+ edgeIds,
2438
+ weights: explicitWeights(snapshot),
2439
+ folding: pairFolding(snapshot)
2440
+ };
2441
+ }
2442
+ function originalIdText(id) {
2443
+ if (typeof id === "string") {
2444
+ return quoteGmlString(id);
2445
+ }
2446
+ return Number.isSafeInteger(id) ? formatInteger(id) : formatGmlReal(id, "f64");
2447
+ }
2448
+ function* gmlParts(context) {
2449
+ const { snapshot, plan, ids } = context;
2450
+ const w = new GmlWriter(plan.mangle);
2451
+ const { meta } = snapshot;
2452
+ if (meta.creator !== null) {
2453
+ w.line("", "Creator", quoteGmlString(meta.creator));
2454
+ }
2455
+ if (meta.sourceFormat === "gml" && meta.sourceVersion !== null) {
2456
+ w.line("", "Version", versionText(meta.sourceVersion));
2457
+ }
2458
+ for (const { column, key } of context.topColumns) {
2459
+ if (column.isSet(0)) {
2460
+ w.cell("", key, column, 0);
2461
+ }
2462
+ }
2463
+ w.raw("graph [");
2464
+ w.line(" ", "directed", context.writeDirected ? "1" : "0");
2465
+ const multigraph = meta.declaredMultigraph ?? (snapshot.flags.multigraph ? true : null);
2466
+ if (multigraph !== null) {
2467
+ w.line(" ", "multigraph", multigraph ? "1" : "0");
2468
+ }
2469
+ for (const { column, key } of context.graphColumns) {
2470
+ if (column.isSet(0)) {
2471
+ w.cell(" ", key, column, 0);
2472
+ }
2473
+ }
2474
+ yield w.take();
2475
+ for (let i = 0; i < snapshot.nodeCount; i++) {
2476
+ w.raw(" node [");
2477
+ w.line(" ", "id", formatInteger(ids.idAt(i)));
2478
+ if (ids.isChanged(i)) {
2479
+ w.line(" ", ORIGINAL_ID_KEY, originalIdText(ids.originalAt(i)));
2480
+ } else if (context.originalIds?.isSet(i)) {
2481
+ const original = context.originalIds.value(i);
2482
+ if (typeof original === "string" || typeof original === "number") {
2483
+ w.line(" ", ORIGINAL_ID_KEY, originalIdText(original));
2484
+ }
2485
+ }
2486
+ for (const { column, key } of context.nodeColumns) {
2487
+ if (column.isSet(i)) {
2488
+ w.cell(" ", key, column, i);
2489
+ }
2490
+ }
2491
+ writeGraphics(w, context, i);
2492
+ w.raw(" ]");
2493
+ yield w.take();
2494
+ }
2495
+ const el = snapshot.edgeList();
2496
+ const origin = meta.weightOrigin;
2497
+ const integerWeights = origin !== null && origin.type === "int";
2498
+ for (let e = 0; e < snapshot.edgeCount; e++) {
2499
+ if (context.foldPairs && context.folding.folded(e)) {
2500
+ continue;
2501
+ }
2502
+ w.raw(" edge [");
2503
+ w.line(" ", "source", formatInteger(ids.idAt(el.src[e])));
2504
+ w.line(" ", "target", formatInteger(ids.idAt(el.dst[e])));
2505
+ if (context.edgeIds?.isSet(e)) {
2506
+ w.cell(" ", "id", context.edgeIds, e);
2507
+ }
2508
+ if (context.weights.isExplicit(e)) {
2509
+ const weight = context.weights.value(e);
2510
+ const text = integerWeights && Number.isInteger(weight) ? formatInteger(weight) : formatGmlReal(weight, context.weights.dtype);
2511
+ w.line(" ", plan.weightKey, text);
2512
+ }
2513
+ for (const { column, key } of context.edgeColumns) {
2514
+ if (column.isSet(e)) {
2515
+ w.cell(" ", key, column, e);
2516
+ }
2517
+ }
2518
+ w.raw(" ]");
2519
+ yield w.take();
2520
+ }
2521
+ w.raw("]");
2522
+ yield w.take();
2523
+ }
2524
+ function writeGraphics(w, context, i) {
2525
+ const { position, graphics } = context;
2526
+ const hasPosition = position?.isSet(i) === true;
2527
+ const graphicsValue = graphics?.isSet(i) ? graphics.values[i] : void 0;
2528
+ if (!hasPosition) {
2529
+ if (graphics !== null && graphicsValue !== void 0) {
2530
+ w.cell(" ", "graphics", graphics, i);
2531
+ }
2532
+ return;
2533
+ }
2534
+ const numeric = position;
2535
+ const { components } = numeric.meta;
2536
+ const lanes = Math.min(components, 3);
2537
+ const extra = [];
2538
+ const names = ["x", "y", "z"];
2539
+ for (let k = 0; k < lanes; k++) {
2540
+ extra.push([names[k], numberText(numeric.data[i * components + k], numeric.meta)]);
2541
+ }
2542
+ let record = {};
2543
+ if (graphicsValue !== void 0) {
2544
+ if (typeof graphicsValue !== "object" || graphicsValue === null || Array.isArray(graphicsValue)) {
2545
+ throw new GraphFormatError(
2546
+ "E_UNSUPPORTED",
2547
+ `node ${i}: the graphics value is not a record and cannot hold the position`,
2548
+ {
2549
+ reason: "graphics conflict",
2550
+ node: i
2551
+ }
2552
+ );
2553
+ }
2554
+ record = graphicsValue;
2555
+ }
2556
+ w.record(" ", "graphics", record, "graphics", extra);
2557
+ }
2558
+ const gmlExporter = Object.freeze({
2559
+ format: "gml",
2560
+ options: Object.freeze(["sanitizeKeys", "weightKey"]),
2561
+ capabilities: GML_CAPABILITIES,
2562
+ styles: Object.freeze({ capabilities: GML_STYLE_CAPABILITIES }),
2563
+ /**
2564
+ * Pre-flight: the common capability notes, the GML-specific ones and, with styles, the values written as the
2565
+ * nearest ones yEd's GML has.
2566
+ * @param snapshot - the snapshot
2567
+ * @param options - the options
2568
+ * @returns the notes, empty when the export is exact
2569
+ */
2570
+ check(snapshot, options) {
2571
+ const styled = gmlStyledExport(snapshot, options?.styles);
2572
+ if (styled === null) {
2573
+ return check(snapshot, options);
2574
+ }
2575
+ return Object.freeze([...check(styled.snapshot, options), ...styled.notes]);
2576
+ },
2577
+ /**
2578
+ * Write the snapshot as UTF-8 chunks; the context is resolved on the first pull, so the
2579
+ * errors check() announced surface from the iteration.
2580
+ * @param snapshot - the snapshot
2581
+ * @param options - the options
2582
+ * @returns the chunks
2583
+ */
2584
+ export(snapshot, options) {
2585
+ return encodeChunks(lazyParts(snapshot, options));
2586
+ },
2587
+ /**
2588
+ * Write the snapshot as one string.
2589
+ * @param snapshot - the snapshot
2590
+ * @param options - the options
2591
+ * @returns the document
2592
+ */
2593
+ exportToString(snapshot, options) {
2594
+ return joinText(lazyParts(snapshot, options));
2595
+ }
2596
+ });
2597
+ function* lazyParts(snapshot, options) {
2598
+ yield* gmlParts(contextOf(gmlStyledExport(snapshot, options?.styles)?.snapshot ?? snapshot, options));
2599
+ }
2600
+ const GRAPHICS_COLUMN = "graphics";
2601
+ function gmlPositions(snapshot) {
2602
+ if (snapshot.nodes.byRole("position") === null) {
2603
+ return null;
2604
+ }
2605
+ const tool = toolOf(snapshot.meta.creator);
2606
+ return {
2607
+ units: "px",
2608
+ // a z that is the same on every node collapses to 2D
2609
+ sourceDims: 3,
2610
+ yAxis: tool === "gephi" || tool === "tulip" ? "up" : "down",
2611
+ tool,
2612
+ medianNodeSize: graphicsMedian(snapshot)
2613
+ };
2614
+ }
2615
+ function graphicsMedian(snapshot) {
2616
+ const graphics = snapshot.nodes.get(GRAPHICS_COLUMN);
2617
+ const sizes = [];
2618
+ for (let row = 0; graphics !== null && row < graphics.length; row++) {
2619
+ const g = graphics.isSet(row) ? graphics.value(row) : void 0;
2620
+ const { w, h } = typeof g === "object" && g !== null ? g : {};
2621
+ if (typeof w === "number" && typeof h === "number") {
2622
+ sizes.push((w + h) / 2);
2623
+ }
2624
+ }
2625
+ return median(sizes);
2626
+ }
2627
+ const NO_GRAPH_CODE = NO_GRAPH_CODE$1;
2628
+ const SECOND_GRAPH_CODE = MULTIPLE_GRAPHS_CODE;
2629
+ const MISSING_ID_CODE = MISSING_ID_CODE$1;
2630
+ const MISSING_LABEL_CODE = "E_GML_MISSING_LABEL";
2631
+ const MISSING_ENDPOINT_CODE = MISSING_ENDPOINT_CODE$1;
2632
+ const ID_TYPE_CODE = "E_GML_ID_TYPE";
2633
+ const STRING_ID_CODE = "W_GML_STRING_ID";
2634
+ const DUPLICATE_NODE_CODE = DUPLICATE_NODE_CODE$1;
2635
+ const REPEATED_KEY_CODE = "E_GML_REPEATED_KEY";
2636
+ const ELEMENT_TYPE_CODE = "E_GML_ELEMENT_TYPE";
2637
+ const FLAG_TYPE_CODE = "E_GML_FLAG_TYPE";
2638
+ const FLAG_VALUE_CODE = "W_GML_FLAG_VALUE";
2639
+ const QUOTED_INT = /^\s*[+-]?\d+\s*$/;
2640
+ const UNKNOWN_ENTITY_CODE = "W_GML_UNKNOWN_ENTITY";
2641
+ const GRAPHICS_CODE = "W_GML_GRAPHICS";
2642
+ const NESTED_ELEMENT_CODE = "W_GML_NESTED_ELEMENT";
2643
+ const GROUPS_CODE = "W_GML_GROUPS";
2644
+ const WIDENED_CODE = WIDENED_CODE$1;
2645
+ const PRECISION_CODE = PRECISION_CODE$1;
2646
+ const COLUMN_RENAMED_CODE_GML = COLUMN_RENAMED_CODE;
2647
+ const ROLE_TAKEN_CODE = ROLE_TAKEN_CODE$1;
2648
+ const ID_DROPPED_CODE = "W_GML_ID_DROPPED";
2649
+ const FORMAT_DEFAULTS = { ids: "canonical", defaultDirected: false, weightFrom: "value" };
2650
+ const KIND_INT = 1;
2651
+ const KIND_REAL = 2;
2652
+ const KIND_STRING = 4;
2653
+ const KIND_RECORD = 8;
2654
+ const I32_MIN = -2147483648;
2655
+ const I32_MAX = 2147483647;
2656
+ const ABORT_CHECK_INTERVAL = 64;
2657
+ const USED_OPTIONS = /* @__PURE__ */ new Set([
2658
+ "ids",
2659
+ "nodeIdFrom",
2660
+ "addMissingNodes",
2661
+ "duplicateEdges",
2662
+ "selfLoops",
2663
+ "onMixedDirection",
2664
+ "defaultDirected",
2665
+ "weightFrom",
2666
+ "weightDtype",
2667
+ "restoreMangledIds",
2668
+ "long",
2669
+ "errorLimit",
2670
+ "signal",
2671
+ "onProgress"
2672
+ ]);
2673
+ const GRAPH_SEQ = -1;
2674
+ const TOP_SEQ = -2;
2675
+ function isLabel(domain, key) {
2676
+ return roleOf(domain, key) === "label";
2677
+ }
2678
+ function isRecord(value) {
2679
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2680
+ }
2681
+ function roleOf(domain, key) {
2682
+ if (key === "label" && domain !== "graph") {
2683
+ return "label";
2684
+ }
2685
+ if (domain === "edge") {
2686
+ if (key === "id") {
2687
+ return "id";
2688
+ }
2689
+ if (key === "key") {
2690
+ return "key";
2691
+ }
2692
+ }
2693
+ return null;
2694
+ }
2695
+ class GmlImport {
2696
+ /**
2697
+ * Create one import.
2698
+ * @param sink - the sink
2699
+ * @param report - the report
2700
+ * @param options - the resolved common options
2701
+ * @param format - the format-specific options
2702
+ * @param which - which `graph [ ... ]` block to read (0: the first)
2703
+ */
2704
+ constructor(sink, report, options, format, which = 0) {
2705
+ this.which = which;
2706
+ this.tokens = null;
2707
+ this.graphCount = 0;
2708
+ this.resolver = null;
2709
+ this.nodeSchema = /* @__PURE__ */ new Map();
2710
+ this.edgeSchema = /* @__PURE__ */ new Map();
2711
+ this.graphSchema = /* @__PURE__ */ new Map();
2712
+ this.topSchema = /* @__PURE__ */ new Map();
2713
+ this.nodePlans = /* @__PURE__ */ new Map();
2714
+ this.edgePlans = /* @__PURE__ */ new Map();
2715
+ this.graphPlans = /* @__PURE__ */ new Map();
2716
+ this.topPlans = /* @__PURE__ */ new Map();
2717
+ this.graphNames = /* @__PURE__ */ new Set();
2718
+ this.graphOpen = -1;
2719
+ this.directedToken = -1;
2720
+ this.multigraphToken = -1;
2721
+ this.creatorToken = -1;
2722
+ this.versionToken = -1;
2723
+ this.nodeCount = 0;
2724
+ this.edgeCount = 0;
2725
+ this.weightKinds = 0;
2726
+ this.hasOriginalId = false;
2727
+ this.headerDirected = false;
2728
+ this.seq = 0;
2729
+ this.nodeOrdinal = 0;
2730
+ this.elementsSinceCheck = 0;
2731
+ this.idMap = null;
2732
+ this.skippedIds = /* @__PURE__ */ new Set();
2733
+ this.declared = new Uint32Array(64);
2734
+ this.touched = [];
2735
+ this.graphTouched = [];
2736
+ this.seqNow = 0;
2737
+ this.sink = sink;
2738
+ this.report = report;
2739
+ this.options = options;
2740
+ this.positions = format?.positions ?? true;
2741
+ this.keepPositionSource = format?.keepPositionSource ?? false;
2742
+ this.dictionaries = format?.dictionaries ?? true;
2743
+ this.coercer = new IdCoercer(options.ids);
2744
+ }
2745
+ /**
2746
+ * Run the import over the tokens of a text.
2747
+ * @param tokens - the whole text, tokenized (tokenize())
2748
+ */
2749
+ run(tokens) {
2750
+ this.tokens = tokens;
2751
+ tokens.onUnknownEntity = (entity, line) => {
2752
+ this.report.warnOnce(
2753
+ "parse-error",
2754
+ UNKNOWN_ENTITY_CODE,
2755
+ `character entity ${entity} cannot be decoded; it is kept as written`,
2756
+ { line, element: entity },
2757
+ `${UNKNOWN_ENTITY_CODE}:${entity}`
2758
+ );
2759
+ };
2760
+ this.scan();
2761
+ this.push();
2762
+ }
2763
+ // ============================================================ pass 1: the schema
2764
+ /**
2765
+ * Walk the top level: find the graph block, the metadata keys and the top-level attributes,
2766
+ * and derive every table's schema.
2767
+ */
2768
+ scan() {
2769
+ const t = this.requireTokens();
2770
+ let scalarGraph = -1;
2771
+ for (let p = 0; p < t.count; p = t.nextPair(p)) {
2772
+ const key = t.textOf(p);
2773
+ const v = p + 1;
2774
+ const record = t.kind[v] === TOKEN_OPEN;
2775
+ if (key === "graph" && !record && scalarGraph < 0) {
2776
+ scalarGraph = v;
2777
+ }
2778
+ this.scanTopPair(key, record, v);
2779
+ }
2780
+ if (this.graphOpen < 0) {
2781
+ this.failNoGraph(t, scalarGraph);
2782
+ }
2783
+ }
2784
+ /**
2785
+ * Handle one top-level pair other than a scalar `graph`.
2786
+ * @param key - the key
2787
+ * @param record - whether the value is a `[ ... ]` block
2788
+ * @param v - the value token
2789
+ */
2790
+ scanTopPair(key, record, v) {
2791
+ if (key === "graph" && record) {
2792
+ if (this.graphCount++ === this.which) {
2793
+ this.graphOpen = v;
2794
+ this.scanGraph(v);
2795
+ }
2796
+ } else if (key === "Creator" && !record && this.creatorToken < 0) {
2797
+ this.creatorToken = v;
2798
+ } else if (key === "Version" && !record && this.versionToken < 0) {
2799
+ this.versionToken = v;
2800
+ } else {
2801
+ this.observe(this.topSchema, "graph", key, v, TOP_SEQ);
2802
+ }
2803
+ }
2804
+ /**
2805
+ * Fail because the input holds no graph block.
2806
+ * @param t - the tokens
2807
+ * @param scalarGraph - the token of a scalar `graph` key, or -1
2808
+ */
2809
+ failNoGraph(t, scalarGraph) {
2810
+ if (scalarGraph >= 0) {
2811
+ this.report.fail(
2812
+ NO_GRAPH_CODE,
2813
+ `the graph key at line ${t.line[scalarGraph]} holds ${describeValue(t, scalarGraph)}, not a [ ... ] block; the input contains no graph block`,
2814
+ { line: t.line[scalarGraph] }
2815
+ );
2816
+ }
2817
+ this.report.fail(NO_GRAPH_CODE, "the input contains no graph [ ... ] block", { line: null });
2818
+ }
2819
+ /**
2820
+ * Walk the graph block for the schema.
2821
+ * @param open - the index of the block's `[`
2822
+ */
2823
+ scanGraph(open) {
2824
+ const t = this.requireTokens();
2825
+ const close = t.match[open];
2826
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
2827
+ const key = t.textOf(p);
2828
+ const v = p + 1;
2829
+ switch (key) {
2830
+ case "node":
2831
+ if (t.kind[v] === TOKEN_OPEN) {
2832
+ this.nodeCount++;
2833
+ this.scanElement("node", v);
2834
+ }
2835
+ break;
2836
+ case "edge":
2837
+ if (t.kind[v] === TOKEN_OPEN) {
2838
+ this.edgeCount++;
2839
+ this.scanElement("edge", v);
2840
+ }
2841
+ break;
2842
+ case "directed":
2843
+ if (this.directedToken < 0) {
2844
+ this.directedToken = v;
2845
+ }
2846
+ break;
2847
+ case "multigraph":
2848
+ if (this.multigraphToken < 0) {
2849
+ this.multigraphToken = v;
2850
+ }
2851
+ break;
2852
+ default:
2853
+ this.observe(this.graphSchema, "graph", key, v, GRAPH_SEQ);
2854
+ }
2855
+ }
2856
+ }
2857
+ /**
2858
+ * Walk one node or edge block for the schema.
2859
+ * @param domain - node or edge
2860
+ * @param open - the block's `[`
2861
+ */
2862
+ scanElement(domain, open) {
2863
+ const t = this.requireTokens();
2864
+ const close = t.match[open];
2865
+ const seq = this.seq++;
2866
+ const schema = domain === "node" ? this.nodeSchema : this.edgeSchema;
2867
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
2868
+ const key = t.textOf(p);
2869
+ const v = p + 1;
2870
+ if (this.skipsKey(domain, key, v)) {
2871
+ continue;
2872
+ }
2873
+ this.observe(schema, domain, key, v, seq);
2874
+ }
2875
+ }
2876
+ /**
2877
+ * Whether a key of a node or edge block is structural (id, endpoints, weight, original id), not
2878
+ * a column; notes the original id and the weight's kinds on the way.
2879
+ * @param domain - node or edge
2880
+ * @param key - the key
2881
+ * @param v - the value token
2882
+ * @returns true when the key is not a column
2883
+ */
2884
+ skipsKey(domain, key, v) {
2885
+ const { weightFrom, restoreMangledIds } = this.options;
2886
+ if (domain === "node") {
2887
+ if (key === ORIGINAL_ID_KEY && restoreMangledIds) {
2888
+ this.hasOriginalId = true;
2889
+ return true;
2890
+ }
2891
+ return key === "id";
2892
+ }
2893
+ if (key === "source" || key === "target" || key === "directed") {
2894
+ return true;
2895
+ }
2896
+ if (key === weightFrom) {
2897
+ this.weightKinds |= kindBit(this.requireTokens(), v);
2898
+ return true;
2899
+ }
2900
+ return false;
2901
+ }
2902
+ /**
2903
+ * Record one value of one key in a table's schema.
2904
+ * @param schema - the table's schema
2905
+ * @param domain - the table
2906
+ * @param key - the key
2907
+ * @param v - the value token
2908
+ * @param seq - the element sequence number
2909
+ */
2910
+ observe(schema, domain, key, v, seq) {
2911
+ const t = this.requireTokens();
2912
+ this.noteStructure(domain, key, v);
2913
+ let entry = schema.get(key);
2914
+ const graphics = domain === "node" && key === "graphics" && this.positions;
2915
+ if (entry === void 0) {
2916
+ entry = {
2917
+ key,
2918
+ kinds: 0,
2919
+ list: false,
2920
+ lastSeq: seq,
2921
+ wide: false,
2922
+ unsafe: false,
2923
+ dict: this.dictionaries && roleOf(domain, key) === null ? new DictHeuristic() : null,
2924
+ position: false,
2925
+ rest: false
2926
+ };
2927
+ schema.set(key, entry);
2928
+ } else if (entry.lastSeq === seq) {
2929
+ entry.list = true;
2930
+ entry.rest ||= graphics;
2931
+ } else {
2932
+ entry.lastSeq = seq;
2933
+ }
2934
+ if (graphics && t.kind[v] !== TOKEN_OPEN) {
2935
+ entry.rest = true;
2936
+ }
2937
+ switch (t.kind[v]) {
2938
+ case TOKEN_STRING: {
2939
+ const raw = t.textOf(v);
2940
+ if (raw === LIST_START_MARKER || !isLabel(domain, key) && (raw === EMPTY_LIST_TEXT || raw === EMPTY_TUPLE_TEXT)) {
2941
+ entry.list = true;
2942
+ return;
2943
+ }
2944
+ entry.kinds |= KIND_STRING;
2945
+ if (entry.dict !== null && !entry.dict.decided) {
2946
+ entry.dict.observe(raw);
2947
+ }
2948
+ return;
2949
+ }
2950
+ case TOKEN_INT: {
2951
+ entry.kinds |= KIND_INT;
2952
+ const n = Number(t.textOf(v));
2953
+ if (n < I32_MIN || n > I32_MAX) {
2954
+ entry.wide = true;
2955
+ if (!Number.isSafeInteger(n)) {
2956
+ entry.unsafe = true;
2957
+ }
2958
+ }
2959
+ return;
2960
+ }
2961
+ case TOKEN_OPEN:
2962
+ entry.kinds |= KIND_RECORD;
2963
+ if (domain === "node" && key === "graphics" && this.positions) {
2964
+ this.inspectGraphics(entry, v);
2965
+ }
2966
+ return;
2967
+ default: {
2968
+ entry.kinds |= KIND_REAL;
2969
+ const text = t.textOf(v);
2970
+ if (/\d/.test(text) && !Number.isFinite(Number(text))) {
2971
+ this.report.warnOnce(
2972
+ "precision",
2973
+ PRECISION_CODE,
2974
+ `${domain} key "${key}" holds the real ${text}, beyond the f64 range; it is stored as ${numberOfText(text) > 0 ? "" : "-"}Infinity`,
2975
+ { line: t.line[v], element: key },
2976
+ `${PRECISION_CODE}:overflow:${domain}:${key}`
2977
+ );
2978
+ }
2979
+ }
2980
+ }
2981
+ }
2982
+ /**
2983
+ * Warn about keys of a node or edge whose meaning GML readers give but this import does not:
2984
+ * a nested graph / node / edge record, and yEd's group hierarchy.
2985
+ * @param domain - the table
2986
+ * @param key - the key
2987
+ * @param v - the value token
2988
+ */
2989
+ noteStructure(domain, key, v) {
2990
+ const t = this.requireTokens();
2991
+ if (domain === "graph") {
2992
+ return;
2993
+ }
2994
+ if ((key === "graph" || key === "node" || key === "edge") && t.kind[v] === TOKEN_OPEN) {
2995
+ this.report.warnOnce(
2996
+ "unsupported",
2997
+ NESTED_ELEMENT_CODE,
2998
+ `a nested ${key} record inside a ${domain} is kept as the json column "${key}"; it is not read as structure`,
2999
+ { line: t.line[v], element: key },
3000
+ `${NESTED_ELEMENT_CODE}:${domain}:${key}`
3001
+ );
3002
+ } else if (domain === "node" && (key === "isGroup" || key === "gid")) {
3003
+ this.report.warnOnce(
3004
+ "unsupported",
3005
+ GROUPS_CODE,
3006
+ "yEd group keys (isGroup, gid) are kept as plain node columns; the group hierarchy is not read as containment",
3007
+ { line: t.line[v], element: key }
3008
+ );
3009
+ }
3010
+ }
3011
+ /**
3012
+ * Note whether a node's graphics record carries a position (numeric x and y) and whether
3013
+ * anything besides the position remains.
3014
+ * @param entry - the graphics schema entry
3015
+ * @param open - the record's `[`
3016
+ */
3017
+ inspectGraphics(entry, open) {
3018
+ const t = this.requireTokens();
3019
+ const close = t.match[open];
3020
+ let x = false;
3021
+ let y = false;
3022
+ let other = false;
3023
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
3024
+ const key = t.textOf(p);
3025
+ const numeric = t.kind[p + 1] === TOKEN_INT || t.kind[p + 1] === TOKEN_REAL;
3026
+ if (key === "x" && numeric && !x) {
3027
+ x = true;
3028
+ } else if (key === "y" && numeric && !y) {
3029
+ y = true;
3030
+ } else if (!(key === "z" && numeric)) {
3031
+ other = true;
3032
+ }
3033
+ }
3034
+ const positioned = x && y;
3035
+ entry.position ||= positioned;
3036
+ entry.rest ||= positioned ? other || this.keepPositionSource : true;
3037
+ }
3038
+ // ============================================================ pass 2: the push
3039
+ /** Declare what the schema needs, set the header and push every element. */
3040
+ push() {
3041
+ const { sink, report, options } = this;
3042
+ const t = this.requireTokens();
3043
+ sink.reserve(this.nodeCount, this.edgeCount);
3044
+ this.buildPlans();
3045
+ this.setMetadata();
3046
+ this.headerDirected = this.readFlag(this.directedToken, "directed") ?? options.defaultDirected;
3047
+ const resolver = new DirectionResolver(sink, report, options.onMixedDirection);
3048
+ resolver.setHeader(this.headerDirected, {
3049
+ line: this.directedToken >= 0 ? t.line[this.directedToken] : t.line[this.graphOpen]
3050
+ });
3051
+ this.resolver = resolver;
3052
+ if (options.nodeIdFrom !== "id" || this.hasOriginalId) {
3053
+ this.idMap = /* @__PURE__ */ new Map();
3054
+ }
3055
+ if (options.nodeIdFrom !== "id") {
3056
+ report.loss(
3057
+ ID_DROPPED_CODE,
3058
+ `node ids taken from the ${options.nodeIdFrom}; the integer id keys are not kept as a column`,
3059
+ null,
3060
+ this.nodeCount
3061
+ );
3062
+ }
3063
+ this.pushTopLevel();
3064
+ this.pushNodes();
3065
+ this.pushEdges();
3066
+ }
3067
+ /** Turn every schema entry into a column plan. */
3068
+ buildPlans() {
3069
+ for (const [key, entry] of this.nodeSchema) {
3070
+ this.nodePlans.set(key, this.planOf("node", entry));
3071
+ }
3072
+ for (const [key, entry] of this.edgeSchema) {
3073
+ this.edgePlans.set(key, this.planOf("edge", entry));
3074
+ }
3075
+ for (const [key, entry] of this.graphSchema) {
3076
+ this.graphPlans.set(key, this.planOf("graph", entry));
3077
+ }
3078
+ for (const [key, entry] of this.topSchema) {
3079
+ this.topPlans.set(key, this.planOf("graph", entry, true));
3080
+ }
3081
+ }
3082
+ /**
3083
+ * Warn about a key that mixes numbers and strings, or holds integers beyond 2^53.
3084
+ * @param domain - the table
3085
+ * @param entry - the schema entry
3086
+ * @param asText - whether the integers are kept as text
3087
+ */
3088
+ warnKinds(domain, entry, asText) {
3089
+ if ((entry.kinds & KIND_STRING) !== 0 && (entry.kinds & (KIND_INT | KIND_REAL)) !== 0 && (entry.kinds & KIND_RECORD) === 0) {
3090
+ this.report.warnOnce(
3091
+ "coercion",
3092
+ WIDENED_CODE,
3093
+ `${domain} key "${entry.key}" mixes numbers and strings; the column is string and the numbers are kept as written`,
3094
+ { element: entry.key },
3095
+ `${WIDENED_CODE}:${domain}:${entry.key}`
3096
+ );
3097
+ }
3098
+ if (entry.unsafe && !asText) {
3099
+ this.report.warnOnce(
3100
+ "precision",
3101
+ PRECISION_CODE,
3102
+ `${domain} key "${entry.key}" holds integers beyond 2^53; they are stored as the nearest f64 (pass long: "string" to keep every digit as text)`,
3103
+ { element: entry.key },
3104
+ `${PRECISION_CODE}:${domain}:${entry.key}`
3105
+ );
3106
+ }
3107
+ }
3108
+ /**
3109
+ * The plan of a node `graphics` record: the plan plus its position and rest columns.
3110
+ * @param plan - the record's plan
3111
+ * @param entry - the schema entry
3112
+ * @returns the plan with its position (and rest) plans
3113
+ */
3114
+ graphicsPlan(plan, entry) {
3115
+ const taken = (candidate) => this.nodeSchema.has(candidate) || this.sink.nodeColumn(candidate) !== INVALID_INDEX;
3116
+ const position = {
3117
+ ...plan,
3118
+ name: uniqueColumnName("position", "graphics", taken),
3119
+ dtype: "f64",
3120
+ itemDtype: null,
3121
+ kind: "real",
3122
+ list: false,
3123
+ role: "position"
3124
+ };
3125
+ const rest = entry.rest ? { ...plan, dtype: "json", itemDtype: null, kind: "record", list: false } : null;
3126
+ return { ...plan, position, rest };
3127
+ }
3128
+ /**
3129
+ * The plan of one schema entry.
3130
+ * @param domain - the table
3131
+ * @param entry - the schema entry
3132
+ * @param top - whether the key is a top-level one (graph table)
3133
+ * @returns the plan
3134
+ */
3135
+ planOf(domain, entry, top = false) {
3136
+ const asText = entry.unsafe && this.options.long === "string" && kindOf(entry) === "int";
3137
+ const kind = asText ? "string" : kindOf(entry);
3138
+ let scalar = dtypeOfKind(kind, entry);
3139
+ if (scalar === "string" && !entry.list && entry.dict !== null && entry.kinds === KIND_STRING && entry.dict.decide() === "dict") {
3140
+ scalar = "dict";
3141
+ }
3142
+ this.warnKinds(domain, entry, asText);
3143
+ const name = domain === "graph" ? this.uniqueGraphName(entry.key) : entry.key;
3144
+ let itemDtype = null;
3145
+ if (entry.list) {
3146
+ itemDtype = scalar === "dict" ? "string" : scalar;
3147
+ }
3148
+ const plan = {
3149
+ key: entry.key,
3150
+ name,
3151
+ dtype: entry.list ? "list" : scalar,
3152
+ itemDtype,
3153
+ kind,
3154
+ list: entry.list,
3155
+ role: roleOf(domain, entry.key),
3156
+ top,
3157
+ position: null,
3158
+ rest: null,
3159
+ handle: INVALID_INDEX,
3160
+ declared: false,
3161
+ items: null,
3162
+ seq: Number.NEGATIVE_INFINITY
3163
+ };
3164
+ if (domain === "node" && entry.key === "graphics" && this.positions && (entry.kinds & KIND_RECORD) !== 0) {
3165
+ return this.graphicsPlan(plan, entry);
3166
+ }
3167
+ return plan;
3168
+ }
3169
+ /**
3170
+ * A graph column name not yet used by this import (top-level and in-graph keys share the table).
3171
+ * @param key - the key
3172
+ * @returns the name
3173
+ */
3174
+ uniqueGraphName(key) {
3175
+ const name = uniqueColumnName(key, key, (candidate) => this.graphNames.has(candidate));
3176
+ this.graphNames.add(name);
3177
+ if (name !== key) {
3178
+ this.report.warning(
3179
+ "coercion",
3180
+ COLUMN_RENAMED_CODE_GML,
3181
+ `the top-level key "${key}" and a key of the graph block share the graph column name; the top-level value is stored as "${name}"`,
3182
+ { element: key }
3183
+ );
3184
+ }
3185
+ return name;
3186
+ }
3187
+ /** Set the graph metadata: source format, creator, version, multigraph flag, weight origin. */
3188
+ setMetadata() {
3189
+ const t = this.requireTokens();
3190
+ const { sink, options } = this;
3191
+ const textOf = (v) => t.kind[v] === TOKEN_STRING ? t.stringOf(v) : t.textOf(v);
3192
+ const multigraph = this.readFlag(this.multigraphToken, "multigraph");
3193
+ sink.setMeta({
3194
+ sourceFormat: "gml",
3195
+ creator: this.creatorToken >= 0 ? textOf(this.creatorToken) : void 0,
3196
+ sourceVersion: this.versionToken >= 0 ? textOf(this.versionToken) : void 0,
3197
+ declaredMultigraph: multigraph ?? void 0,
3198
+ weightOrigin: this.weightKinds !== 0 && options.weightFrom !== null ? {
3199
+ format: "gml",
3200
+ id: options.weightFrom,
3201
+ title: null,
3202
+ type: this.weightKinds === KIND_INT ? "int" : "real",
3203
+ namespace: null
3204
+ } : void 0,
3205
+ // The header as written, so a reader can say which words set the direction and tell
3206
+ // `directed 0` from a file that relies on the specification's default.
3207
+ extra: this.directedToken >= 0 ? { gml: { directed: textOf(this.directedToken) } } : void 0
3208
+ });
3209
+ }
3210
+ /**
3211
+ * Read a `directed` / `multigraph` flag token.
3212
+ * @param v - the value token, or -1 when absent
3213
+ * @param name - the flag name, for issues
3214
+ * @returns the flag, or null when absent or not an integer
3215
+ */
3216
+ readFlag(v, name) {
3217
+ if (v < 0) {
3218
+ return null;
3219
+ }
3220
+ const t = this.requireTokens();
3221
+ const quoted = t.kind[v] === TOKEN_STRING && QUOTED_INT.test(t.stringOf(v));
3222
+ if (quoted) {
3223
+ this.report.warning(
3224
+ "validation-error",
3225
+ FLAG_VALUE_CODE,
3226
+ `flag "${name}" is written as the string ${describeValue(t, v)}; read as an integer`,
3227
+ { line: t.line[v], element: name }
3228
+ );
3229
+ } else if (t.kind[v] !== TOKEN_INT) {
3230
+ this.report.error(
3231
+ "validation-error",
3232
+ FLAG_TYPE_CODE,
3233
+ `flag "${name}" must be the integer 0 or 1, found ${describeValue(t, v)}`,
3234
+ { line: t.line[v], element: name }
3235
+ );
3236
+ return null;
3237
+ }
3238
+ const n = Number(quoted ? t.stringOf(v) : t.textOf(v));
3239
+ if (n !== 0 && n !== 1) {
3240
+ this.report.warning(
3241
+ "validation-error",
3242
+ FLAG_VALUE_CODE,
3243
+ `flag "${name}" is ${n}; read as ${n !== 0 ? "1" : "0"}`,
3244
+ { line: t.line[v], element: name }
3245
+ );
3246
+ }
3247
+ return n !== 0;
3248
+ }
3249
+ /** Push the top-level attributes (every top-level key but graph, Creator and Version). */
3250
+ pushTopLevel() {
3251
+ const t = this.requireTokens();
3252
+ for (let p = 0; p < t.count; p = t.nextPair(p)) {
3253
+ const v = p + 1;
3254
+ if (v === this.graphOpen || v === this.creatorToken || v === this.versionToken) {
3255
+ continue;
3256
+ }
3257
+ const plan = this.topPlans.get(t.textOf(p));
3258
+ if (plan !== void 0) {
3259
+ this.writeGraph(plan, v, TOP_SEQ);
3260
+ }
3261
+ }
3262
+ this.flushGraphLists();
3263
+ }
3264
+ /** Push every node block and the graph block's own attributes, in file order. */
3265
+ pushNodes() {
3266
+ const t = this.requireTokens();
3267
+ const close = t.match[this.graphOpen];
3268
+ for (let p = this.graphOpen + 1; p < close; p = t.nextPair(p)) {
3269
+ const key = t.textOf(p);
3270
+ const v = p + 1;
3271
+ switch (key) {
3272
+ case "node":
3273
+ if (t.kind[v] === TOKEN_OPEN) {
3274
+ this.importNode(v);
3275
+ } else {
3276
+ this.badElement("node", p);
3277
+ this.report.counts.skippedNodes++;
3278
+ }
3279
+ break;
3280
+ case "edge":
3281
+ break;
3282
+ case "directed":
3283
+ case "multigraph":
3284
+ if (v !== this.directedToken && v !== this.multigraphToken) {
3285
+ this.report.warning(
3286
+ "validation-error",
3287
+ FLAG_VALUE_CODE,
3288
+ `graph flag "${key}" repeated; the first value stands`,
3289
+ { line: t.line[p], element: key }
3290
+ );
3291
+ }
3292
+ break;
3293
+ default: {
3294
+ const plan = this.graphPlans.get(key);
3295
+ if (plan !== void 0) {
3296
+ this.writeGraph(plan, v, GRAPH_SEQ);
3297
+ }
3298
+ }
3299
+ }
3300
+ }
3301
+ this.flushGraphLists();
3302
+ }
3303
+ /** Push every edge block, in file order. */
3304
+ pushEdges() {
3305
+ const t = this.requireTokens();
3306
+ const close = t.match[this.graphOpen];
3307
+ let ordinal = 0;
3308
+ for (let p = this.graphOpen + 1; p < close; p = t.nextPair(p)) {
3309
+ if (t.textOf(p) !== "edge") {
3310
+ continue;
3311
+ }
3312
+ const v = p + 1;
3313
+ if (t.kind[v] === TOKEN_OPEN) {
3314
+ this.importEdge(v, ordinal);
3315
+ } else {
3316
+ this.badElement("edge", p);
3317
+ this.report.counts.skippedEdges++;
3318
+ }
3319
+ ordinal++;
3320
+ }
3321
+ }
3322
+ /**
3323
+ * Record a `node` / `edge` key whose value is not a block.
3324
+ * @param what - node or edge
3325
+ * @param p - the key token
3326
+ */
3327
+ badElement(what, p) {
3328
+ const t = this.requireTokens();
3329
+ this.report.error(
3330
+ "validation-error",
3331
+ ELEMENT_TYPE_CODE,
3332
+ `${what} must be a [ ... ] block, found ${describeValue(t, p + 1)}`,
3333
+ { line: t.line[p], element: what }
3334
+ );
3335
+ }
3336
+ /**
3337
+ * Push one node block.
3338
+ * @param open - the block's `[`
3339
+ */
3340
+ importNode(open) {
3341
+ const t = this.requireTokens();
3342
+ const { sink, report, options } = this;
3343
+ const close = t.match[open];
3344
+ const line = t.line[open];
3345
+ const ordinal = this.nodeOrdinal++;
3346
+ this.checkAbort();
3347
+ let index;
3348
+ let sinkId;
3349
+ let fileId;
3350
+ try {
3351
+ let idTok = -1;
3352
+ let labelTok = -1;
3353
+ let originalTok = -1;
3354
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
3355
+ const key = t.textOf(p);
3356
+ if (key === "id") {
3357
+ idTok = this.structuralToken(idTok, p, "id");
3358
+ } else if (key === "label") {
3359
+ labelTok = labelTok < 0 ? p + 1 : labelTok;
3360
+ } else if (key === ORIGINAL_ID_KEY && options.restoreMangledIds) {
3361
+ originalTok = originalTok < 0 ? p + 1 : originalTok;
3362
+ }
3363
+ }
3364
+ if (idTok < 0) {
3365
+ throw new GraphFormatError("E_INVALID_ID", "node has no id", { reason: "missing id" });
3366
+ }
3367
+ fileId = this.coerceFileId(idTok, this.fileIdOf(idTok, "id"));
3368
+ if (originalTok >= 0) {
3369
+ sinkId = this.restoredId(originalTok);
3370
+ } else {
3371
+ switch (options.nodeIdFrom) {
3372
+ case "label":
3373
+ if (labelTok < 0) {
3374
+ throw new GraphFormatError("E_INVALID_ID", "node has no label", {
3375
+ reason: "missing label"
3376
+ });
3377
+ }
3378
+ sinkId = this.coerceValueToken(labelTok);
3379
+ break;
3380
+ case "index":
3381
+ sinkId = ordinal;
3382
+ break;
3383
+ default:
3384
+ sinkId = fileId;
3385
+ }
3386
+ }
3387
+ index = sink.addNode(sinkId);
3388
+ if (this.idMap !== null) {
3389
+ const before = this.idMap.get(fileId);
3390
+ if (before !== void 0 && before !== sinkId) {
3391
+ report.warning(
3392
+ "merged",
3393
+ DUPLICATE_NODE_CODE,
3394
+ `file id ${JSON.stringify(fileId)} is declared twice (nodes ${JSON.stringify(before)} and ${JSON.stringify(sinkId)}); edges to it resolve to the later`,
3395
+ { line, element: String(fileId) }
3396
+ );
3397
+ }
3398
+ this.idMap.set(fileId, sinkId);
3399
+ }
3400
+ } catch (err) {
3401
+ this.recordNodeError(err, line);
3402
+ report.counts.skippedNodes++;
3403
+ if (fileId !== void 0) {
3404
+ this.skippedIds.add(fileId);
3405
+ }
3406
+ return;
3407
+ }
3408
+ if (this.isDeclared(index)) {
3409
+ report.warning(
3410
+ "merged",
3411
+ DUPLICATE_NODE_CODE,
3412
+ `node ${JSON.stringify(sinkId)} is declared twice; later keys overwrite`,
3413
+ { line, element: String(sinkId) }
3414
+ );
3415
+ } else {
3416
+ this.markDeclared(index);
3417
+ report.counts.nodes++;
3418
+ }
3419
+ this.beginElement(this.seq++);
3420
+ try {
3421
+ const graphics = [];
3422
+ let graphicsPlan = null;
3423
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
3424
+ const key = t.textOf(p);
3425
+ if (key === "id" || key === ORIGINAL_ID_KEY && options.restoreMangledIds) {
3426
+ continue;
3427
+ }
3428
+ const plan = this.nodePlans.get(key);
3429
+ if (plan !== void 0 && plan.position !== null) {
3430
+ graphicsPlan = plan;
3431
+ graphics.push(p + 1);
3432
+ } else if (plan !== void 0) {
3433
+ this.writeNode(plan, index, p + 1);
3434
+ }
3435
+ }
3436
+ if (graphicsPlan !== null) {
3437
+ this.writeGraphics(graphicsPlan, index, graphics, {
3438
+ line: t.line[graphics[0]],
3439
+ element: String(sinkId)
3440
+ });
3441
+ }
3442
+ this.flushLists("node", index);
3443
+ } catch (err) {
3444
+ this.touched.length = 0;
3445
+ report.recordError(err, { line, element: String(sinkId) });
3446
+ }
3447
+ }
3448
+ /**
3449
+ * Push one edge block.
3450
+ * @param open - the block's `[`
3451
+ * @param ordinal - the edge's ordinal among edge blocks, for messages
3452
+ */
3453
+ importEdge(open, ordinal) {
3454
+ const t = this.requireTokens();
3455
+ const { sink, report, options } = this;
3456
+ const close = t.match[open];
3457
+ const line = t.line[open];
3458
+ const element = `edge #${ordinal}`;
3459
+ this.checkAbort();
3460
+ let edge;
3461
+ try {
3462
+ let sourceTok = -1;
3463
+ let targetTok = -1;
3464
+ let weightTok = -1;
3465
+ let directedTok = -1;
3466
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
3467
+ const key = t.textOf(p);
3468
+ if (key === "source") {
3469
+ sourceTok = this.structuralToken(sourceTok, p, "source");
3470
+ } else if (key === "target") {
3471
+ targetTok = this.structuralToken(targetTok, p, "target");
3472
+ } else if (key === "directed") {
3473
+ directedTok = this.structuralToken(directedTok, p, "directed");
3474
+ } else if (key === options.weightFrom) {
3475
+ weightTok = p + 1;
3476
+ }
3477
+ }
3478
+ if (sourceTok < 0 || targetTok < 0) {
3479
+ throw new GraphFormatError("E_UNKNOWN_NODE", `edge has no ${sourceTok < 0 ? "source" : "target"}`, {
3480
+ reason: "missing endpoint"
3481
+ });
3482
+ }
3483
+ const source = this.endpoint(sourceTok, "source");
3484
+ const target = this.endpoint(targetTok, "target");
3485
+ const weight = weightTok >= 0 ? this.weightOf(weightTok) : void 0;
3486
+ const before = sink.edgeCount;
3487
+ const sourceNew = sink.indexOf(source) === INVALID_INDEX;
3488
+ const targetNew = source !== target && sink.indexOf(target) === INVALID_INDEX;
3489
+ const directed = this.readFlag(directedTok, "directed") ?? this.headerDirected;
3490
+ edge = this.requireResolver().addEdge(source, target, directed ? "directed" : "undirected", weight, {
3491
+ line,
3492
+ element
3493
+ });
3494
+ report.counts.edges += sink.edgeCount - before;
3495
+ report.counts.nodes += (sourceNew ? 1 : 0) + (targetNew ? 1 : 0);
3496
+ } catch (err) {
3497
+ this.recordEdgeError(err, line, element);
3498
+ report.counts.skippedEdges++;
3499
+ return;
3500
+ }
3501
+ this.beginElement(this.seq++);
3502
+ try {
3503
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
3504
+ const key = t.textOf(p);
3505
+ if (key === "source" || key === "target" || key === "directed" || key === options.weightFrom) {
3506
+ continue;
3507
+ }
3508
+ const plan = this.edgePlans.get(key);
3509
+ if (plan !== void 0) {
3510
+ this.writeEdge(plan, edge, p + 1);
3511
+ }
3512
+ }
3513
+ this.flushLists("edge", edge);
3514
+ } catch (err) {
3515
+ this.touched.length = 0;
3516
+ report.recordError(err, { line, element });
3517
+ }
3518
+ }
3519
+ // ============================================================ ids and weights
3520
+ /**
3521
+ * Note a structural key token, refusing a repeat.
3522
+ * @param previous - the token seen before, or -1
3523
+ * @param p - the key token
3524
+ * @param key - the key name
3525
+ * @returns the value token
3526
+ */
3527
+ structuralToken(previous, p, key) {
3528
+ if (previous >= 0) {
3529
+ throw new GraphFormatError("E_INVALID_ID", `key "${key}" appears twice in one block`, {
3530
+ reason: "duplicate key",
3531
+ key
3532
+ });
3533
+ }
3534
+ return p + 1;
3535
+ }
3536
+ /**
3537
+ * The value of an id / source / target token: an integer (the GML spec's type) as a number, a
3538
+ * string (NetworkX and Gephi write them) as its text with one warning per file.
3539
+ * @param v - the value token
3540
+ * @param key - the key name, for the error
3541
+ * @returns the number or the string
3542
+ */
3543
+ fileIdOf(v, key) {
3544
+ const t = this.requireTokens();
3545
+ if (t.kind[v] === TOKEN_STRING) {
3546
+ this.report.warnOnce(
3547
+ "validation-error",
3548
+ STRING_ID_CODE,
3549
+ `${key} is the string "${t.textOf(v)}"; GML ids are integers, string ids are kept under the ids rule`,
3550
+ { line: t.line[v], element: t.stringOf(v) }
3551
+ );
3552
+ return t.stringOf(v);
3553
+ }
3554
+ if (t.kind[v] !== TOKEN_INT) {
3555
+ throw new GraphFormatError(
3556
+ "E_INVALID_ID",
3557
+ `${key} must be an integer or a string, found ${describeValue(t, v)}`,
3558
+ {
3559
+ reason: "not an integer",
3560
+ key,
3561
+ value: t.kind[v] === TOKEN_OPEN ? "[...]" : t.textOf(v)
3562
+ }
3563
+ );
3564
+ }
3565
+ return Number(t.textOf(v));
3566
+ }
3567
+ /**
3568
+ * Coerce an id / source / target value under the `ids` rule: a string by its text, an integer
3569
+ * through coerceInteger().
3570
+ * @param v - the value token
3571
+ * @param id - its value (fileIdOf())
3572
+ * @returns the id
3573
+ */
3574
+ coerceFileId(v, id) {
3575
+ return typeof id === "string" ? this.coercer.text(id) : this.coerceInteger(v, id);
3576
+ }
3577
+ /**
3578
+ * Coerce an integer id token under the `ids` rule: a safe integer passes as a typed number
3579
+ * (so `01` is 1 and `-0` is 0), a larger one goes through the text rule (a string under
3580
+ * "canonical"), and "string" keeps the text as written.
3581
+ * @param v - the value token
3582
+ * @param n - its numeric value
3583
+ * @returns the id
3584
+ */
3585
+ coerceInteger(v, n) {
3586
+ if (Number.isSafeInteger(n) && this.coercer.mode !== "string") {
3587
+ return this.coercer.value(n);
3588
+ }
3589
+ return this.coercer.text(this.requireTokens().textOf(v));
3590
+ }
3591
+ /**
3592
+ * Coerce a label token under the `ids` rule: a string by its text, a number as a typed value.
3593
+ * @param v - the value token
3594
+ * @returns the id
3595
+ */
3596
+ coerceValueToken(v) {
3597
+ const t = this.requireTokens();
3598
+ switch (t.kind[v]) {
3599
+ case TOKEN_STRING:
3600
+ return this.coercer.text(t.stringOf(v));
3601
+ case TOKEN_INT:
3602
+ return this.coerceInteger(v, Number(t.textOf(v)));
3603
+ case TOKEN_OPEN:
3604
+ throw new GraphFormatError("E_INVALID_ID", "label must be a string or a number, found a record", {
3605
+ reason: "record label"
3606
+ });
3607
+ default:
3608
+ return this.coercer.value(numberOfText(t.textOf(v)));
3609
+ }
3610
+ }
3611
+ /**
3612
+ * The id a `graphty_originalId` key restores: typed, never coerced (the exporter wrote it exactly).
3613
+ * @param v - the value token
3614
+ * @returns the original id
3615
+ */
3616
+ restoredId(v) {
3617
+ const t = this.requireTokens();
3618
+ switch (t.kind[v]) {
3619
+ case TOKEN_STRING:
3620
+ return t.stringOf(v);
3621
+ case TOKEN_OPEN:
3622
+ throw new GraphFormatError("E_INVALID_ID", `${ORIGINAL_ID_KEY} must be a string or a number`, {
3623
+ reason: "record original id"
3624
+ });
3625
+ default:
3626
+ return numberOfText(t.textOf(v));
3627
+ }
3628
+ }
3629
+ /**
3630
+ * Resolve a source / target token to a sink id.
3631
+ * @param v - the value token
3632
+ * @param key - source or target
3633
+ * @returns the id
3634
+ */
3635
+ endpoint(v, key) {
3636
+ const id = this.coerceFileId(v, this.fileIdOf(v, key));
3637
+ const mapped = this.idMap?.get(id);
3638
+ if (mapped !== void 0) {
3639
+ return mapped;
3640
+ }
3641
+ if (this.skippedIds.has(id)) {
3642
+ throw new GraphFormatError(
3643
+ "E_UNKNOWN_NODE",
3644
+ `${key} ${JSON.stringify(id)} names a node block that was skipped after an error`,
3645
+ { reason: "skipped node", id }
3646
+ );
3647
+ }
3648
+ return id;
3649
+ }
3650
+ /**
3651
+ * The weight of an edge from its weight token.
3652
+ * @param v - the value token
3653
+ * @returns the weight; E_INVALID_WEIGHT for a record or non-numeric text
3654
+ */
3655
+ weightOf(v) {
3656
+ const t = this.requireTokens();
3657
+ switch (t.kind[v]) {
3658
+ case TOKEN_STRING:
3659
+ return parseWeightText(t.stringOf(v), this.report);
3660
+ case TOKEN_OPEN:
3661
+ throw new GraphFormatError("E_INVALID_WEIGHT", "edge weight is a record", { value: "[...]" });
3662
+ default: {
3663
+ const text = t.textOf(v);
3664
+ const weight = weightFromValue(numberOfText(text));
3665
+ if (weight !== void 0) {
3666
+ reportWeightPrecision(text, weight, this.report);
3667
+ }
3668
+ return weight;
3669
+ }
3670
+ }
3671
+ }
3672
+ /**
3673
+ * Record a node-level error under the GML code it implies.
3674
+ * @param err - the thrown value
3675
+ * @param line - the block's line
3676
+ */
3677
+ recordNodeError(err, line) {
3678
+ const { report } = this;
3679
+ if (err instanceof GraphFormatError && err.code === "E_INVALID_ID") {
3680
+ const { reason } = err.details;
3681
+ switch (reason) {
3682
+ case "missing id":
3683
+ report.error("missing-value", MISSING_ID_CODE, err.message, { line, element: "node" });
3684
+ return;
3685
+ case "missing label":
3686
+ report.error("missing-value", MISSING_LABEL_CODE, err.message, { line, element: "node" });
3687
+ return;
3688
+ case "duplicate key":
3689
+ report.error("validation-error", REPEATED_KEY_CODE, err.message, { line, element: "node" });
3690
+ return;
3691
+ case "not an integer":
3692
+ report.error("validation-error", ID_TYPE_CODE, err.message, { line, element: "node" });
3693
+ return;
3694
+ }
3695
+ }
3696
+ report.recordError(err, { line, element: "node" });
3697
+ }
3698
+ /**
3699
+ * Record an edge-level error under the GML code it implies.
3700
+ * @param err - the thrown value
3701
+ * @param line - the block's line
3702
+ * @param element - the edge description
3703
+ */
3704
+ recordEdgeError(err, line, element) {
3705
+ const { report } = this;
3706
+ if (err instanceof GraphFormatError) {
3707
+ const { reason } = err.details;
3708
+ if (err.code === "E_UNKNOWN_NODE" && reason === "missing endpoint") {
3709
+ report.error("missing-value", MISSING_ENDPOINT_CODE, err.message, { line, element });
3710
+ return;
3711
+ }
3712
+ if (err.code === "E_INVALID_ID" && reason === "duplicate key") {
3713
+ report.error("validation-error", REPEATED_KEY_CODE, err.message, { line, element });
3714
+ return;
3715
+ }
3716
+ if (err.code === "E_INVALID_ID" && reason === "not an integer") {
3717
+ report.error("validation-error", ID_TYPE_CODE, err.message, { line, element });
3718
+ return;
3719
+ }
3720
+ }
3721
+ report.recordError(err, { line, element });
3722
+ }
3723
+ // ============================================================ values
3724
+ /**
3725
+ * Write one node value.
3726
+ * @param plan - the key's plan
3727
+ * @param index - the node index
3728
+ * @param v - the value token
3729
+ */
3730
+ writeNode(plan, index, v) {
3731
+ if (plan.list) {
3732
+ this.collectItem(plan, v, this.seqNow, this.touched);
3733
+ return;
3734
+ }
3735
+ this.sink.setNodeValue(this.handleOf(plan, "node"), index, this.scalarOf(plan, v));
3736
+ }
3737
+ /**
3738
+ * Write one edge value.
3739
+ * @param plan - the key's plan
3740
+ * @param edge - the edge index
3741
+ * @param v - the value token
3742
+ */
3743
+ writeEdge(plan, edge, v) {
3744
+ if (plan.list) {
3745
+ this.collectItem(plan, v, this.seqNow, this.touched);
3746
+ return;
3747
+ }
3748
+ this.sink.setEdgeValue(this.handleOf(plan, "edge"), edge, this.scalarOf(plan, v));
3749
+ }
3750
+ /**
3751
+ * Write one graph value.
3752
+ * @param plan - the key's plan
3753
+ * @param v - the value token
3754
+ * @param seq - TOP_SEQ or GRAPH_SEQ
3755
+ */
3756
+ writeGraph(plan, v, seq) {
3757
+ if (plan.list) {
3758
+ this.collectItem(plan, v, seq, this.graphTouched);
3759
+ return;
3760
+ }
3761
+ const value = this.scalarOf(plan, v);
3762
+ this.sink.setGraphValue(plan.name, value, graphDecl(plan));
3763
+ if (seq === GRAPH_SEQ && plan.key === "name" && typeof value === "string") {
3764
+ this.sink.setMeta({ name: value });
3765
+ }
3766
+ }
3767
+ /**
3768
+ * Write a node's graphics values: the position column from the x / y / z of the first record,
3769
+ * everything else (the remaining keys, a value that is not a record, a repeated record) to the
3770
+ * json column, with a warning when the value cannot give a position as written.
3771
+ * @param plan - the graphics plan
3772
+ * @param index - the node index
3773
+ * @param vs - the value tokens of the node's graphics keys, in order
3774
+ * @param where - the line and the node, for issues
3775
+ */
3776
+ writeGraphics(plan, index, vs, where) {
3777
+ const t = this.requireTokens();
3778
+ const { sink } = this;
3779
+ const values = vs.map((v) => t.kind[v] === TOKEN_OPEN ? parseRecord(t, v) : this.scalarOf(plan, v));
3780
+ const first2 = values[0];
3781
+ const positionPlan = plan.position;
3782
+ let rest = first2;
3783
+ if (isRecord(first2) && positionPlan !== null) {
3784
+ const left = this.writePosition(positionPlan, index, this.keepPositionSource ? { ...first2 } : first2, where);
3785
+ rest = this.keepPositionSource ? first2 : left;
3786
+ } else if (!isRecord(first2)) {
3787
+ this.graphicsWarning("scalar", `node ${where.element}'s graphics is not a record`, where);
3788
+ }
3789
+ if (values.length > 1) {
3790
+ this.graphicsWarning(
3791
+ "repeated",
3792
+ `node ${where.element} has ${values.length} graphics records; the position comes from the first`,
3793
+ where
3794
+ );
3795
+ rest = values;
3796
+ }
3797
+ if (rest !== void 0 && plan.rest !== null) {
3798
+ sink.setNodeValue(this.handleOf(plan.rest, "node"), index, rest);
3799
+ }
3800
+ }
3801
+ /**
3802
+ * Write a node's position from the x / y / z of its graphics record, taking them out of it.
3803
+ * @param positionPlan - the position column's plan
3804
+ * @param index - the node index
3805
+ * @param record - the graphics record
3806
+ * @param where - the line and the node, for issues
3807
+ * @returns what is left of the record for the json column, or undefined when nothing is
3808
+ */
3809
+ writePosition(positionPlan, index, record, where) {
3810
+ const { x, y, z } = record;
3811
+ let rest = record;
3812
+ if (typeof x === "number" && typeof y === "number") {
3813
+ const depth = typeof z === "number" ? z : 0;
3814
+ this.sink.setNodeValue(this.handleOf(positionPlan, "node"), index, [x, y, depth]);
3815
+ delete record.x;
3816
+ delete record.y;
3817
+ if (typeof z === "number") {
3818
+ delete record.z;
3819
+ }
3820
+ rest = Object.keys(record).length > 0 ? record : void 0;
3821
+ }
3822
+ if ("x" in record || "y" in record || "z" in record) {
3823
+ this.graphicsWarning(
3824
+ "coordinate",
3825
+ `node ${where.element}'s graphics x / y / z is not one number each`,
3826
+ where
3827
+ );
3828
+ }
3829
+ return rest;
3830
+ }
3831
+ /**
3832
+ * Warn once per kind of graphics problem.
3833
+ * @param kind - the problem
3834
+ * @param what - the message start, naming the node
3835
+ * @param where - the line and the node
3836
+ */
3837
+ graphicsWarning(kind, what, where) {
3838
+ this.report.warnOnce(
3839
+ "validation-error",
3840
+ GRAPHICS_CODE,
3841
+ `${what}; the value is kept in the graphics json column (warned once for every such node)`,
3842
+ where,
3843
+ `${GRAPHICS_CODE}:${kind}`
3844
+ );
3845
+ }
3846
+ /**
3847
+ * Collect one list item.
3848
+ * @param plan - the list plan
3849
+ * @param v - the value token
3850
+ * @param seq - the sequence number of the element the item belongs to
3851
+ * @param touched - the plans touched by that element
3852
+ */
3853
+ collectItem(plan, v, seq, touched) {
3854
+ const t = this.requireTokens();
3855
+ if (plan.seq !== seq) {
3856
+ plan.seq = seq;
3857
+ plan.items = [];
3858
+ touched.push(plan);
3859
+ }
3860
+ if (t.kind[v] === TOKEN_STRING) {
3861
+ const raw = t.textOf(v);
3862
+ if (raw === LIST_START_MARKER || raw === EMPTY_LIST_TEXT || raw === EMPTY_TUPLE_TEXT) {
3863
+ return;
3864
+ }
3865
+ }
3866
+ plan.items.push(this.scalarOf(plan, v));
3867
+ }
3868
+ /**
3869
+ * Start a node or edge: later list items belong to it.
3870
+ * @param seq - the element's sequence number
3871
+ */
3872
+ beginElement(seq) {
3873
+ this.seqNow = seq;
3874
+ this.touched.length = 0;
3875
+ }
3876
+ /**
3877
+ * Write the lists collected for the current node or edge.
3878
+ * @param domain - node or edge
3879
+ * @param row - the row
3880
+ */
3881
+ flushLists(domain, row) {
3882
+ const { sink, touched } = this;
3883
+ for (const plan of touched) {
3884
+ const handle = this.handleOf(plan, domain);
3885
+ if (domain === "node") {
3886
+ sink.setNodeValue(handle, row, plan.items);
3887
+ } else {
3888
+ sink.setEdgeValue(handle, row, plan.items);
3889
+ }
3890
+ plan.items = null;
3891
+ }
3892
+ touched.length = 0;
3893
+ }
3894
+ /** Write the lists collected for the graph table. */
3895
+ flushGraphLists() {
3896
+ const { sink, graphTouched } = this;
3897
+ for (const plan of graphTouched) {
3898
+ sink.setGraphValue(plan.name, plan.items, graphDecl(plan));
3899
+ plan.items = null;
3900
+ }
3901
+ graphTouched.length = 0;
3902
+ }
3903
+ /**
3904
+ * The JS value of a scalar value token (or list item) under a plan's storage class.
3905
+ * @param plan - the plan
3906
+ * @param v - the value token
3907
+ * @returns the value to push
3908
+ */
3909
+ scalarOf(plan, v) {
3910
+ const t = this.requireTokens();
3911
+ const kind = t.kind[v];
3912
+ switch (plan.kind) {
3913
+ case "int":
3914
+ return Number(t.textOf(v));
3915
+ case "real":
3916
+ return numberOfText(t.textOf(v));
3917
+ case "string":
3918
+ return kind === TOKEN_STRING ? t.stringOf(v) : t.textOf(v);
3919
+ default:
3920
+ return kind === TOKEN_STRING ? t.stringOf(v) : scalarOrRecord(t, v);
3921
+ }
3922
+ }
3923
+ /**
3924
+ * The handle of a plan's column, declaring it on first use. A declaration the sink refuses
3925
+ * because a column of the name exists with another declaration falls back to that column
3926
+ * (reported once); a taken role is dropped (reported once).
3927
+ * @param plan - the plan
3928
+ * @param domain - node or edge
3929
+ * @returns the handle
3930
+ */
3931
+ handleOf(plan, domain) {
3932
+ if (plan.declared) {
3933
+ return plan.handle;
3934
+ }
3935
+ plan.declared = true;
3936
+ const { sink, report } = this;
3937
+ const decl = {
3938
+ name: plan.name,
3939
+ dtype: plan.dtype,
3940
+ nullable: true,
3941
+ origin: { format: "gml", id: plan.key, title: null, type: originTypeOf(plan), namespace: null }
3942
+ };
3943
+ if (plan.list) {
3944
+ decl.itemDtype = plan.itemDtype ?? "string";
3945
+ }
3946
+ if (plan.role === "position") {
3947
+ decl.components = 3;
3948
+ }
3949
+ if (plan.role !== null) {
3950
+ decl.role = plan.role;
3951
+ }
3952
+ const resolved = declareResolved(sink, domain, decl, report, { element: plan.name });
3953
+ plan.handle = resolved.handle;
3954
+ if (resolved.renamed) {
3955
+ plan.name = resolved.decl.name;
3956
+ }
3957
+ return plan.handle;
3958
+ }
3959
+ // ============================================================ bookkeeping
3960
+ /**
3961
+ * Whether a node index was declared by a node block of this import.
3962
+ * @param index - the node index
3963
+ * @returns true when declared
3964
+ */
3965
+ isDeclared(index) {
3966
+ const word = index >>> 5;
3967
+ return word < this.declared.length && (this.declared[word] >>> (index & 31) & 1) === 1;
3968
+ }
3969
+ /**
3970
+ * Mark a node index as declared.
3971
+ * @param index - the node index
3972
+ */
3973
+ markDeclared(index) {
3974
+ const word = index >>> 5;
3975
+ if (word >= this.declared.length) {
3976
+ const grown = new Uint32Array(Math.max(word + 1, this.declared.length * 2));
3977
+ grown.set(this.declared);
3978
+ this.declared = grown;
3979
+ }
3980
+ this.declared[word] |= 1 << (index & 31);
3981
+ }
3982
+ /** Check the abort signal every ABORT_CHECK_INTERVAL elements. */
3983
+ checkAbort() {
3984
+ if (++this.elementsSinceCheck >= ABORT_CHECK_INTERVAL) {
3985
+ this.elementsSinceCheck = 0;
3986
+ throwIfAborted(this.options.signal);
3987
+ }
3988
+ }
3989
+ /**
3990
+ * The tokens, which exist after run() lexed the text.
3991
+ * @returns the tokens
3992
+ */
3993
+ requireTokens() {
3994
+ if (this.tokens === null) {
3995
+ throw new GraphFormatError("E_UNSUPPORTED", "the GML text was not lexed", { reason: "no tokens" });
3996
+ }
3997
+ return this.tokens;
3998
+ }
3999
+ /**
4000
+ * The direction resolver, which exists once the header was set.
4001
+ * @returns the resolver
4002
+ */
4003
+ requireResolver() {
4004
+ if (this.resolver === null) {
4005
+ throw new GraphFormatError("E_DIRECTED", "the header was not set", { reason: "no resolver" });
4006
+ }
4007
+ return this.resolver;
4008
+ }
4009
+ }
4010
+ function kindBit(t, v) {
4011
+ switch (t.kind[v]) {
4012
+ case TOKEN_INT:
4013
+ return KIND_INT;
4014
+ case TOKEN_STRING:
4015
+ return KIND_STRING;
4016
+ case TOKEN_OPEN:
4017
+ return KIND_RECORD;
4018
+ default:
4019
+ return KIND_REAL;
4020
+ }
4021
+ }
4022
+ function kindOf(entry) {
4023
+ if ((entry.kinds & KIND_RECORD) !== 0) {
4024
+ return "record";
4025
+ }
4026
+ if ((entry.kinds & KIND_STRING) !== 0) {
4027
+ return "string";
4028
+ }
4029
+ if ((entry.kinds & KIND_REAL) !== 0) {
4030
+ return "real";
4031
+ }
4032
+ return "int";
4033
+ }
4034
+ function dtypeOfKind(kind, entry) {
4035
+ switch (kind) {
4036
+ case "int":
4037
+ return entry.wide ? "f64" : "i32";
4038
+ case "real":
4039
+ return "f64";
4040
+ case "string":
4041
+ return "string";
4042
+ default:
4043
+ return "json";
4044
+ }
4045
+ }
4046
+ function originTypeOf(plan) {
4047
+ return plan.kind;
4048
+ }
4049
+ function graphDecl(plan) {
4050
+ const decl = {
4051
+ name: plan.name,
4052
+ dtype: plan.dtype,
4053
+ nullable: true,
4054
+ origin: { format: "gml", id: plan.key, title: null, type: originTypeOf(plan), namespace: null }
4055
+ };
4056
+ if (plan.list) {
4057
+ decl.itemDtype = plan.itemDtype ?? "string";
4058
+ }
4059
+ if (plan.top) {
4060
+ decl.extra = { gmlTopLevel: true };
4061
+ }
4062
+ return decl;
4063
+ }
4064
+ function describeValue(t, v) {
4065
+ switch (t.kind[v]) {
4066
+ case TOKEN_OPEN:
4067
+ return "a record";
4068
+ case TOKEN_STRING:
4069
+ return `the string "${t.textOf(v)}"`;
4070
+ case TOKEN_REAL:
4071
+ return `the real ${t.textOf(v)}`;
4072
+ case TOKEN_WORD:
4073
+ return `the word ${t.textOf(v)}`;
4074
+ default:
4075
+ return t.textOf(v);
4076
+ }
4077
+ }
4078
+ const SNIFF_GRAPH_START = /^(?:\s|#[^\n]*\n)*graph\s*\[/;
4079
+ const SNIFF_GRAPH_ANYWHERE = /(?:^|\s)graph\s*\[/;
4080
+ const SNIFF_KEY_START = /^(?:\s|#[^\n]*\n)*[A-Za-z_]\w*\s+(?:"|[+-]?[0-9.]|\[)/;
4081
+ function sniffGml(head) {
4082
+ let text = new TextDecoder("utf-8", { fatal: false }).decode(head);
4083
+ if (text.charCodeAt(0) === 65279) {
4084
+ text = text.slice(1);
4085
+ }
4086
+ if (SNIFF_GRAPH_START.test(text)) {
4087
+ return 0.95;
4088
+ }
4089
+ if (SNIFF_KEY_START.test(text)) {
4090
+ return SNIFF_GRAPH_ANYWHERE.test(text) ? 0.85 : 0.4;
4091
+ }
4092
+ return 0;
4093
+ }
4094
+ const gmlImporter = Object.freeze({
4095
+ format: "gml",
4096
+ options: Object.freeze(["dictionaries", "keepPositionSource", "positions"]),
4097
+ extensions: Object.freeze([".gml"]),
4098
+ mimeTypes: Object.freeze(["text/x-gml", "text/plain"]),
4099
+ sniff: sniffGml,
4100
+ styles: gmlStyles,
4101
+ positions: gmlPositions,
4102
+ /**
4103
+ * Read a GML text into a sink.
4104
+ * @param input - the text, bytes or stream
4105
+ * @param sink - the sink
4106
+ * @param options - format-specific and common options
4107
+ * @returns the report
4108
+ */
4109
+ async import(input, sink, options) {
4110
+ if (graphChosen(options)) {
4111
+ return importChosenGraph(gmlImporter, input, sink, options);
4112
+ }
4113
+ const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
4114
+ const report = new ImportReportBuilder("gml", resolved.errorLimit);
4115
+ reportSinkOptions(sink, options, report);
4116
+ reportUnusedOptions(options, report, USED_OPTIONS);
4117
+ const tokens = tokenize(await readText(input, report, resolved), report);
4118
+ throwIfAborted(resolved.signal);
4119
+ const gml = new GmlImport(sink, report, resolved, options);
4120
+ gml.run(tokens);
4121
+ if (gml.graphCount > 1) {
4122
+ report.warning(
4123
+ "unsupported",
4124
+ SECOND_GRAPH_CODE,
4125
+ `the input holds ${gml.graphCount - 1} more graph block${plural(gml.graphCount - 1)} after the first; the first is read (graphIndex or graphName chooses another; importAllGraphs() reads every one)`
4126
+ );
4127
+ }
4128
+ throwIfAborted(resolved.signal);
4129
+ return report.finish();
4130
+ },
4131
+ /**
4132
+ * Read every `graph [ ... ]` block of a GML text, each into its own sink; the top-level keys
4133
+ * (Creator, Version, ...) apply to each.
4134
+ * @param input - the text, bytes or stream
4135
+ * @param sinkFor - the sink of the graph with this index, called before its first push
4136
+ * @param options - format-specific and common options
4137
+ * @returns one report per graph block
4138
+ */
4139
+ async importAll(input, sinkFor, options) {
4140
+ const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
4141
+ const first2 = new ImportReportBuilder("gml", resolved.errorLimit);
4142
+ reportUnusedOptions(options, first2, USED_OPTIONS);
4143
+ const tokens = tokenize(await readText(input, first2, resolved), first2);
4144
+ const reports = [];
4145
+ let count = 1;
4146
+ for (let i = 0; i < count; i++) {
4147
+ throwIfAborted(resolved.signal);
4148
+ const report = i === 0 ? first2 : new ImportReportBuilder("gml", resolved.errorLimit);
4149
+ const sink = sinkFor(i);
4150
+ reportSinkOptions(sink, options, report);
4151
+ const gml = new GmlImport(sink, report, resolved, options, i);
4152
+ gml.run(tokens);
4153
+ count = gml.graphCount;
4154
+ reports.push(report.finish());
4155
+ }
4156
+ throwIfAborted(resolved.signal);
4157
+ return reports;
4158
+ }
4159
+ });
4160
+ function tokenize(text, report) {
4161
+ try {
4162
+ return tokenizeGml(text);
4163
+ } catch (err) {
4164
+ if (err instanceof GmlSyntaxError) {
4165
+ report.fail(err.code, err.message, { line: err.line });
4166
+ }
4167
+ throw err;
4168
+ }
4169
+ }
4170
+ const GML_ISSUE = Object.freeze({
4171
+ ...INPUT_ISSUE,
4172
+ ...ELEMENT_ISSUE,
4173
+ /**
4174
+ * The text breaks GML's syntax: a word that is not a key or a value, an unclosed string or `[`, a stray `]`, or a
4175
+ * key without a value. The import stops.
4176
+ */
4177
+ SYNTAX: SYNTAX_CODE,
4178
+ /** The input is not valid UTF-8. The import stops. */
4179
+ INVALID_UTF8: INVALID_UTF8_CODE,
4180
+ /**
4181
+ * Some bytes are not valid in the encoding that was chosen (by a byte order mark, the file's declaration or the
4182
+ * `encoding` option). The import stops.
4183
+ */
4184
+ INVALID_ENCODING: INVALID_ENCODING_CODE,
4185
+ /** Bytes that are not UTF-8 and declare no encoding were read as windows-1252. */
4186
+ ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
4187
+ /** A declared encoding the platform cannot decode was ignored. */
4188
+ UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
4189
+ /** There is no `graph [` block. The import stops. */
4190
+ NO_GRAPH: NO_GRAPH_CODE,
4191
+ /** The file holds more than one `graph` block and only the first was read. It is not added when `graphIndex` or `graphName` chose the graph. */
4192
+ MULTIPLE_GRAPHS: SECOND_GRAPH_CODE,
4193
+ /** `graphIndex` or `graphName` names no graph of the file; the message lists the graphs it holds. The import stops. */
4194
+ GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
4195
+ /** `graphName` matches more than one graph; pass `graphIndex`. The import stops. */
4196
+ AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
4197
+ /** A node without an `id`. */
4198
+ MISSING_ID: MISSING_ID_CODE,
4199
+ /** A node without a `label` under nodeIdFrom "label". */
4200
+ MISSING_LABEL: MISSING_LABEL_CODE,
4201
+ /** An edge without `source` or `target`. */
4202
+ MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
4203
+ /** A node id, source or target that is neither an integer nor a string. */
4204
+ ID_TYPE: ID_TYPE_CODE,
4205
+ /**
4206
+ * Node ids, sources or targets are text, where GML expects integers. They are read under the `ids` option.
4207
+ * Reported once per file.
4208
+ */
4209
+ STRING_ID: STRING_ID_CODE,
4210
+ /** A node id declared twice (later keys overwrite). */
4211
+ DUPLICATE_NODE: DUPLICATE_NODE_CODE,
4212
+ /** A structural key repeated in one element. */
4213
+ REPEATED_KEY: REPEATED_KEY_CODE,
4214
+ /** A `node` / `edge` key whose value is not a record. */
4215
+ ELEMENT_TYPE: ELEMENT_TYPE_CODE,
4216
+ /** A `directed` / `multigraph` flag that is not an integer. */
4217
+ FLAG_TYPE: FLAG_TYPE_CODE,
4218
+ /** A `directed` / `multigraph` flag outside 0 / 1, or repeated. */
4219
+ FLAG_VALUE: FLAG_VALUE_CODE,
4220
+ /** A named entity no table decodes, or a numeric reference beyond U+10FFFF; kept as written. */
4221
+ UNKNOWN_ENTITY: UNKNOWN_ENTITY_CODE,
4222
+ /** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
4223
+ PRECISION: PRECISION_CODE,
4224
+ /** A node's graphics value that cannot give a position as written; kept in the graphics json column. */
4225
+ GRAPHICS: GRAPHICS_CODE,
4226
+ /** A graph, node or edge record nested in a node or edge; kept as json, not read as structure. */
4227
+ NESTED_ELEMENT: NESTED_ELEMENT_CODE,
4228
+ /** YEd's isGroup / gid keys, kept as plain columns; the hierarchy is not read as containment. */
4229
+ GROUPS: GROUPS_CODE,
4230
+ /**
4231
+ * An attribute's type was widened because a later value did not fit: an integer above 2^31 in an integer column,
4232
+ * or two declared types for one attribute.
4233
+ */
4234
+ WIDENED: WIDENED_CODE,
4235
+ /**
4236
+ * An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example
4237
+ * two attributes declared with the same name.
4238
+ */
4239
+ COLUMN_RENAMED: COLUMN_RENAMED_CODE,
4240
+ /**
4241
+ * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
4242
+ * a plain attribute.
4243
+ */
4244
+ ROLE_TAKEN: ROLE_TAKEN_CODE,
4245
+ /** Two different id texts became the same number because `ids` is "number", so their nodes were merged. */
4246
+ ID_MERGED: ID_MERGED_CODE,
4247
+ /** You set an option this format does not use; it had no effect. The message names the option. */
4248
+ OPTION_IGNORED: OPTION_IGNORED_CODE,
4249
+ /**
4250
+ * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
4251
+ * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
4252
+ */
4253
+ SINK_OPTION: SINK_OPTION_CODE,
4254
+ /**
4255
+ * You read into a graph builder whose direction is already set, or which already holds edges, so the file is read
4256
+ * with the builder's direction instead of its own.
4257
+ */
4258
+ DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
4259
+ /** Edges of the other direction were read with the direction `onMixedDirection` chose. */
4260
+ DIRECTION_FORCED: DIRECTION_FORCED_CODE,
4261
+ /**
4262
+ * The graph has both directed and undirected edges and `onMixedDirection` is "error". An import stops; a save to a
4263
+ * format that holds one direction per file fails with E_DIRECTED. Pass "directed" or "undirected" to read or write
4264
+ * it anyway.
4265
+ */
4266
+ MIXED_DIRECTION: MIXED_DIRECTION_CODE,
4267
+ /**
4268
+ * Under `nodeIdFrom: "label"` or `"index"` the file's integer ids are not kept. It is listed in `report.lossy`, not
4269
+ * in `report.issues`.
4270
+ */
4271
+ ID_DROPPED: ID_DROPPED_CODE
4272
+ });
4273
+ const GML_LOSS = Object.freeze({
4274
+ /** A json column holds numbers; GML records cannot keep int versus real. */
4275
+ RECORD_NUMBER_TYPE: RECORD_NUMBER_TYPE_CODE,
4276
+ /** A json column holds booleans, written 1 / 0. */
4277
+ RECORD_BOOLEAN: RECORD_BOOLEAN_CODE,
4278
+ /** A json column holds nulls, omitted. */
4279
+ RECORD_NULL: RECORD_NULL_CODE,
4280
+ /** An attribute holds an array inside an array, which GML cannot write; the save fails. */
4281
+ NESTED_ARRAY: NESTED_ARRAY_CODE,
4282
+ /** A json row that is an array, written as repeated keys. */
4283
+ JSON_ARRAY: JSON_ARRAY_CODE,
4284
+ /**
4285
+ * An attribute name GML cannot use as a key (keys are letters and digits, starting with a letter). The save fails
4286
+ * unless `sanitizeKeys` is "mangle".
4287
+ */
4288
+ INVALID_KEY: INVALID_KEY_CODE,
4289
+ /** A column named like a structural key. */
4290
+ RESERVED_KEY: RESERVED_KEY_CODE,
4291
+ /** A key rewritten under sanitizeKeys "mangle", or a column renamed because its key would read back as a style value. */
4292
+ KEY_MANGLED: KEY_MANGLED_CODE,
4293
+ /** A position column with more than three components. */
4294
+ POSITION_COMPONENTS: POSITION_COMPONENTS_CODE,
4295
+ /** A graphics record whose x / y / z the position column overrides. */
4296
+ GRAPHICS_OVERRIDDEN: GRAPHICS_OVERRIDDEN_CODE,
4297
+ /** A graphics record that cannot hold the position. */
4298
+ GRAPHICS_CONFLICT: GRAPHICS_CONFLICT_CODE
4299
+ });
4300
+ export {
4301
+ GML_ISSUE as G,
4302
+ gmlExporter as a,
4303
+ GML_LOSS as b,
4304
+ GML_STYLE_BLOCK_KEY as c,
4305
+ GML_STYLE_CAPABILITIES as d,
4306
+ gmlImporter as g,
4307
+ isKeptGraphtyProperty as i,
4308
+ prepareStyleExport as p
4309
+ };
4310
+ //# sourceMappingURL=index-B9fdNpTA.js.map