@graphty/graph-io 0.0.0 → 0.2.1

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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -28
  3. package/dist/chunks/children-CL3Cy0ez.js +238 -0
  4. package/dist/chunks/children-CL3Cy0ez.js.map +1 -0
  5. package/dist/chunks/escape-DyI8JofU.js +938 -0
  6. package/dist/chunks/escape-DyI8JofU.js.map +1 -0
  7. package/dist/chunks/importer-CQnJuWJw.js +2987 -0
  8. package/dist/chunks/importer-CQnJuWJw.js.map +1 -0
  9. package/dist/chunks/importer-CpCpfbxr.js +2015 -0
  10. package/dist/chunks/importer-CpCpfbxr.js.map +1 -0
  11. package/dist/chunks/importer-DbnGYr3_.js +2342 -0
  12. package/dist/chunks/importer-DbnGYr3_.js.map +1 -0
  13. package/dist/chunks/importer-GozH8DkN.js +3050 -0
  14. package/dist/chunks/importer-GozH8DkN.js.map +1 -0
  15. package/dist/chunks/records-CGpxszm1.js +605 -0
  16. package/dist/chunks/records-CGpxszm1.js.map +1 -0
  17. package/dist/chunks/text-CajMdVFy.js +189 -0
  18. package/dist/chunks/text-CajMdVFy.js.map +1 -0
  19. package/dist/chunks/writer-DxSKC7TL.js +2842 -0
  20. package/dist/chunks/writer-DxSKC7TL.js.map +1 -0
  21. package/dist/csv.d.ts +1 -0
  22. package/dist/csv.js +1702 -0
  23. package/dist/csv.js.map +1 -0
  24. package/dist/dot.d.ts +1 -0
  25. package/dist/dot.js +8 -0
  26. package/dist/dot.js.map +1 -0
  27. package/dist/gexf.d.ts +1 -0
  28. package/dist/gexf.js +3466 -0
  29. package/dist/gexf.js.map +1 -0
  30. package/dist/gml.d.ts +1 -0
  31. package/dist/gml.js +2647 -0
  32. package/dist/gml.js.map +1 -0
  33. package/dist/graph-io.d.ts +1 -0
  34. package/dist/graph-io.js +790 -0
  35. package/dist/graph-io.js.map +1 -0
  36. package/dist/graphml.d.ts +1 -0
  37. package/dist/graphml.js +8 -0
  38. package/dist/graphml.js.map +1 -0
  39. package/dist/json.d.ts +1 -0
  40. package/dist/json.js +11 -0
  41. package/dist/json.js.map +1 -0
  42. package/dist/neo4j.d.ts +1 -0
  43. package/dist/neo4j.js +2046 -0
  44. package/dist/neo4j.js.map +1 -0
  45. package/dist/pajek.d.ts +1 -0
  46. package/dist/pajek.js +8 -0
  47. package/dist/pajek.js.map +1 -0
  48. package/dist/src/children.d.ts +134 -0
  49. package/dist/src/children.d.ts.map +1 -0
  50. package/dist/src/children.js +274 -0
  51. package/dist/src/children.js.map +1 -0
  52. package/dist/src/common/attributes.d.ts +229 -0
  53. package/dist/src/common/attributes.d.ts.map +1 -0
  54. package/dist/src/common/attributes.js +368 -0
  55. package/dist/src/common/attributes.js.map +1 -0
  56. package/dist/src/common/codes.d.ts +105 -0
  57. package/dist/src/common/codes.d.ts.map +1 -0
  58. package/dist/src/common/codes.js +107 -0
  59. package/dist/src/common/codes.js.map +1 -0
  60. package/dist/src/common/declared-types.d.ts +84 -0
  61. package/dist/src/common/declared-types.d.ts.map +1 -0
  62. package/dist/src/common/declared-types.js +326 -0
  63. package/dist/src/common/declared-types.js.map +1 -0
  64. package/dist/src/common/direction.d.ts +206 -0
  65. package/dist/src/common/direction.d.ts.map +1 -0
  66. package/dist/src/common/direction.js +370 -0
  67. package/dist/src/common/direction.js.map +1 -0
  68. package/dist/src/common/escape.d.ts +92 -0
  69. package/dist/src/common/escape.d.ts.map +1 -0
  70. package/dist/src/common/escape.js +212 -0
  71. package/dist/src/common/escape.js.map +1 -0
  72. package/dist/src/common/export.d.ts +249 -0
  73. package/dist/src/common/export.d.ts.map +1 -0
  74. package/dist/src/common/export.js +594 -0
  75. package/dist/src/common/export.js.map +1 -0
  76. package/dist/src/common/format.d.ts +59 -0
  77. package/dist/src/common/format.d.ts.map +1 -0
  78. package/dist/src/common/format.js +106 -0
  79. package/dist/src/common/format.js.map +1 -0
  80. package/dist/src/common/ids.d.ts +83 -0
  81. package/dist/src/common/ids.d.ts.map +1 -0
  82. package/dist/src/common/ids.js +158 -0
  83. package/dist/src/common/ids.js.map +1 -0
  84. package/dist/src/common/input.d.ts +100 -0
  85. package/dist/src/common/input.d.ts.map +1 -0
  86. package/dist/src/common/input.js +335 -0
  87. package/dist/src/common/input.js.map +1 -0
  88. package/dist/src/common/lists.d.ts +34 -0
  89. package/dist/src/common/lists.d.ts.map +1 -0
  90. package/dist/src/common/lists.js +185 -0
  91. package/dist/src/common/lists.js.map +1 -0
  92. package/dist/src/common/options.d.ts +108 -0
  93. package/dist/src/common/options.d.ts.map +1 -0
  94. package/dist/src/common/options.js +265 -0
  95. package/dist/src/common/options.js.map +1 -0
  96. package/dist/src/common/report.d.ts +187 -0
  97. package/dist/src/common/report.d.ts.map +1 -0
  98. package/dist/src/common/report.js +274 -0
  99. package/dist/src/common/report.js.map +1 -0
  100. package/dist/src/common/temporal.d.ts +71 -0
  101. package/dist/src/common/temporal.d.ts.map +1 -0
  102. package/dist/src/common/temporal.js +266 -0
  103. package/dist/src/common/temporal.js.map +1 -0
  104. package/dist/src/common/text.d.ts +104 -0
  105. package/dist/src/common/text.d.ts.map +1 -0
  106. package/dist/src/common/text.js +255 -0
  107. package/dist/src/common/text.js.map +1 -0
  108. package/dist/src/common/weights.d.ts +77 -0
  109. package/dist/src/common/weights.d.ts.map +1 -0
  110. package/dist/src/common/weights.js +156 -0
  111. package/dist/src/common/weights.js.map +1 -0
  112. package/dist/src/common/writer.d.ts +51 -0
  113. package/dist/src/common/writer.d.ts.map +1 -0
  114. package/dist/src/common/writer.js +108 -0
  115. package/dist/src/common/writer.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +245 -0
  117. package/dist/src/common/xml.d.ts.map +1 -0
  118. package/dist/src/common/xml.js +942 -0
  119. package/dist/src/common/xml.js.map +1 -0
  120. package/dist/src/formats/csv/exporter.d.ts +70 -0
  121. package/dist/src/formats/csv/exporter.d.ts.map +1 -0
  122. package/dist/src/formats/csv/exporter.js +682 -0
  123. package/dist/src/formats/csv/exporter.js.map +1 -0
  124. package/dist/src/formats/csv/header.d.ts +66 -0
  125. package/dist/src/formats/csv/header.d.ts.map +1 -0
  126. package/dist/src/formats/csv/header.js +152 -0
  127. package/dist/src/formats/csv/header.js.map +1 -0
  128. package/dist/src/formats/csv/importer.d.ts +82 -0
  129. package/dist/src/formats/csv/importer.d.ts.map +1 -0
  130. package/dist/src/formats/csv/importer.js +849 -0
  131. package/dist/src/formats/csv/importer.js.map +1 -0
  132. package/dist/src/formats/csv/index.d.ts +60 -0
  133. package/dist/src/formats/csv/index.d.ts.map +1 -0
  134. package/dist/src/formats/csv/index.js +63 -0
  135. package/dist/src/formats/csv/index.js.map +1 -0
  136. package/dist/src/formats/csv/records.d.ts +188 -0
  137. package/dist/src/formats/csv/records.d.ts.map +1 -0
  138. package/dist/src/formats/csv/records.js +702 -0
  139. package/dist/src/formats/csv/records.js.map +1 -0
  140. package/dist/src/formats/csv/values.d.ts +105 -0
  141. package/dist/src/formats/csv/values.d.ts.map +1 -0
  142. package/dist/src/formats/csv/values.js +192 -0
  143. package/dist/src/formats/csv/values.js.map +1 -0
  144. package/dist/src/formats/dot/exporter.d.ts +52 -0
  145. package/dist/src/formats/dot/exporter.d.ts.map +1 -0
  146. package/dist/src/formats/dot/exporter.js +836 -0
  147. package/dist/src/formats/dot/exporter.js.map +1 -0
  148. package/dist/src/formats/dot/importer.d.ts +102 -0
  149. package/dist/src/formats/dot/importer.d.ts.map +1 -0
  150. package/dist/src/formats/dot/importer.js +1291 -0
  151. package/dist/src/formats/dot/importer.js.map +1 -0
  152. package/dist/src/formats/dot/index.d.ts +7 -0
  153. package/dist/src/formats/dot/index.d.ts.map +1 -0
  154. package/dist/src/formats/dot/index.js +7 -0
  155. package/dist/src/formats/dot/index.js.map +1 -0
  156. package/dist/src/formats/dot/names.d.ts +29 -0
  157. package/dist/src/formats/dot/names.d.ts.map +1 -0
  158. package/dist/src/formats/dot/names.js +28 -0
  159. package/dist/src/formats/dot/names.js.map +1 -0
  160. package/dist/src/formats/dot/tokenizer.d.ts +114 -0
  161. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -0
  162. package/dist/src/formats/dot/tokenizer.js +341 -0
  163. package/dist/src/formats/dot/tokenizer.js.map +1 -0
  164. package/dist/src/formats/gexf/exporter.d.ts +56 -0
  165. package/dist/src/formats/gexf/exporter.d.ts.map +1 -0
  166. package/dist/src/formats/gexf/exporter.js +1395 -0
  167. package/dist/src/formats/gexf/exporter.js.map +1 -0
  168. package/dist/src/formats/gexf/importer.d.ts +73 -0
  169. package/dist/src/formats/gexf/importer.d.ts.map +1 -0
  170. package/dist/src/formats/gexf/importer.js +1880 -0
  171. package/dist/src/formats/gexf/importer.js.map +1 -0
  172. package/dist/src/formats/gexf/index.d.ts +96 -0
  173. package/dist/src/formats/gexf/index.d.ts.map +1 -0
  174. package/dist/src/formats/gexf/index.js +97 -0
  175. package/dist/src/formats/gexf/index.js.map +1 -0
  176. package/dist/src/formats/gexf/schema.d.ts +135 -0
  177. package/dist/src/formats/gexf/schema.d.ts.map +1 -0
  178. package/dist/src/formats/gexf/schema.js +323 -0
  179. package/dist/src/formats/gexf/schema.js.map +1 -0
  180. package/dist/src/formats/gml/exporter.d.ts +69 -0
  181. package/dist/src/formats/gml/exporter.d.ts.map +1 -0
  182. package/dist/src/formats/gml/exporter.js +1093 -0
  183. package/dist/src/formats/gml/exporter.js.map +1 -0
  184. package/dist/src/formats/gml/importer.d.ts +66 -0
  185. package/dist/src/formats/gml/importer.d.ts.map +1 -0
  186. package/dist/src/formats/gml/importer.js +1331 -0
  187. package/dist/src/formats/gml/importer.js.map +1 -0
  188. package/dist/src/formats/gml/index.d.ts +85 -0
  189. package/dist/src/formats/gml/index.d.ts.map +1 -0
  190. package/dist/src/formats/gml/index.js +88 -0
  191. package/dist/src/formats/gml/index.js.map +1 -0
  192. package/dist/src/formats/gml/syntax.d.ts +186 -0
  193. package/dist/src/formats/gml/syntax.d.ts.map +1 -0
  194. package/dist/src/formats/gml/syntax.js +467 -0
  195. package/dist/src/formats/gml/syntax.js.map +1 -0
  196. package/dist/src/formats/graphml/constants.d.ts +169 -0
  197. package/dist/src/formats/graphml/constants.d.ts.map +1 -0
  198. package/dist/src/formats/graphml/constants.js +165 -0
  199. package/dist/src/formats/graphml/constants.js.map +1 -0
  200. package/dist/src/formats/graphml/exporter.d.ts +34 -0
  201. package/dist/src/formats/graphml/exporter.d.ts.map +1 -0
  202. package/dist/src/formats/graphml/exporter.js +1176 -0
  203. package/dist/src/formats/graphml/exporter.js.map +1 -0
  204. package/dist/src/formats/graphml/importer.d.ts +31 -0
  205. package/dist/src/formats/graphml/importer.d.ts.map +1 -0
  206. package/dist/src/formats/graphml/importer.js +1607 -0
  207. package/dist/src/formats/graphml/importer.js.map +1 -0
  208. package/dist/src/formats/graphml/index.d.ts +8 -0
  209. package/dist/src/formats/graphml/index.d.ts.map +1 -0
  210. package/dist/src/formats/graphml/index.js +8 -0
  211. package/dist/src/formats/graphml/index.js.map +1 -0
  212. package/dist/src/formats/graphml/tree.d.ts +72 -0
  213. package/dist/src/formats/graphml/tree.d.ts.map +1 -0
  214. package/dist/src/formats/graphml/tree.js +290 -0
  215. package/dist/src/formats/graphml/tree.js.map +1 -0
  216. package/dist/src/formats/json/dialect.d.ts +125 -0
  217. package/dist/src/formats/json/dialect.d.ts.map +1 -0
  218. package/dist/src/formats/json/dialect.js +262 -0
  219. package/dist/src/formats/json/dialect.js.map +1 -0
  220. package/dist/src/formats/json/exporter.d.ts +89 -0
  221. package/dist/src/formats/json/exporter.d.ts.map +1 -0
  222. package/dist/src/formats/json/exporter.js +1358 -0
  223. package/dist/src/formats/json/exporter.js.map +1 -0
  224. package/dist/src/formats/json/importer.d.ts +108 -0
  225. package/dist/src/formats/json/importer.d.ts.map +1 -0
  226. package/dist/src/formats/json/importer.js +1838 -0
  227. package/dist/src/formats/json/importer.js.map +1 -0
  228. package/dist/src/formats/json/index.d.ts +8 -0
  229. package/dist/src/formats/json/index.d.ts.map +1 -0
  230. package/dist/src/formats/json/index.js +8 -0
  231. package/dist/src/formats/json/index.js.map +1 -0
  232. package/dist/src/formats/neo4j/exporter.d.ts +68 -0
  233. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -0
  234. package/dist/src/formats/neo4j/exporter.js +1055 -0
  235. package/dist/src/formats/neo4j/exporter.js.map +1 -0
  236. package/dist/src/formats/neo4j/header.d.ts +52 -0
  237. package/dist/src/formats/neo4j/header.d.ts.map +1 -0
  238. package/dist/src/formats/neo4j/header.js +131 -0
  239. package/dist/src/formats/neo4j/header.js.map +1 -0
  240. package/dist/src/formats/neo4j/importer.d.ts +73 -0
  241. package/dist/src/formats/neo4j/importer.d.ts.map +1 -0
  242. package/dist/src/formats/neo4j/importer.js +932 -0
  243. package/dist/src/formats/neo4j/importer.js.map +1 -0
  244. package/dist/src/formats/neo4j/index.d.ts +79 -0
  245. package/dist/src/formats/neo4j/index.d.ts.map +1 -0
  246. package/dist/src/formats/neo4j/index.js +83 -0
  247. package/dist/src/formats/neo4j/index.js.map +1 -0
  248. package/dist/src/formats/pajek/exporter.d.ts +58 -0
  249. package/dist/src/formats/pajek/exporter.d.ts.map +1 -0
  250. package/dist/src/formats/pajek/exporter.js +825 -0
  251. package/dist/src/formats/pajek/exporter.js.map +1 -0
  252. package/dist/src/formats/pajek/importer.d.ts +88 -0
  253. package/dist/src/formats/pajek/importer.d.ts.map +1 -0
  254. package/dist/src/formats/pajek/importer.js +1047 -0
  255. package/dist/src/formats/pajek/importer.js.map +1 -0
  256. package/dist/src/formats/pajek/index.d.ts +7 -0
  257. package/dist/src/formats/pajek/index.d.ts.map +1 -0
  258. package/dist/src/formats/pajek/index.js +7 -0
  259. package/dist/src/formats/pajek/index.js.map +1 -0
  260. package/dist/src/formats/pajek/syntax.d.ts +112 -0
  261. package/dist/src/formats/pajek/syntax.d.ts.map +1 -0
  262. package/dist/src/formats/pajek/syntax.js +269 -0
  263. package/dist/src/formats/pajek/syntax.js.map +1 -0
  264. package/dist/src/index.d.ts +35 -0
  265. package/dist/src/index.d.ts.map +1 -0
  266. package/dist/src/index.js +39 -0
  267. package/dist/src/index.js.map +1 -0
  268. package/dist/src/registry.d.ts +207 -0
  269. package/dist/src/registry.d.ts.map +1 -0
  270. package/dist/src/registry.js +481 -0
  271. package/dist/src/registry.js.map +1 -0
  272. package/dist/src/sniff.d.ts +104 -0
  273. package/dist/src/sniff.d.ts.map +1 -0
  274. package/dist/src/sniff.js +357 -0
  275. package/dist/src/sniff.js.map +1 -0
  276. package/dist/src/types.d.ts +238 -0
  277. package/dist/src/types.d.ts.map +1 -0
  278. package/dist/src/types.js +29 -0
  279. package/dist/src/types.js.map +1 -0
  280. package/dist/tsconfig.build.tsbuildinfo +1 -0
  281. package/package.json +122 -7
  282. package/src/children.ts +335 -0
  283. package/src/common/attributes.ts +520 -0
  284. package/src/common/codes.ts +153 -0
  285. package/src/common/declared-types.ts +374 -0
  286. package/src/common/direction.ts +518 -0
  287. package/src/common/escape.ts +231 -0
  288. package/src/common/export.ts +817 -0
  289. package/src/common/format.ts +111 -0
  290. package/src/common/ids.ts +176 -0
  291. package/src/common/input.ts +378 -0
  292. package/src/common/lists.ts +196 -0
  293. package/src/common/options.ts +377 -0
  294. package/src/common/report.ts +352 -0
  295. package/src/common/temporal.ts +302 -0
  296. package/src/common/text.ts +294 -0
  297. package/src/common/weights.ts +202 -0
  298. package/src/common/writer.ts +123 -0
  299. package/src/common/xml.ts +1053 -0
  300. package/src/formats/csv/exporter.ts +894 -0
  301. package/src/formats/csv/header.ts +172 -0
  302. package/src/formats/csv/importer.ts +1104 -0
  303. package/src/formats/csv/index.ts +88 -0
  304. package/src/formats/csv/records.ts +813 -0
  305. package/src/formats/csv/values.ts +224 -0
  306. package/src/formats/dot/exporter.ts +1014 -0
  307. package/src/formats/dot/importer.ts +1549 -0
  308. package/src/formats/dot/index.ts +7 -0
  309. package/src/formats/dot/names.ts +40 -0
  310. package/src/formats/dot/tokenizer.ts +384 -0
  311. package/src/formats/gexf/exporter.ts +1696 -0
  312. package/src/formats/gexf/importer.ts +2333 -0
  313. package/src/formats/gexf/index.ts +142 -0
  314. package/src/formats/gexf/schema.ts +361 -0
  315. package/src/formats/gml/exporter.ts +1404 -0
  316. package/src/formats/gml/importer.ts +1591 -0
  317. package/src/formats/gml/index.ts +128 -0
  318. package/src/formats/gml/syntax.ts +545 -0
  319. package/src/formats/graphml/constants.ts +225 -0
  320. package/src/formats/graphml/exporter.ts +1458 -0
  321. package/src/formats/graphml/importer.ts +2027 -0
  322. package/src/formats/graphml/index.ts +8 -0
  323. package/src/formats/graphml/tree.ts +318 -0
  324. package/src/formats/json/dialect.ts +317 -0
  325. package/src/formats/json/exporter.ts +1616 -0
  326. package/src/formats/json/importer.ts +2271 -0
  327. package/src/formats/json/index.ts +8 -0
  328. package/src/formats/neo4j/exporter.ts +1287 -0
  329. package/src/formats/neo4j/header.ts +156 -0
  330. package/src/formats/neo4j/importer.ts +1220 -0
  331. package/src/formats/neo4j/index.ts +116 -0
  332. package/src/formats/pajek/exporter.ts +1000 -0
  333. package/src/formats/pajek/importer.ts +1311 -0
  334. package/src/formats/pajek/index.ts +7 -0
  335. package/src/formats/pajek/syntax.ts +307 -0
  336. package/src/index.ts +244 -0
  337. package/src/registry.ts +617 -0
  338. package/src/sniff.ts +397 -0
  339. package/src/types.ts +262 -0
package/dist/gml.js ADDED
@@ -0,0 +1,2647 @@
1
+ import { Z as SYNTAX_CODE, au as joinText, ah as encodeChunks, aa as checkCapabilities, L as LOSS, aw as pairFolding, a9 as capabilities, aB as resolveExportOptions, aX as DictHeuristic, an as formatInteger, aD as sanitizeIds, aG as countMixedEdges, ai as explicitWeights, aY as DICT_SAMPLE_ROWS, am as formatGmlReal, V as ROLE_TAKEN_CODE$1, R as PRECISION_CODE$1, k as DUPLICATE_NODE_CODE$1, M as MISSING_ENDPOINT_CODE$1, w as MISSING_ID_CODE$1, G as NO_GRAPH_CODE$1, aC as resolveImportOptions, I as ImportReportBuilder, az as reportSinkOptions, aA as reportUnusedOptions, ay as readText, a as throwIfAborted, s as IdCoercer, l as DirectionResolver, aI as uniqueColumnName, aH as weightFromValue, ax as parseWeightText, af as declareResolved, x as MIXED_DIRECTION_CODE, g as DIRECTION_FORCED_CODE, h as DIRECTION_REFUSED_CODE, W as SINK_OPTION_CODE, J as OPTION_IGNORED_CODE, n as ID_MERGED_CODE, C as COLUMN_RENAMED_CODE, r as INVALID_UTF8_CODE } from "./chunks/writer-DxSKC7TL.js";
2
+ import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
3
+ import { g as decodeGmlString, j as quoteGmlString } from "./chunks/escape-DyI8JofU.js";
4
+ const TOKEN_WORD = 0;
5
+ const TOKEN_INT = 1;
6
+ const TOKEN_REAL = 2;
7
+ const TOKEN_STRING = 3;
8
+ const TOKEN_OPEN = 4;
9
+ const TOKEN_CLOSE = 5;
10
+ const LIST_START_MARKER = "_networkx_list_start";
11
+ const EMPTY_LIST_TEXT = "[]";
12
+ const EMPTY_TUPLE_TEXT = "()";
13
+ const ORIGINAL_ID_KEY = "graphty_originalId";
14
+ const SYNTAX_STRUCTURE_CODE = SYNTAX_CODE;
15
+ const SYNTAX_TOKEN_CODE = SYNTAX_CODE;
16
+ const SYNTAX_STRING_CODE = SYNTAX_CODE;
17
+ const SYNTAX_BRACKET_CODE = SYNTAX_CODE;
18
+ const KEY_TEXT = /^[A-Za-z_][0-9A-Za-z_]*$/;
19
+ const STRICT_KEY_TEXT = /^[A-Za-z][0-9A-Za-z_]*$/;
20
+ const INT_TEXT = /^[+-]?[0-9]+$/;
21
+ const REAL_TEXT = /^[+-]?(?:(?:[0-9]+\.[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+)$/;
22
+ const NON_FINITE_TEXT = /^[+-]?(?:inf|infinity|nan)$/i;
23
+ const NON_FINITE_WORD = /^(?:inf|infinity|nan)$/i;
24
+ class GmlSyntaxError extends Error {
25
+ /**
26
+ * Create a syntax error.
27
+ * @param code - the issue code
28
+ * @param message - a plain-ASCII message
29
+ * @param line - the 1-based line
30
+ */
31
+ constructor(code, message, line) {
32
+ super(message);
33
+ this.name = "GmlSyntaxError";
34
+ this.code = code;
35
+ this.line = line;
36
+ }
37
+ }
38
+ class GmlTokens {
39
+ /**
40
+ * Create an empty token list over a text.
41
+ * @param text - the source text
42
+ * @param capacity - the initial capacity
43
+ */
44
+ constructor(text, capacity = 1024) {
45
+ this.count = 0;
46
+ this.text = text;
47
+ this.kind = new Uint8Array(capacity);
48
+ this.start = new Uint32Array(capacity);
49
+ this.end = new Uint32Array(capacity);
50
+ this.line = new Uint32Array(capacity);
51
+ this.match = new Uint32Array(0);
52
+ }
53
+ /**
54
+ * Append a token.
55
+ * @param kind - the kind
56
+ * @param start - the start offset
57
+ * @param end - the end offset
58
+ * @param line - the 1-based line
59
+ */
60
+ push(kind, start, end, line) {
61
+ if (this.count === this.kind.length) {
62
+ this.grow();
63
+ }
64
+ const i = this.count++;
65
+ this.kind[i] = kind;
66
+ this.start[i] = start;
67
+ this.end[i] = end;
68
+ this.line[i] = line;
69
+ }
70
+ /**
71
+ * The raw text of a token (a string's body without the quotes, entities undecoded).
72
+ * @param i - the token index
73
+ * @returns the text
74
+ */
75
+ textOf(i) {
76
+ return this.text.slice(this.start[i], this.end[i]);
77
+ }
78
+ /**
79
+ * The decoded value of a string token.
80
+ * @param i - the token index
81
+ * @returns the body with character references decoded
82
+ */
83
+ stringOf(i) {
84
+ return decodeGmlString(this.textOf(i));
85
+ }
86
+ /**
87
+ * Whether a token is a `[` or `]`.
88
+ * @param i - the token index
89
+ * @returns true for a bracket
90
+ */
91
+ isBracket(i) {
92
+ return this.kind[i] >= TOKEN_OPEN;
93
+ }
94
+ /**
95
+ * The index of the pair following the pair whose key is at `key`.
96
+ * @param key - the index of a key token
97
+ * @returns the index after the pair's value (after the matching `]` for a record)
98
+ */
99
+ nextPair(key) {
100
+ const value = key + 1;
101
+ return this.kind[value] === TOKEN_OPEN ? this.match[value] + 1 : value + 1;
102
+ }
103
+ /** Double every array. */
104
+ grow() {
105
+ const capacity = this.kind.length * 2;
106
+ const kind = new Uint8Array(capacity);
107
+ kind.set(this.kind);
108
+ this.kind = kind;
109
+ const start = new Uint32Array(capacity);
110
+ start.set(this.start);
111
+ this.start = start;
112
+ const end = new Uint32Array(capacity);
113
+ end.set(this.end);
114
+ this.end = end;
115
+ const line = new Uint32Array(capacity);
116
+ line.set(this.line);
117
+ this.line = line;
118
+ }
119
+ }
120
+ function tokenizeGml(text) {
121
+ const tokens = new GmlTokens(text, Math.max(1024, text.length >>> 3));
122
+ const n = text.length;
123
+ let i = 0;
124
+ let line = 1;
125
+ while (i < n) {
126
+ const c = text.charCodeAt(i);
127
+ if (c === 10) {
128
+ line++;
129
+ i++;
130
+ } else if (c === 13) {
131
+ line++;
132
+ i++;
133
+ if (i < n && text.charCodeAt(i) === 10) {
134
+ i++;
135
+ }
136
+ } else if (c === 32 || c === 9 || c === 11 || c === 12) {
137
+ i++;
138
+ } else if (c === 35) {
139
+ i = endOfLine(text, i);
140
+ } else if (c === 91) {
141
+ tokens.push(TOKEN_OPEN, i, i + 1, line);
142
+ i++;
143
+ } else if (c === 93) {
144
+ tokens.push(TOKEN_CLOSE, i, i + 1, line);
145
+ i++;
146
+ } else if (c === 34) {
147
+ const close = text.indexOf('"', i + 1);
148
+ const eol = endOfLine(text, i + 1);
149
+ if (close < 0 || close > eol) {
150
+ throw new GmlSyntaxError(SYNTAX_STRING_CODE, `unclosed string at line ${line}`, line);
151
+ }
152
+ tokens.push(TOKEN_STRING, i + 1, close, line);
153
+ i = close + 1;
154
+ } else {
155
+ let j = i + 1;
156
+ while (j < n && !isDelimiter(text.charCodeAt(j))) {
157
+ j++;
158
+ }
159
+ tokens.push(classifyBare(text, i, j, line), i, j, line);
160
+ i = j;
161
+ }
162
+ }
163
+ validateStructure(tokens);
164
+ return tokens;
165
+ }
166
+ function endOfLine(text, from) {
167
+ const n = text.length;
168
+ for (let i = from; i < n; i++) {
169
+ const c = text.charCodeAt(i);
170
+ if (c === 10 || c === 13) {
171
+ return i;
172
+ }
173
+ }
174
+ return n;
175
+ }
176
+ function isDelimiter(c) {
177
+ return c === 32 || c === 9 || c === 10 || c === 13 || c === 11 || c === 12 || c === 91 || c === 93 || c === 34 || c === 35;
178
+ }
179
+ function classifyBare(text, start, end, line) {
180
+ const token = text.slice(start, end);
181
+ if (KEY_TEXT.test(token)) {
182
+ return TOKEN_WORD;
183
+ }
184
+ if (INT_TEXT.test(token)) {
185
+ return TOKEN_INT;
186
+ }
187
+ if (REAL_TEXT.test(token) || NON_FINITE_TEXT.test(token)) {
188
+ return TOKEN_REAL;
189
+ }
190
+ const shown = token.length > 20 ? `${token.slice(0, 20)}...` : token;
191
+ throw new GmlSyntaxError(SYNTAX_TOKEN_CODE, `cannot tokenize "${shown}" at line ${line}`, line);
192
+ }
193
+ function validateStructure(tokens) {
194
+ const { count, kind } = tokens;
195
+ const match = new Uint32Array(count);
196
+ const stack = [];
197
+ let i = 0;
198
+ while (i < count) {
199
+ const k = kind[i];
200
+ if (k === TOKEN_CLOSE) {
201
+ const open = stack.pop();
202
+ if (open === void 0) {
203
+ throw new GmlSyntaxError(
204
+ SYNTAX_STRUCTURE_CODE,
205
+ `unexpected "]" at line ${tokens.line[i]}: no open "["`,
206
+ tokens.line[i]
207
+ );
208
+ }
209
+ match[open] = i;
210
+ i++;
211
+ continue;
212
+ }
213
+ if (k !== TOKEN_WORD) {
214
+ throw new GmlSyntaxError(
215
+ SYNTAX_STRUCTURE_CODE,
216
+ `expected a key at line ${tokens.line[i]}, found ${describeToken(tokens, i)}`,
217
+ tokens.line[i]
218
+ );
219
+ }
220
+ const value = i + 1;
221
+ if (value >= count) {
222
+ throw new GmlSyntaxError(
223
+ SYNTAX_STRUCTURE_CODE,
224
+ `key "${tokens.textOf(i)}" at line ${tokens.line[i]} has no value`,
225
+ tokens.line[i]
226
+ );
227
+ }
228
+ const vk = kind[value];
229
+ if (vk === TOKEN_OPEN) {
230
+ stack.push(value);
231
+ } else if (vk === TOKEN_CLOSE || vk === TOKEN_WORD && !NON_FINITE_WORD.test(tokens.textOf(value))) {
232
+ throw new GmlSyntaxError(
233
+ SYNTAX_STRUCTURE_CODE,
234
+ `expected a value for key "${tokens.textOf(i)}" at line ${tokens.line[i]}, found ${describeToken(tokens, value)}`,
235
+ tokens.line[value]
236
+ );
237
+ }
238
+ i = value + 1;
239
+ }
240
+ if (stack.length > 0) {
241
+ const open = stack[stack.length - 1];
242
+ throw new GmlSyntaxError(
243
+ SYNTAX_BRACKET_CODE,
244
+ `unexpected end of input: ${stack.length} bracket(s) still open, the last opened at line ${tokens.line[open]}`,
245
+ tokens.line[open]
246
+ );
247
+ }
248
+ tokens.match = match;
249
+ }
250
+ function describeToken(tokens, i) {
251
+ switch (tokens.kind[i]) {
252
+ case TOKEN_OPEN:
253
+ return '"["';
254
+ case TOKEN_CLOSE:
255
+ return '"]"';
256
+ case TOKEN_STRING:
257
+ return `the string "${tokens.textOf(i)}"`;
258
+ default:
259
+ return `"${tokens.textOf(i)}"`;
260
+ }
261
+ }
262
+ function numberOfText(text) {
263
+ if (NON_FINITE_TEXT.test(text)) {
264
+ const lower = text.toLowerCase();
265
+ if (lower.endsWith("nan")) {
266
+ return NaN;
267
+ }
268
+ return lower.startsWith("-") ? -Infinity : Infinity;
269
+ }
270
+ return Number(text);
271
+ }
272
+ function isGmlKey(text) {
273
+ return STRICT_KEY_TEXT.test(text);
274
+ }
275
+ function mangleGmlKey(text) {
276
+ const body = text.replace(/[^0-9A-Za-z_]/g, "_");
277
+ return /^[A-Za-z]/.test(body) ? body : `x${body}`;
278
+ }
279
+ function parseRecord(tokens, open) {
280
+ const root = /* @__PURE__ */ Object.create(null);
281
+ const stack = [
282
+ { record: root, listKeys: null, close: tokens.match[open], next: open + 1, key: null }
283
+ ];
284
+ while (stack.length > 0) {
285
+ const frame = stack[stack.length - 1];
286
+ if (frame.next >= frame.close) {
287
+ stack.pop();
288
+ if (frame.key !== null) {
289
+ storeValue(stack[stack.length - 1], frame.key, frame.record);
290
+ }
291
+ continue;
292
+ }
293
+ const p = frame.next;
294
+ frame.next = tokens.nextPair(p);
295
+ const key = tokens.textOf(p);
296
+ const v = p + 1;
297
+ if (tokens.kind[v] === TOKEN_STRING) {
298
+ const text = tokens.stringOf(v);
299
+ if (text === LIST_START_MARKER) {
300
+ frame.record[key] = [];
301
+ frame.listKeys ??= /* @__PURE__ */ new Set();
302
+ frame.listKeys.add(key);
303
+ continue;
304
+ }
305
+ storeValue(frame, key, text === EMPTY_LIST_TEXT || text === EMPTY_TUPLE_TEXT ? [] : text);
306
+ } else if (tokens.kind[v] === TOKEN_OPEN) {
307
+ stack.push({
308
+ record: /* @__PURE__ */ Object.create(null),
309
+ listKeys: null,
310
+ close: tokens.match[v],
311
+ next: v + 1,
312
+ key
313
+ });
314
+ } else {
315
+ storeValue(frame, key, numberOfText(tokens.textOf(v)));
316
+ }
317
+ }
318
+ return root;
319
+ }
320
+ function storeValue(frame, key, value) {
321
+ const { record } = frame;
322
+ if (frame.listKeys !== null && frame.listKeys.has(key)) {
323
+ record[key].push(value);
324
+ } else if (key in record) {
325
+ record[key] = [record[key], value];
326
+ frame.listKeys ??= /* @__PURE__ */ new Set();
327
+ frame.listKeys.add(key);
328
+ } else {
329
+ record[key] = value;
330
+ }
331
+ }
332
+ function scalarOrRecord(tokens, v) {
333
+ return tokens.kind[v] === TOKEN_OPEN ? parseRecord(tokens, v) : numberOfText(tokens.textOf(v));
334
+ }
335
+ const RECORD_NUMBER_TYPE_CODE = "W_GML_RECORD_NUMBER_TYPE";
336
+ const RECORD_BOOLEAN_CODE = "W_GML_RECORD_BOOLEAN";
337
+ const RECORD_NULL_CODE = "W_GML_RECORD_NULL";
338
+ const NESTED_ARRAY_CODE = "E_GML_NESTED_ARRAY";
339
+ const JSON_ARRAY_CODE = "W_GML_JSON_ARRAY";
340
+ const INVALID_KEY_CODE = "E_GML_INVALID_KEY";
341
+ const RESERVED_KEY_CODE = "E_GML_RESERVED_KEY";
342
+ const KEY_MANGLED_CODE = "W_GML_KEY_MANGLED";
343
+ const POSITION_COMPONENTS_CODE = "W_GML_POSITION_COMPONENTS";
344
+ const GRAPHICS_OVERRIDDEN_CODE = "W_GML_GRAPHICS_OVERRIDDEN";
345
+ const GRAPHICS_CONFLICT_CODE = "E_GML_GRAPHICS_CONFLICT";
346
+ const DEFAULT_WEIGHT_KEY = "value";
347
+ const KEPT_ROLES = /* @__PURE__ */ new Set(["label", "id", "key"]);
348
+ const ROLE_NAMES = Object.freeze({
349
+ label: "label",
350
+ id: "id",
351
+ key: "key"
352
+ });
353
+ const GML_CAPABILITIES = capabilities({
354
+ mixedDirection: false,
355
+ multiEdges: true,
356
+ selfLoops: true,
357
+ edgeIds: "optional",
358
+ idCharset: "integer",
359
+ dtypes: ["i32", "f64", "string", "dict", "json"],
360
+ components: false,
361
+ lists: true,
362
+ json: true,
363
+ defaults: false,
364
+ options: false,
365
+ hierarchy: false,
366
+ temporal: "none",
367
+ graphAttributes: true,
368
+ positions: true,
369
+ viz: false
370
+ });
371
+ const SKIPPED_ROLES = /* @__PURE__ */ new Set([
372
+ "directed",
373
+ "pair",
374
+ "mutual",
375
+ "weight",
376
+ "timeText",
377
+ "originalId",
378
+ "position",
379
+ "color",
380
+ "size",
381
+ "shape",
382
+ "thickness",
383
+ "parent",
384
+ "parents",
385
+ "start",
386
+ "end",
387
+ "timestamp",
388
+ "timestamps",
389
+ "spells",
390
+ "open"
391
+ ]);
392
+ const NODE_RESERVED = /* @__PURE__ */ new Set(["id"]);
393
+ const EDGE_RESERVED = /* @__PURE__ */ new Set(["source", "target"]);
394
+ const GRAPH_RESERVED = /* @__PURE__ */ new Set(["node", "edge", "directed", "multigraph"]);
395
+ const TOP_RESERVED = /* @__PURE__ */ new Set(["graph"]);
396
+ const NUMBER_TEXT = /^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
397
+ function planOf(snapshot, options) {
398
+ const common = resolveExportOptions(options);
399
+ const mode = options?.sanitizeKeys ?? "error";
400
+ if (mode !== "error" && mode !== "mangle") {
401
+ throw new GraphFormatError(
402
+ "E_UNSUPPORTED",
403
+ `option sanitizeKeys: ${JSON.stringify(mode)} is not "error" or "mangle"`,
404
+ {
405
+ option: "sanitizeKeys",
406
+ found: mode
407
+ }
408
+ );
409
+ }
410
+ let weightKey = options?.weightKey;
411
+ if (weightKey === void 0) {
412
+ const origin = snapshot.meta.weightOrigin;
413
+ weightKey = origin !== null && origin.format === "gml" && origin.id !== null ? origin.id : DEFAULT_WEIGHT_KEY;
414
+ } else if (typeof weightKey !== "string" || !isGmlKey(weightKey)) {
415
+ throw new GraphFormatError("E_UNSUPPORTED", `option weightKey: ${JSON.stringify(weightKey)} is not a GML key`, {
416
+ option: "weightKey",
417
+ found: weightKey
418
+ });
419
+ }
420
+ return { common, weightKey, mangle: mode === "mangle" };
421
+ }
422
+ function isWritten(column) {
423
+ const { role } = column.meta;
424
+ return role === null || !SKIPPED_ROLES.has(role);
425
+ }
426
+ function preferredKey(meta) {
427
+ const { origin } = meta;
428
+ return origin !== null && origin.format === "gml" && origin.id !== null ? origin.id : meta.name;
429
+ }
430
+ function hasWeights(snapshot) {
431
+ return snapshot.flags.weighted;
432
+ }
433
+ function isTopLevel(column) {
434
+ return column.meta.extra.gmlTopLevel === true;
435
+ }
436
+ function versionText(text) {
437
+ return NUMBER_TEXT.test(text) ? text : quoteGmlString(text);
438
+ }
439
+ function selectColumns(table, reserved, label, plan, notes, filter = () => true) {
440
+ const used = new Set(reserved);
441
+ const out = [];
442
+ for (const column of table) {
443
+ if (!isWritten(column) || !filter(column)) {
444
+ continue;
445
+ }
446
+ const preferred = preferredKey(column.meta);
447
+ let key = preferred;
448
+ const valid = isGmlKey(preferred);
449
+ const taken = used.has(preferred);
450
+ if (!valid || taken) {
451
+ const code = valid ? RESERVED_KEY_CODE : INVALID_KEY_CODE;
452
+ let why = "is not a GML key";
453
+ if (valid) {
454
+ why = reserved.has(preferred) ? "collides with a structural GML key" : "repeats another column's key";
455
+ }
456
+ if (!plan.mangle) {
457
+ if (notes === null) {
458
+ throw new GraphFormatError(
459
+ "E_UNSUPPORTED",
460
+ `${label} column "${column.meta.name}" ${why}; pass sanitizeKeys: "mangle" to rewrite it`,
461
+ {
462
+ reason: "gml key",
463
+ column: column.meta.name,
464
+ key: preferred
465
+ }
466
+ );
467
+ }
468
+ notes.push(
469
+ note(
470
+ code,
471
+ `${label} column "${column.meta.name}" ${why}; export() will throw unless sanitizeKeys is "mangle"`,
472
+ column.meta.name
473
+ )
474
+ );
475
+ continue;
476
+ }
477
+ key = uniqueKey(valid ? preferred : mangleGmlKey(preferred), used);
478
+ notes?.push(
479
+ note(
480
+ KEY_MANGLED_CODE,
481
+ `${label} column "${column.meta.name}" is written as "${key}"`,
482
+ column.meta.name
483
+ )
484
+ );
485
+ }
486
+ used.add(key);
487
+ out.push({ column, key });
488
+ }
489
+ return out;
490
+ }
491
+ function uniqueKey(base, used) {
492
+ let candidate = base;
493
+ for (let k = 2; used.has(candidate); k++) {
494
+ candidate = `${base}_${k}`;
495
+ }
496
+ return candidate;
497
+ }
498
+ function note(code, message, column = null, count = null) {
499
+ return Object.freeze({ code, message, column, count });
500
+ }
501
+ function inspectJson(value, stats, inArray, seen) {
502
+ if (value === null) {
503
+ if (!seen.nulls) {
504
+ seen.nulls = true;
505
+ stats.nulls++;
506
+ }
507
+ return;
508
+ }
509
+ switch (typeof value) {
510
+ case "number":
511
+ if (!seen.numbers) {
512
+ seen.numbers = true;
513
+ stats.numbers++;
514
+ }
515
+ return;
516
+ case "boolean":
517
+ if (!seen.booleans) {
518
+ seen.booleans = true;
519
+ stats.booleans++;
520
+ }
521
+ return;
522
+ case "string":
523
+ return;
524
+ }
525
+ if (Array.isArray(value)) {
526
+ if (inArray && !seen.nested) {
527
+ seen.nested = true;
528
+ stats.nestedArrays++;
529
+ }
530
+ for (const item of value) {
531
+ inspectJson(item, stats, true, seen);
532
+ }
533
+ return;
534
+ }
535
+ const record = value;
536
+ for (const key of Object.keys(record)) {
537
+ if (!isGmlKey(key)) {
538
+ stats.invalidKeys++;
539
+ }
540
+ inspectJson(record[key], stats, false, seen);
541
+ }
542
+ }
543
+ function jsonNotes(columns, label, plan, notes) {
544
+ for (const { column } of columns) {
545
+ const stats = jsonStatsOf(column);
546
+ if (stats === null) {
547
+ continue;
548
+ }
549
+ const { name } = column.meta;
550
+ const where = `${label} column "${name}"`;
551
+ if (stats.numbers > 0) {
552
+ notes.push(
553
+ note(
554
+ RECORD_NUMBER_TYPE_CODE,
555
+ `${where} holds numbers in ${stats.numbers} row(s); GML records cannot keep the int / real distinction`,
556
+ name,
557
+ stats.numbers
558
+ )
559
+ );
560
+ }
561
+ if (stats.booleans > 0) {
562
+ notes.push(
563
+ note(
564
+ RECORD_BOOLEAN_CODE,
565
+ `${where} holds booleans in ${stats.booleans} row(s); written as 1 / 0`,
566
+ name,
567
+ stats.booleans
568
+ )
569
+ );
570
+ }
571
+ if (stats.nulls > 0) {
572
+ notes.push(
573
+ note(
574
+ RECORD_NULL_CODE,
575
+ `${where} holds nulls in ${stats.nulls} row(s); GML has no null, the key is omitted`,
576
+ name,
577
+ stats.nulls
578
+ )
579
+ );
580
+ }
581
+ if (stats.arrays > 0) {
582
+ notes.push(
583
+ note(
584
+ JSON_ARRAY_CODE,
585
+ `${where} holds arrays as values in ${stats.arrays} row(s); written as repeated keys, they re-import as a list`,
586
+ name,
587
+ stats.arrays
588
+ )
589
+ );
590
+ }
591
+ if (stats.nestedArrays > 0) {
592
+ notes.push(
593
+ note(
594
+ NESTED_ARRAY_CODE,
595
+ `${where} holds arrays nested in arrays in ${stats.nestedArrays} row(s); GML cannot write them and export() will throw`,
596
+ name,
597
+ stats.nestedArrays
598
+ )
599
+ );
600
+ }
601
+ if (stats.invalidKeys > 0) {
602
+ notes.push(
603
+ plan.mangle ? note(
604
+ KEY_MANGLED_CODE,
605
+ `${where}: ${stats.invalidKeys} record key(s) are not GML keys and are rewritten`,
606
+ name,
607
+ stats.invalidKeys
608
+ ) : note(
609
+ INVALID_KEY_CODE,
610
+ `${where}: ${stats.invalidKeys} record key(s) are not GML keys; export() will throw unless sanitizeKeys is "mangle"`,
611
+ name,
612
+ stats.invalidKeys
613
+ )
614
+ );
615
+ }
616
+ }
617
+ }
618
+ function jsonStatsOf(column) {
619
+ const stats = { numbers: 0, booleans: 0, nulls: 0, nestedArrays: 0, arrays: 0, invalidKeys: 0 };
620
+ if (column.dtype === "json") {
621
+ for (let r = 0; r < column.length; r++) {
622
+ if (!column.isSet(r)) {
623
+ continue;
624
+ }
625
+ const value = column.values[r];
626
+ const seen = { numbers: false, booleans: false, nulls: false, nested: false };
627
+ if (Array.isArray(value)) {
628
+ stats.arrays++;
629
+ for (const item of value) {
630
+ inspectJson(item, stats, true, seen);
631
+ }
632
+ } else {
633
+ inspectJson(value, stats, false, seen);
634
+ }
635
+ }
636
+ return stats;
637
+ }
638
+ if (column.dtype === "list" && column.meta.itemDtype === "json") {
639
+ for (let r = 0; r < column.length; r++) {
640
+ if (!column.isSet(r)) {
641
+ continue;
642
+ }
643
+ const seen = { numbers: false, booleans: false, nulls: false, nested: false };
644
+ for (const item of column.sliceOf(r)) {
645
+ inspectJson(item, stats, true, seen);
646
+ }
647
+ }
648
+ return stats;
649
+ }
650
+ return null;
651
+ }
652
+ function positionColumn(snapshot) {
653
+ const column = snapshot.nodes.byRole("position");
654
+ if (column === null) {
655
+ return null;
656
+ }
657
+ switch (column.dtype) {
658
+ case "f32":
659
+ case "f64":
660
+ case "i32":
661
+ case "u32":
662
+ case "u8":
663
+ return column;
664
+ default:
665
+ return null;
666
+ }
667
+ }
668
+ function graphicsColumn(snapshot) {
669
+ return snapshot.nodes.typed("graphics", "json");
670
+ }
671
+ function graphicsNotes(snapshot, notes) {
672
+ const position = positionColumn(snapshot);
673
+ if (position === null) {
674
+ return;
675
+ }
676
+ if (position.meta.components > 3) {
677
+ notes.push(
678
+ note(
679
+ POSITION_COMPONENTS_CODE,
680
+ `node column "${position.meta.name}" has ${position.meta.components} components; only x, y and z are written`,
681
+ position.meta.name,
682
+ position.length - position.nullCount
683
+ )
684
+ );
685
+ }
686
+ const graphics = graphicsColumn(snapshot);
687
+ if (graphics === null) {
688
+ return;
689
+ }
690
+ let overridden = 0;
691
+ let conflicts = 0;
692
+ for (let r = 0; r < snapshot.nodeCount; r++) {
693
+ if (!position.isSet(r) || !graphics.isSet(r)) {
694
+ continue;
695
+ }
696
+ const value = graphics.values[r];
697
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
698
+ conflicts++;
699
+ } else if ("x" in value || "y" in value || "z" in value) {
700
+ overridden++;
701
+ }
702
+ }
703
+ if (overridden > 0) {
704
+ notes.push(
705
+ note(
706
+ GRAPHICS_OVERRIDDEN_CODE,
707
+ `${overridden} node graphics record(s) have x / y / z keys the position column replaces`,
708
+ "graphics",
709
+ overridden
710
+ )
711
+ );
712
+ }
713
+ if (conflicts > 0) {
714
+ notes.push(
715
+ note(
716
+ GRAPHICS_CONFLICT_CODE,
717
+ `${conflicts} node graphics value(s) are not records and cannot hold the position; export() will throw`,
718
+ "graphics",
719
+ conflicts
720
+ )
721
+ );
722
+ }
723
+ }
724
+ function reimportNotes(columns, domain, notes) {
725
+ for (const { column, key } of columns) {
726
+ const { meta } = column;
727
+ const set = column.length - column.nullCount;
728
+ if (set === 0) {
729
+ notes.push(
730
+ note(
731
+ LOSS.EMPTY_COLUMN,
732
+ `${domain} column "${meta.name}" has no set cell and is not written: GML writes cells, never declarations`,
733
+ meta.name,
734
+ 0
735
+ )
736
+ );
737
+ continue;
738
+ }
739
+ if (meta.role === null && (key === "label" || domain === "edge" && (key === "id" || key === "key"))) {
740
+ notes.push(
741
+ note(
742
+ LOSS.ROLE_ASSUMED,
743
+ `${domain} column "${meta.name}" is written under "${key}" and reads back with the ${key === "label" ? "label" : key} role`,
744
+ meta.name,
745
+ set
746
+ )
747
+ );
748
+ continue;
749
+ }
750
+ if (column.dtype === "list" || !(column.dtype === "string" || column.dtype === "dict")) {
751
+ continue;
752
+ }
753
+ const heuristic = new DictHeuristic(DICT_SAMPLE_ROWS);
754
+ for (let r = 0; r < column.length && !heuristic.decided; r++) {
755
+ if (column.isSet(r)) {
756
+ heuristic.observe(column.value(r));
757
+ }
758
+ }
759
+ const readsAsDict = heuristic.decide() === "dict";
760
+ if (column.dtype === "string" && readsAsDict) {
761
+ notes.push(
762
+ note(
763
+ LOSS.STORAGE_CLASS,
764
+ `${domain} column "${meta.name}" reads back as dict (low cardinality)`,
765
+ meta.name,
766
+ null
767
+ )
768
+ );
769
+ } else if (column.dtype === "dict" && !readsAsDict) {
770
+ notes.push(
771
+ note(
772
+ LOSS.STORAGE_CLASS,
773
+ `${domain} column "${meta.name}" reads back as string (cardinality too high for a dict)`,
774
+ meta.name,
775
+ null
776
+ )
777
+ );
778
+ }
779
+ }
780
+ }
781
+ function positionNameNote(snapshot, nodeColumns, notes) {
782
+ const position = positionColumn(snapshot);
783
+ if (position === null) {
784
+ return;
785
+ }
786
+ const { name } = position.meta;
787
+ const plainPosition = nodeColumns.some((c) => c.key === "position");
788
+ const reimportName = plainPosition ? "position#graphics" : "position";
789
+ if (name !== reimportName) {
790
+ notes.push(
791
+ note(
792
+ LOSS.COLUMN_NAME_CHANGED,
793
+ `node column "${name}" (position) is written as the graphics x / y / z keys and reads back as "${reimportName}"`,
794
+ name,
795
+ position.length - position.nullCount
796
+ )
797
+ );
798
+ }
799
+ }
800
+ function check(snapshot, options) {
801
+ const plan = planOf(snapshot, options);
802
+ const notes = checkCapabilities(snapshot, GML_CAPABILITIES, plan.common, {
803
+ positionDtype: "f64",
804
+ roles: KEPT_ROLES,
805
+ roleNames: ROLE_NAMES
806
+ });
807
+ const nodeReserved = new Set(NODE_RESERVED);
808
+ if (plan.common.sanitizeIds === "mangle" && countMangled(snapshot) > 0 || snapshot.nodes.byRole("originalId") !== null) {
809
+ nodeReserved.add(ORIGINAL_ID_KEY);
810
+ }
811
+ const edgeIds = snapshot.edges.byRole("id");
812
+ const edgeReserved = edgeReservedKeys(snapshot, plan, edgeIds);
813
+ const topReserved = topReservedKeys(snapshot);
814
+ const nodeColumns = selectColumns(snapshot.nodes, nodeReserved, "node", plan, notes);
815
+ const edgeColumns = selectColumns(snapshot.edges, edgeReserved, "edge", plan, notes, (c) => c !== edgeIds);
816
+ const graphColumns = selectColumns(snapshot.graph, GRAPH_RESERVED, "graph", plan, notes, (c) => !isTopLevel(c));
817
+ const topColumns = selectColumns(snapshot.graph, topReserved, "top-level", plan, notes, isTopLevel);
818
+ jsonNotes(nodeColumns, "node", plan, notes);
819
+ jsonNotes(edgeColumns, "edge", plan, notes);
820
+ jsonNotes(graphColumns, "graph", plan, notes);
821
+ jsonNotes(topColumns, "top-level", plan, notes);
822
+ graphicsNotes(snapshot, notes);
823
+ positionNameNote(snapshot, nodeColumns, notes);
824
+ reimportNotes(nodeColumns, "node", notes);
825
+ reimportNotes(edgeColumns, "edge", notes);
826
+ if (!hasWeights(snapshot)) {
827
+ const clash = edgeColumns.find((c) => c.key === plan.weightKey);
828
+ if (clash !== void 0) {
829
+ notes.push(
830
+ note(
831
+ LOSS.WEIGHT_KEY_CLASH,
832
+ `edge column "${clash.column.meta.name}" is written under "${plan.weightKey}", the key the importer reads THE weight from; it reads back as the weight, not as a column`,
833
+ clash.column.meta.name,
834
+ clash.column.length - clash.column.nullCount
835
+ )
836
+ );
837
+ }
838
+ }
839
+ const folding = pairFolding(snapshot);
840
+ if (folding.mutualCount > 0) {
841
+ notes.push(
842
+ note(
843
+ LOSS.MUTUAL_EXPANDED,
844
+ `${folding.mutualCount} mutual pair(s) are written as two directed edges; the mutual mark is lost`,
845
+ null,
846
+ folding.mutualCount
847
+ )
848
+ );
849
+ }
850
+ return Object.freeze(notes);
851
+ }
852
+ function edgeReservedKeys(snapshot, plan, edgeIds) {
853
+ const reserved = new Set(EDGE_RESERVED);
854
+ if (hasWeights(snapshot)) {
855
+ reserved.add(plan.weightKey);
856
+ }
857
+ if (edgeIds !== null) {
858
+ reserved.add("id");
859
+ }
860
+ return reserved;
861
+ }
862
+ function topReservedKeys(snapshot) {
863
+ const reserved = new Set(TOP_RESERVED);
864
+ if (snapshot.meta.creator !== null) {
865
+ reserved.add("Creator");
866
+ }
867
+ if (snapshot.meta.sourceFormat === "gml" && snapshot.meta.sourceVersion !== null) {
868
+ reserved.add("Version");
869
+ }
870
+ return reserved;
871
+ }
872
+ function countMangled(snapshot) {
873
+ let count = 0;
874
+ for (let i = 0; i < snapshot.nodeCount; i++) {
875
+ const id = snapshot.ids.idOf(i);
876
+ if (typeof id !== "number" || !Number.isSafeInteger(id)) {
877
+ count++;
878
+ }
879
+ }
880
+ return count;
881
+ }
882
+ function gmlRealText(value, dtype = "f64") {
883
+ return formatGmlReal(value, dtype);
884
+ }
885
+ function numberText(value, meta) {
886
+ switch (meta.dtype) {
887
+ case "i32":
888
+ case "u32":
889
+ case "u8":
890
+ return formatInteger(value);
891
+ case "f32":
892
+ return gmlRealText(value, "f32");
893
+ default:
894
+ if (meta.origin?.type === "int" && Number.isInteger(value)) {
895
+ return formatInteger(value);
896
+ }
897
+ return gmlRealText(value, "f64");
898
+ }
899
+ }
900
+ function jsonScalarText(value) {
901
+ switch (typeof value) {
902
+ case "number":
903
+ return Number.isInteger(value) ? formatInteger(value) : gmlRealText(value, "f64");
904
+ case "boolean":
905
+ return value ? "1" : "0";
906
+ default:
907
+ return quoteGmlString(value);
908
+ }
909
+ }
910
+ function itemText(item, itemDtype, meta) {
911
+ switch (typeof item) {
912
+ case "number":
913
+ switch (itemDtype) {
914
+ case "i32":
915
+ case "u32":
916
+ case "u8":
917
+ return formatInteger(item);
918
+ case "f32":
919
+ return gmlRealText(item, "f32");
920
+ case "json":
921
+ return jsonScalarText(item);
922
+ default:
923
+ return meta.origin?.type === "int" && Number.isInteger(item) ? formatInteger(item) : gmlRealText(item, "f64");
924
+ }
925
+ case "boolean":
926
+ return item ? "1" : "0";
927
+ case "string":
928
+ return quoteGmlString(item);
929
+ default:
930
+ throw new GraphFormatError(
931
+ "E_COLUMN_TYPE",
932
+ `column "${meta.name}": a ${typeof item} list item cannot be written as GML`,
933
+ {
934
+ column: meta.name,
935
+ found: typeof item
936
+ }
937
+ );
938
+ }
939
+ }
940
+ class GmlWriter {
941
+ /**
942
+ * Create a writer.
943
+ * @param mangle - whether record keys are rewritten rather than refused
944
+ */
945
+ constructor(mangle) {
946
+ this.lines = [];
947
+ this.mangle = mangle;
948
+ }
949
+ /**
950
+ * Take the lines written so far as one string.
951
+ * @returns the text; the writer is empty afterwards
952
+ */
953
+ take() {
954
+ const text = this.lines.join("");
955
+ this.lines.length = 0;
956
+ return text;
957
+ }
958
+ /**
959
+ * Write one `key value` line.
960
+ * @param indent - the indentation
961
+ * @param key - the key
962
+ * @param value - the value text
963
+ */
964
+ line(indent, key, value) {
965
+ this.lines.push(`${indent}${key} ${value}
966
+ `);
967
+ }
968
+ /**
969
+ * Write a raw line.
970
+ * @param text - the line without its terminator
971
+ */
972
+ raw(text) {
973
+ this.lines.push(`${text}
974
+ `);
975
+ }
976
+ /**
977
+ * Write one column cell of a row.
978
+ * @param indent - the indentation
979
+ * @param key - the key
980
+ * @param column - the column
981
+ * @param row - the row
982
+ */
983
+ cell(indent, key, column, row) {
984
+ const { meta, dtype } = column;
985
+ switch (dtype) {
986
+ case "f32":
987
+ case "f64":
988
+ case "i32":
989
+ case "u32":
990
+ case "u8": {
991
+ const { components } = meta;
992
+ if (components === 1) {
993
+ this.line(indent, key, numberText(column.data[row], meta));
994
+ } else {
995
+ for (let k = 0; k < components; k++) {
996
+ this.line(indent, key, numberText(column.data[row * components + k], meta));
997
+ }
998
+ }
999
+ return;
1000
+ }
1001
+ case "bool":
1002
+ this.line(indent, key, column.value(row) === true ? "1" : "0");
1003
+ return;
1004
+ case "dict":
1005
+ case "string":
1006
+ this.line(indent, key, quoteGmlString(column.value(row) ?? ""));
1007
+ return;
1008
+ case "list":
1009
+ this.list(indent, key, column.sliceOf(row), meta.itemDtype ?? "string", meta);
1010
+ return;
1011
+ case "json":
1012
+ this.json(indent, key, column.values[row], meta.name);
1013
+ return;
1014
+ default: {
1015
+ const name = dtype;
1016
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
1017
+ }
1018
+ }
1019
+ }
1020
+ /**
1021
+ * Write a list as repeated keys with the NetworkX conventions.
1022
+ * @param indent - the indentation
1023
+ * @param key - the key
1024
+ * @param items - the items
1025
+ * @param itemDtype - the item dtype
1026
+ * @param meta - the column metadata
1027
+ */
1028
+ list(indent, key, items, itemDtype, meta) {
1029
+ if (items.length === 0) {
1030
+ this.line(indent, key, quoteGmlString(EMPTY_LIST_TEXT));
1031
+ return;
1032
+ }
1033
+ if (items.length === 1) {
1034
+ this.line(indent, key, quoteGmlString(LIST_START_MARKER));
1035
+ }
1036
+ for (const item of items) {
1037
+ if (itemDtype === "json" && typeof item === "object") {
1038
+ this.json(indent, key, item, meta.name, true);
1039
+ } else {
1040
+ this.line(indent, key, itemText(item, itemDtype, meta));
1041
+ }
1042
+ }
1043
+ }
1044
+ /**
1045
+ * Write a JSON value: a record for an object, repeated keys for an array, a scalar otherwise;
1046
+ * null writes nothing.
1047
+ * @param indent - the indentation
1048
+ * @param key - the key
1049
+ * @param value - the value
1050
+ * @param column - the column name, for errors
1051
+ * @param inArray - whether the value is an array item (an array here has no GML spelling)
1052
+ */
1053
+ json(indent, key, value, column, inArray = false) {
1054
+ if (value === null || value === void 0) {
1055
+ return;
1056
+ }
1057
+ if (Array.isArray(value)) {
1058
+ if (inArray) {
1059
+ throw new GraphFormatError(
1060
+ "E_UNSUPPORTED",
1061
+ `column "${column}": an array nested in an array cannot be written as GML`,
1062
+ {
1063
+ reason: "nested array",
1064
+ column
1065
+ }
1066
+ );
1067
+ }
1068
+ if (value.length === 0) {
1069
+ this.line(indent, key, quoteGmlString(EMPTY_LIST_TEXT));
1070
+ return;
1071
+ }
1072
+ if (value.length === 1) {
1073
+ this.line(indent, key, quoteGmlString(LIST_START_MARKER));
1074
+ }
1075
+ for (const item of value) {
1076
+ this.json(indent, key, item, column, true);
1077
+ }
1078
+ return;
1079
+ }
1080
+ if (typeof value === "object") {
1081
+ this.record(indent, key, value, column);
1082
+ return;
1083
+ }
1084
+ if (typeof value === "number" || typeof value === "string" || typeof value === "boolean") {
1085
+ this.line(indent, key, jsonScalarText(value));
1086
+ return;
1087
+ }
1088
+ throw new GraphFormatError("E_COLUMN_TYPE", `column "${column}": a ${typeof value} cannot be written as GML`, {
1089
+ column,
1090
+ found: typeof value
1091
+ });
1092
+ }
1093
+ /**
1094
+ * Write a record `key [ ... ]`.
1095
+ * @param indent - the indentation
1096
+ * @param key - the key
1097
+ * @param record - the object
1098
+ * @param column - the column name, for errors
1099
+ * @param extra - lines to write first (the position of a graphics record), or null
1100
+ */
1101
+ record(indent, key, record, column, extra = null) {
1102
+ this.raw(`${indent}${key} [`);
1103
+ const inner = `${indent} `;
1104
+ const used = /* @__PURE__ */ new Set();
1105
+ if (extra !== null) {
1106
+ for (const [k, v] of extra) {
1107
+ this.line(inner, k, v);
1108
+ used.add(k);
1109
+ }
1110
+ }
1111
+ for (const name of Object.keys(record)) {
1112
+ if (used.has(name) && extra !== null && (name === "x" || name === "y" || name === "z")) {
1113
+ continue;
1114
+ }
1115
+ const written = this.recordKey(name, used, column);
1116
+ used.add(written);
1117
+ this.json(inner, written, record[name], column);
1118
+ }
1119
+ this.raw(`${indent}]`);
1120
+ }
1121
+ /**
1122
+ * The key a record field is written under.
1123
+ * @param name - the field name
1124
+ * @param used - the keys already written in the record
1125
+ * @param column - the column name, for errors
1126
+ * @returns a valid, unused key; E_UNSUPPORTED for an invalid key unless mangling
1127
+ */
1128
+ recordKey(name, used, column) {
1129
+ if (isGmlKey(name) && !used.has(name)) {
1130
+ return name;
1131
+ }
1132
+ if (!this.mangle) {
1133
+ throw new GraphFormatError(
1134
+ "E_UNSUPPORTED",
1135
+ `column "${column}": record key "${name}" ${isGmlKey(name) ? "repeats a key" : "is not a GML key"}; pass sanitizeKeys: "mangle" to rewrite it`,
1136
+ {
1137
+ reason: "gml key",
1138
+ column,
1139
+ key: name
1140
+ }
1141
+ );
1142
+ }
1143
+ return uniqueKey(isGmlKey(name) ? name : mangleGmlKey(name), used);
1144
+ }
1145
+ }
1146
+ function contextOf(snapshot, options) {
1147
+ const plan = planOf(snapshot, options);
1148
+ const ids = sanitizeIds(snapshot, "integer", plan.common.sanitizeIds);
1149
+ const mixed = countMixedEdges(snapshot);
1150
+ if (mixed > 0 && plan.common.onMixedDirection === "error") {
1151
+ throw new GraphFormatError(
1152
+ "E_DIRECTED",
1153
+ `${mixed} undirected edge(s) in a directed graph; GML has no mixed direction (onMixedDirection: "error")`,
1154
+ {
1155
+ reason: "mixed direction",
1156
+ count: mixed
1157
+ }
1158
+ );
1159
+ }
1160
+ const foldPairs = mixed > 0;
1161
+ const writeDirected = snapshot.directed && plan.common.onMixedDirection !== "undirected";
1162
+ const originalIds = snapshot.nodes.byRole("originalId");
1163
+ const nodeReserved = new Set(NODE_RESERVED);
1164
+ if (ids.changed > 0 || originalIds !== null) {
1165
+ nodeReserved.add(ORIGINAL_ID_KEY);
1166
+ }
1167
+ const edgeIds = snapshot.edges.byRole("id");
1168
+ const edgeReserved = edgeReservedKeys(snapshot, plan, edgeIds);
1169
+ const topReserved = topReservedKeys(snapshot);
1170
+ return {
1171
+ snapshot,
1172
+ plan,
1173
+ ids,
1174
+ writeDirected,
1175
+ foldPairs,
1176
+ nodeColumns: selectColumns(
1177
+ snapshot.nodes,
1178
+ nodeReserved,
1179
+ "node",
1180
+ plan,
1181
+ null,
1182
+ (c) => !(c.meta.name === "graphics" && c.dtype === "json")
1183
+ ),
1184
+ edgeColumns: selectColumns(snapshot.edges, edgeReserved, "edge", plan, null, (c) => c !== edgeIds),
1185
+ graphColumns: selectColumns(snapshot.graph, GRAPH_RESERVED, "graph", plan, null, (c) => !isTopLevel(c)),
1186
+ topColumns: selectColumns(snapshot.graph, topReserved, "top-level", plan, null, isTopLevel),
1187
+ position: positionColumn(snapshot),
1188
+ graphics: graphicsColumn(snapshot),
1189
+ originalIds,
1190
+ edgeIds,
1191
+ weights: explicitWeights(snapshot),
1192
+ folding: pairFolding(snapshot)
1193
+ };
1194
+ }
1195
+ function originalIdText(id) {
1196
+ if (typeof id === "string") {
1197
+ return quoteGmlString(id);
1198
+ }
1199
+ return Number.isSafeInteger(id) ? formatInteger(id) : gmlRealText(id, "f64");
1200
+ }
1201
+ function* gmlParts(context) {
1202
+ const { snapshot, plan, ids } = context;
1203
+ const w = new GmlWriter(plan.mangle);
1204
+ const { meta } = snapshot;
1205
+ if (meta.creator !== null) {
1206
+ w.line("", "Creator", quoteGmlString(meta.creator));
1207
+ }
1208
+ if (meta.sourceFormat === "gml" && meta.sourceVersion !== null) {
1209
+ w.line("", "Version", versionText(meta.sourceVersion));
1210
+ }
1211
+ for (const { column, key } of context.topColumns) {
1212
+ if (column.isSet(0)) {
1213
+ w.cell("", key, column, 0);
1214
+ }
1215
+ }
1216
+ w.raw("graph [");
1217
+ w.line(" ", "directed", context.writeDirected ? "1" : "0");
1218
+ const multigraph = meta.declaredMultigraph ?? (snapshot.flags.multigraph ? true : null);
1219
+ if (multigraph !== null) {
1220
+ w.line(" ", "multigraph", multigraph ? "1" : "0");
1221
+ }
1222
+ for (const { column, key } of context.graphColumns) {
1223
+ if (column.isSet(0)) {
1224
+ w.cell(" ", key, column, 0);
1225
+ }
1226
+ }
1227
+ yield w.take();
1228
+ for (let i = 0; i < snapshot.nodeCount; i++) {
1229
+ w.raw(" node [");
1230
+ w.line(" ", "id", formatInteger(ids.idAt(i)));
1231
+ if (ids.isChanged(i)) {
1232
+ w.line(" ", ORIGINAL_ID_KEY, originalIdText(ids.originalAt(i)));
1233
+ } else if (context.originalIds !== null && context.originalIds.isSet(i)) {
1234
+ const original = context.originalIds.value(i);
1235
+ if (typeof original === "string" || typeof original === "number") {
1236
+ w.line(" ", ORIGINAL_ID_KEY, originalIdText(original));
1237
+ }
1238
+ }
1239
+ for (const { column, key } of context.nodeColumns) {
1240
+ if (column.isSet(i)) {
1241
+ w.cell(" ", key, column, i);
1242
+ }
1243
+ }
1244
+ writeGraphics(w, context, i);
1245
+ w.raw(" ]");
1246
+ yield w.take();
1247
+ }
1248
+ const el = snapshot.edgeList();
1249
+ const origin = meta.weightOrigin;
1250
+ const integerWeights = origin !== null && origin.type === "int";
1251
+ for (let e = 0; e < snapshot.edgeCount; e++) {
1252
+ if (context.foldPairs && context.folding.folded(e)) {
1253
+ continue;
1254
+ }
1255
+ w.raw(" edge [");
1256
+ w.line(" ", "source", formatInteger(ids.idAt(el.src[e])));
1257
+ w.line(" ", "target", formatInteger(ids.idAt(el.dst[e])));
1258
+ if (context.edgeIds !== null && context.edgeIds.isSet(e)) {
1259
+ w.cell(" ", "id", context.edgeIds, e);
1260
+ }
1261
+ if (context.weights.isExplicit(e)) {
1262
+ const weight = context.weights.value(e);
1263
+ const text = integerWeights && Number.isInteger(weight) ? formatInteger(weight) : gmlRealText(weight, context.weights.dtype);
1264
+ w.line(" ", plan.weightKey, text);
1265
+ }
1266
+ for (const { column, key } of context.edgeColumns) {
1267
+ if (column.isSet(e)) {
1268
+ w.cell(" ", key, column, e);
1269
+ }
1270
+ }
1271
+ w.raw(" ]");
1272
+ yield w.take();
1273
+ }
1274
+ w.raw("]");
1275
+ yield w.take();
1276
+ }
1277
+ function writeGraphics(w, context, i) {
1278
+ const { position, graphics } = context;
1279
+ const hasPosition = position !== null && position.isSet(i);
1280
+ const graphicsValue = graphics !== null && graphics.isSet(i) ? graphics.values[i] : void 0;
1281
+ if (!hasPosition) {
1282
+ if (graphics !== null && graphicsValue !== void 0) {
1283
+ w.cell(" ", "graphics", graphics, i);
1284
+ }
1285
+ return;
1286
+ }
1287
+ const numeric = position;
1288
+ const { components } = numeric.meta;
1289
+ const lanes = Math.min(components, 3);
1290
+ const extra = [];
1291
+ const names = ["x", "y", "z"];
1292
+ for (let k = 0; k < lanes; k++) {
1293
+ extra.push([names[k], numberText(numeric.data[i * components + k], numeric.meta)]);
1294
+ }
1295
+ let record = {};
1296
+ if (graphicsValue !== void 0) {
1297
+ if (typeof graphicsValue !== "object" || graphicsValue === null || Array.isArray(graphicsValue)) {
1298
+ throw new GraphFormatError(
1299
+ "E_UNSUPPORTED",
1300
+ `node ${i}: the graphics value is not a record and cannot hold the position`,
1301
+ {
1302
+ reason: "graphics conflict",
1303
+ node: i
1304
+ }
1305
+ );
1306
+ }
1307
+ record = graphicsValue;
1308
+ }
1309
+ w.record(" ", "graphics", record, "graphics", extra);
1310
+ }
1311
+ const gmlExporter = Object.freeze({
1312
+ format: "gml",
1313
+ capabilities: GML_CAPABILITIES,
1314
+ check,
1315
+ /**
1316
+ * Write the snapshot as UTF-8 chunks; the context is resolved on the first pull, so the
1317
+ * errors check() announced surface from the iteration.
1318
+ * @param snapshot - the snapshot
1319
+ * @param options - the options
1320
+ * @returns the chunks
1321
+ */
1322
+ export(snapshot, options) {
1323
+ return encodeChunks(lazyParts(snapshot, options));
1324
+ },
1325
+ /**
1326
+ * Write the snapshot as one string.
1327
+ * @param snapshot - the snapshot
1328
+ * @param options - the options
1329
+ * @returns the document
1330
+ */
1331
+ exportToString(snapshot, options) {
1332
+ return joinText(lazyParts(snapshot, options));
1333
+ }
1334
+ });
1335
+ function* lazyParts(snapshot, options) {
1336
+ yield* gmlParts(contextOf(snapshot, options));
1337
+ }
1338
+ const NO_GRAPH_CODE = NO_GRAPH_CODE$1;
1339
+ const SECOND_GRAPH_CODE = "E_GML_SECOND_GRAPH";
1340
+ const MISSING_ID_CODE = MISSING_ID_CODE$1;
1341
+ const MISSING_LABEL_CODE = "E_GML_MISSING_LABEL";
1342
+ const MISSING_ENDPOINT_CODE = MISSING_ENDPOINT_CODE$1;
1343
+ const ID_TYPE_CODE = "E_GML_ID_TYPE";
1344
+ const DUPLICATE_NODE_CODE = DUPLICATE_NODE_CODE$1;
1345
+ const REPEATED_KEY_CODE = "E_GML_REPEATED_KEY";
1346
+ const ELEMENT_TYPE_CODE = "E_GML_ELEMENT_TYPE";
1347
+ const FLAG_TYPE_CODE = "E_GML_FLAG_TYPE";
1348
+ const FLAG_VALUE_CODE = "W_GML_FLAG_VALUE";
1349
+ const PRECISION_CODE = PRECISION_CODE$1;
1350
+ const ROLE_TAKEN_CODE = ROLE_TAKEN_CODE$1;
1351
+ const ID_DROPPED_CODE = "W_GML_ID_DROPPED";
1352
+ const FORMAT_DEFAULTS = { ids: "canonical", defaultDirected: false, weightFrom: "value" };
1353
+ const KIND_INT = 1;
1354
+ const KIND_REAL = 2;
1355
+ const KIND_STRING = 4;
1356
+ const KIND_RECORD = 8;
1357
+ const I32_MIN = -2147483648;
1358
+ const I32_MAX = 2147483647;
1359
+ const ABORT_CHECK_INTERVAL = 64;
1360
+ const USED_OPTIONS = /* @__PURE__ */ new Set([
1361
+ "ids",
1362
+ "nodeIdFrom",
1363
+ "addMissingNodes",
1364
+ "duplicateEdges",
1365
+ "selfLoops",
1366
+ "onMixedDirection",
1367
+ "defaultDirected",
1368
+ "weightFrom",
1369
+ "weightDtype",
1370
+ "restoreMangledIds",
1371
+ "errorLimit",
1372
+ "signal",
1373
+ "onProgress"
1374
+ ]);
1375
+ const GRAPH_SEQ = -1;
1376
+ const TOP_SEQ = -2;
1377
+ function roleOf(domain, key) {
1378
+ if (key === "label" && domain !== "graph") {
1379
+ return "label";
1380
+ }
1381
+ if (domain === "edge") {
1382
+ if (key === "id") {
1383
+ return "id";
1384
+ }
1385
+ if (key === "key") {
1386
+ return "key";
1387
+ }
1388
+ }
1389
+ return null;
1390
+ }
1391
+ class GmlImport {
1392
+ /**
1393
+ * Create one import.
1394
+ * @param sink - the sink
1395
+ * @param report - the report
1396
+ * @param options - the resolved common options
1397
+ * @param format - the format-specific options
1398
+ */
1399
+ constructor(sink, report, options, format) {
1400
+ this.tokens = null;
1401
+ this.resolver = null;
1402
+ this.nodeSchema = /* @__PURE__ */ new Map();
1403
+ this.edgeSchema = /* @__PURE__ */ new Map();
1404
+ this.graphSchema = /* @__PURE__ */ new Map();
1405
+ this.topSchema = /* @__PURE__ */ new Map();
1406
+ this.nodePlans = /* @__PURE__ */ new Map();
1407
+ this.edgePlans = /* @__PURE__ */ new Map();
1408
+ this.graphPlans = /* @__PURE__ */ new Map();
1409
+ this.topPlans = /* @__PURE__ */ new Map();
1410
+ this.graphNames = /* @__PURE__ */ new Set();
1411
+ this.graphOpen = -1;
1412
+ this.directedToken = -1;
1413
+ this.multigraphToken = -1;
1414
+ this.creatorToken = -1;
1415
+ this.versionToken = -1;
1416
+ this.nodeCount = 0;
1417
+ this.edgeCount = 0;
1418
+ this.weightKinds = 0;
1419
+ this.hasOriginalId = false;
1420
+ this.headerDirected = false;
1421
+ this.seq = 0;
1422
+ this.nodeOrdinal = 0;
1423
+ this.elementsSinceCheck = 0;
1424
+ this.idMap = null;
1425
+ this.declared = new Uint32Array(64);
1426
+ this.touched = [];
1427
+ this.graphTouched = [];
1428
+ this.seqNow = 0;
1429
+ this.sink = sink;
1430
+ this.report = report;
1431
+ this.options = options;
1432
+ this.positions = format?.positions ?? true;
1433
+ this.dictionaries = format?.dictionaries ?? true;
1434
+ this.coercer = new IdCoercer(options.ids);
1435
+ }
1436
+ /**
1437
+ * Run the import over a text.
1438
+ * @param text - the whole GML text
1439
+ */
1440
+ run(text) {
1441
+ const { report } = this;
1442
+ try {
1443
+ this.tokens = tokenizeGml(text);
1444
+ } catch (err) {
1445
+ if (err instanceof GmlSyntaxError) {
1446
+ report.fail(err.code, err.message, { line: err.line });
1447
+ }
1448
+ throw err;
1449
+ }
1450
+ this.scan();
1451
+ this.push();
1452
+ }
1453
+ // ============================================================ pass 1: the schema
1454
+ /**
1455
+ * Walk the top level: find the graph block, the metadata keys and the top-level attributes,
1456
+ * and derive every table's schema.
1457
+ */
1458
+ scan() {
1459
+ const t = this.requireTokens();
1460
+ for (let p = 0; p < t.count; p = t.nextPair(p)) {
1461
+ const key = t.textOf(p);
1462
+ const v = p + 1;
1463
+ const record = t.kind[v] === TOKEN_OPEN;
1464
+ if (key === "graph" && record) {
1465
+ if (this.graphOpen >= 0) {
1466
+ this.report.fail(SECOND_GRAPH_CODE, "the input contains more than one graph", {
1467
+ line: t.line[p]
1468
+ });
1469
+ }
1470
+ this.graphOpen = v;
1471
+ this.scanGraph(v);
1472
+ } else if (key === "Creator" && !record && this.creatorToken < 0) {
1473
+ this.creatorToken = v;
1474
+ } else if (key === "Version" && !record && this.versionToken < 0) {
1475
+ this.versionToken = v;
1476
+ } else {
1477
+ this.observe(this.topSchema, "graph", key, v, TOP_SEQ);
1478
+ }
1479
+ }
1480
+ if (this.graphOpen < 0) {
1481
+ this.report.fail(NO_GRAPH_CODE, "the input contains no graph [ ... ] block", { line: null });
1482
+ }
1483
+ }
1484
+ /**
1485
+ * Walk the graph block for the schema.
1486
+ * @param open - the index of the block's `[`
1487
+ */
1488
+ scanGraph(open) {
1489
+ const t = this.requireTokens();
1490
+ const close = t.match[open];
1491
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
1492
+ const key = t.textOf(p);
1493
+ const v = p + 1;
1494
+ switch (key) {
1495
+ case "node":
1496
+ if (t.kind[v] === TOKEN_OPEN) {
1497
+ this.nodeCount++;
1498
+ this.scanElement("node", v);
1499
+ }
1500
+ break;
1501
+ case "edge":
1502
+ if (t.kind[v] === TOKEN_OPEN) {
1503
+ this.edgeCount++;
1504
+ this.scanElement("edge", v);
1505
+ }
1506
+ break;
1507
+ case "directed":
1508
+ if (this.directedToken < 0) {
1509
+ this.directedToken = v;
1510
+ }
1511
+ break;
1512
+ case "multigraph":
1513
+ if (this.multigraphToken < 0) {
1514
+ this.multigraphToken = v;
1515
+ }
1516
+ break;
1517
+ default:
1518
+ this.observe(this.graphSchema, "graph", key, v, GRAPH_SEQ);
1519
+ }
1520
+ }
1521
+ }
1522
+ /**
1523
+ * Walk one node or edge block for the schema.
1524
+ * @param domain - node or edge
1525
+ * @param open - the block's `[`
1526
+ */
1527
+ scanElement(domain, open) {
1528
+ const t = this.requireTokens();
1529
+ const close = t.match[open];
1530
+ const seq = this.seq++;
1531
+ const schema = domain === "node" ? this.nodeSchema : this.edgeSchema;
1532
+ const { weightFrom, restoreMangledIds } = this.options;
1533
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
1534
+ const key = t.textOf(p);
1535
+ const v = p + 1;
1536
+ if (domain === "node") {
1537
+ if (key === "id") {
1538
+ continue;
1539
+ }
1540
+ if (key === ORIGINAL_ID_KEY && restoreMangledIds) {
1541
+ this.hasOriginalId = true;
1542
+ continue;
1543
+ }
1544
+ } else {
1545
+ if (key === "source" || key === "target") {
1546
+ continue;
1547
+ }
1548
+ if (key === weightFrom) {
1549
+ this.weightKinds |= kindBit(t, v);
1550
+ continue;
1551
+ }
1552
+ }
1553
+ this.observe(schema, domain, key, v, seq);
1554
+ }
1555
+ }
1556
+ /**
1557
+ * Record one value of one key in a table's schema.
1558
+ * @param schema - the table's schema
1559
+ * @param domain - the table
1560
+ * @param key - the key
1561
+ * @param v - the value token
1562
+ * @param seq - the element sequence number
1563
+ */
1564
+ observe(schema, domain, key, v, seq) {
1565
+ const t = this.requireTokens();
1566
+ let entry = schema.get(key);
1567
+ if (entry === void 0) {
1568
+ entry = {
1569
+ key,
1570
+ kinds: 0,
1571
+ list: false,
1572
+ lastSeq: seq,
1573
+ wide: false,
1574
+ unsafe: false,
1575
+ dict: this.dictionaries && roleOf(domain, key) === null ? new DictHeuristic() : null,
1576
+ position: false,
1577
+ rest: false
1578
+ };
1579
+ schema.set(key, entry);
1580
+ } else if (entry.lastSeq === seq) {
1581
+ entry.list = true;
1582
+ } else {
1583
+ entry.lastSeq = seq;
1584
+ }
1585
+ switch (t.kind[v]) {
1586
+ case TOKEN_STRING: {
1587
+ const raw = t.textOf(v);
1588
+ if (raw === LIST_START_MARKER || raw === EMPTY_LIST_TEXT || raw === EMPTY_TUPLE_TEXT) {
1589
+ entry.list = true;
1590
+ return;
1591
+ }
1592
+ entry.kinds |= KIND_STRING;
1593
+ if (entry.dict !== null && !entry.dict.decided) {
1594
+ entry.dict.observe(raw);
1595
+ }
1596
+ return;
1597
+ }
1598
+ case TOKEN_INT: {
1599
+ entry.kinds |= KIND_INT;
1600
+ const n = Number(t.textOf(v));
1601
+ if (n < I32_MIN || n > I32_MAX) {
1602
+ entry.wide = true;
1603
+ if (!Number.isSafeInteger(n)) {
1604
+ entry.unsafe = true;
1605
+ }
1606
+ }
1607
+ return;
1608
+ }
1609
+ case TOKEN_OPEN:
1610
+ entry.kinds |= KIND_RECORD;
1611
+ if (domain === "node" && key === "graphics" && this.positions) {
1612
+ this.inspectGraphics(entry, v);
1613
+ }
1614
+ return;
1615
+ default:
1616
+ entry.kinds |= KIND_REAL;
1617
+ }
1618
+ }
1619
+ /**
1620
+ * Note whether a node's graphics record carries a position (numeric x and y) and whether
1621
+ * anything besides the position remains.
1622
+ * @param entry - the graphics schema entry
1623
+ * @param open - the record's `[`
1624
+ */
1625
+ inspectGraphics(entry, open) {
1626
+ const t = this.requireTokens();
1627
+ const close = t.match[open];
1628
+ let x = false;
1629
+ let y = false;
1630
+ let other = false;
1631
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
1632
+ const key = t.textOf(p);
1633
+ const numeric = t.kind[p + 1] === TOKEN_INT || t.kind[p + 1] === TOKEN_REAL;
1634
+ if (key === "x" && numeric && !x) {
1635
+ x = true;
1636
+ } else if (key === "y" && numeric && !y) {
1637
+ y = true;
1638
+ } else if (!(key === "z" && numeric)) {
1639
+ other = true;
1640
+ }
1641
+ }
1642
+ const positioned = x && y;
1643
+ entry.position ||= positioned;
1644
+ entry.rest ||= positioned ? other : true;
1645
+ }
1646
+ // ============================================================ pass 2: the push
1647
+ /** Declare what the schema needs, set the header and push every element. */
1648
+ push() {
1649
+ const { sink, report, options } = this;
1650
+ const t = this.requireTokens();
1651
+ sink.reserve(this.nodeCount, this.edgeCount);
1652
+ this.buildPlans();
1653
+ this.setMetadata();
1654
+ this.headerDirected = this.readFlag(this.directedToken, "directed") ?? options.defaultDirected;
1655
+ const resolver = new DirectionResolver(sink, report, options.onMixedDirection);
1656
+ resolver.setHeader(this.headerDirected, {
1657
+ line: this.directedToken >= 0 ? t.line[this.directedToken] : t.line[this.graphOpen]
1658
+ });
1659
+ this.resolver = resolver;
1660
+ if (options.nodeIdFrom !== "id" || this.hasOriginalId) {
1661
+ this.idMap = /* @__PURE__ */ new Map();
1662
+ }
1663
+ if (options.nodeIdFrom !== "id") {
1664
+ report.loss(
1665
+ ID_DROPPED_CODE,
1666
+ `node ids taken from the ${options.nodeIdFrom}; the integer id keys are not kept as a column`,
1667
+ null,
1668
+ this.nodeCount
1669
+ );
1670
+ }
1671
+ this.pushTopLevel();
1672
+ this.pushNodes();
1673
+ this.pushEdges();
1674
+ }
1675
+ /** Turn every schema entry into a column plan. */
1676
+ buildPlans() {
1677
+ for (const [key, entry] of this.nodeSchema) {
1678
+ this.nodePlans.set(key, this.planOf("node", entry));
1679
+ }
1680
+ for (const [key, entry] of this.edgeSchema) {
1681
+ this.edgePlans.set(key, this.planOf("edge", entry));
1682
+ }
1683
+ for (const [key, entry] of this.graphSchema) {
1684
+ this.graphPlans.set(key, this.planOf("graph", entry));
1685
+ }
1686
+ for (const [key, entry] of this.topSchema) {
1687
+ this.topPlans.set(key, this.planOf("graph", entry, true));
1688
+ }
1689
+ }
1690
+ /**
1691
+ * The plan of one schema entry.
1692
+ * @param domain - the table
1693
+ * @param entry - the schema entry
1694
+ * @param top - whether the key is a top-level one (graph table)
1695
+ * @returns the plan
1696
+ */
1697
+ planOf(domain, entry, top = false) {
1698
+ const kind = kindOf(entry);
1699
+ let scalar = dtypeOfKind(kind, entry);
1700
+ if (scalar === "string" && !entry.list && entry.dict !== null && entry.kinds === KIND_STRING && entry.dict.decide() === "dict") {
1701
+ scalar = "dict";
1702
+ }
1703
+ if (entry.unsafe) {
1704
+ this.report.warnOnce(
1705
+ "precision",
1706
+ PRECISION_CODE,
1707
+ `${domain} key "${entry.key}" holds integers beyond 2^53; they are stored as the nearest f64`,
1708
+ { element: entry.key },
1709
+ `${PRECISION_CODE}:${domain}:${entry.key}`
1710
+ );
1711
+ }
1712
+ const name = domain === "graph" ? this.uniqueGraphName(entry.key) : entry.key;
1713
+ let itemDtype = null;
1714
+ if (entry.list) {
1715
+ itemDtype = scalar === "dict" ? "string" : scalar;
1716
+ }
1717
+ const plan = {
1718
+ key: entry.key,
1719
+ name,
1720
+ dtype: entry.list ? "list" : scalar,
1721
+ itemDtype,
1722
+ kind,
1723
+ list: entry.list,
1724
+ role: roleOf(domain, entry.key),
1725
+ top,
1726
+ position: null,
1727
+ rest: null,
1728
+ handle: INVALID_INDEX,
1729
+ declared: false,
1730
+ items: null,
1731
+ seq: Number.NEGATIVE_INFINITY
1732
+ };
1733
+ if (domain === "node" && entry.key === "graphics" && entry.position && entry.kinds === KIND_RECORD && !entry.list) {
1734
+ const taken = (candidate) => this.nodeSchema.has(candidate) || this.sink.nodeColumn(candidate) !== INVALID_INDEX;
1735
+ const position = {
1736
+ ...plan,
1737
+ name: uniqueColumnName("position", "graphics", taken),
1738
+ dtype: "f64",
1739
+ itemDtype: null,
1740
+ kind: "real",
1741
+ role: "position"
1742
+ };
1743
+ const rest = entry.rest ? { ...plan } : null;
1744
+ return { ...plan, position, rest };
1745
+ }
1746
+ return plan;
1747
+ }
1748
+ /**
1749
+ * A graph column name not yet used by this import (top-level and in-graph keys share the table).
1750
+ * @param key - the key
1751
+ * @returns the name
1752
+ */
1753
+ uniqueGraphName(key) {
1754
+ const name = uniqueColumnName(key, key, (candidate) => this.graphNames.has(candidate));
1755
+ this.graphNames.add(name);
1756
+ return name;
1757
+ }
1758
+ /** Set the graph metadata: source format, creator, version, multigraph flag, weight origin. */
1759
+ setMetadata() {
1760
+ const t = this.requireTokens();
1761
+ const { sink, options } = this;
1762
+ const textOf = (v) => t.kind[v] === TOKEN_STRING ? t.stringOf(v) : t.textOf(v);
1763
+ const multigraph = this.readFlag(this.multigraphToken, "multigraph");
1764
+ sink.setMeta({
1765
+ sourceFormat: "gml",
1766
+ creator: this.creatorToken >= 0 ? textOf(this.creatorToken) : void 0,
1767
+ sourceVersion: this.versionToken >= 0 ? textOf(this.versionToken) : void 0,
1768
+ declaredMultigraph: multigraph ?? void 0,
1769
+ weightOrigin: this.weightKinds !== 0 && options.weightFrom !== null ? {
1770
+ format: "gml",
1771
+ id: options.weightFrom,
1772
+ title: null,
1773
+ type: this.weightKinds === KIND_INT ? "int" : "real",
1774
+ namespace: null
1775
+ } : void 0
1776
+ });
1777
+ }
1778
+ /**
1779
+ * Read a `directed` / `multigraph` flag token.
1780
+ * @param v - the value token, or -1 when absent
1781
+ * @param name - the flag name, for issues
1782
+ * @returns the flag, or null when absent or not an integer
1783
+ */
1784
+ readFlag(v, name) {
1785
+ if (v < 0) {
1786
+ return null;
1787
+ }
1788
+ const t = this.requireTokens();
1789
+ if (t.kind[v] !== TOKEN_INT) {
1790
+ this.report.error(
1791
+ "validation-error",
1792
+ FLAG_TYPE_CODE,
1793
+ `graph flag "${name}" must be the integer 0 or 1, found ${describeValue(t, v)}`,
1794
+ { line: t.line[v], element: name }
1795
+ );
1796
+ return null;
1797
+ }
1798
+ const n = Number(t.textOf(v));
1799
+ if (n !== 0 && n !== 1) {
1800
+ this.report.warning(
1801
+ "validation-error",
1802
+ FLAG_VALUE_CODE,
1803
+ `graph flag "${name}" is ${n}; read as ${n !== 0 ? "1" : "0"}`,
1804
+ { line: t.line[v], element: name }
1805
+ );
1806
+ }
1807
+ return n !== 0;
1808
+ }
1809
+ /** Push the top-level attributes (every top-level key but graph, Creator and Version). */
1810
+ pushTopLevel() {
1811
+ const t = this.requireTokens();
1812
+ for (let p = 0; p < t.count; p = t.nextPair(p)) {
1813
+ const v = p + 1;
1814
+ if (v === this.graphOpen || v === this.creatorToken || v === this.versionToken) {
1815
+ continue;
1816
+ }
1817
+ const plan = this.topPlans.get(t.textOf(p));
1818
+ if (plan !== void 0) {
1819
+ this.writeGraph(plan, v, TOP_SEQ);
1820
+ }
1821
+ }
1822
+ this.flushGraphLists();
1823
+ }
1824
+ /** Push every node block and the graph block's own attributes, in file order. */
1825
+ pushNodes() {
1826
+ const t = this.requireTokens();
1827
+ const close = t.match[this.graphOpen];
1828
+ for (let p = this.graphOpen + 1; p < close; p = t.nextPair(p)) {
1829
+ const key = t.textOf(p);
1830
+ const v = p + 1;
1831
+ switch (key) {
1832
+ case "node":
1833
+ if (t.kind[v] === TOKEN_OPEN) {
1834
+ this.importNode(v);
1835
+ } else {
1836
+ this.badElement("node", p);
1837
+ this.report.counts.skippedNodes++;
1838
+ }
1839
+ break;
1840
+ case "edge":
1841
+ break;
1842
+ case "directed":
1843
+ case "multigraph":
1844
+ if (v !== this.directedToken && v !== this.multigraphToken) {
1845
+ this.report.warning(
1846
+ "validation-error",
1847
+ FLAG_VALUE_CODE,
1848
+ `graph flag "${key}" repeated; the first value stands`,
1849
+ { line: t.line[p], element: key }
1850
+ );
1851
+ }
1852
+ break;
1853
+ default: {
1854
+ const plan = this.graphPlans.get(key);
1855
+ if (plan !== void 0) {
1856
+ this.writeGraph(plan, v, GRAPH_SEQ);
1857
+ }
1858
+ }
1859
+ }
1860
+ }
1861
+ this.flushGraphLists();
1862
+ }
1863
+ /** Push every edge block, in file order. */
1864
+ pushEdges() {
1865
+ const t = this.requireTokens();
1866
+ const close = t.match[this.graphOpen];
1867
+ let ordinal = 0;
1868
+ for (let p = this.graphOpen + 1; p < close; p = t.nextPair(p)) {
1869
+ if (t.textOf(p) !== "edge") {
1870
+ continue;
1871
+ }
1872
+ const v = p + 1;
1873
+ if (t.kind[v] === TOKEN_OPEN) {
1874
+ this.importEdge(v, ordinal);
1875
+ } else {
1876
+ this.badElement("edge", p);
1877
+ this.report.counts.skippedEdges++;
1878
+ }
1879
+ ordinal++;
1880
+ }
1881
+ }
1882
+ /**
1883
+ * Record a `node` / `edge` key whose value is not a block.
1884
+ * @param what - node or edge
1885
+ * @param p - the key token
1886
+ */
1887
+ badElement(what, p) {
1888
+ const t = this.requireTokens();
1889
+ this.report.error(
1890
+ "validation-error",
1891
+ ELEMENT_TYPE_CODE,
1892
+ `${what} must be a [ ... ] block, found ${describeValue(t, p + 1)}`,
1893
+ { line: t.line[p], element: what }
1894
+ );
1895
+ }
1896
+ /**
1897
+ * Push one node block.
1898
+ * @param open - the block's `[`
1899
+ */
1900
+ importNode(open) {
1901
+ const t = this.requireTokens();
1902
+ const { sink, report, options } = this;
1903
+ const close = t.match[open];
1904
+ const line = t.line[open];
1905
+ const ordinal = this.nodeOrdinal++;
1906
+ this.checkAbort();
1907
+ let index;
1908
+ let sinkId;
1909
+ try {
1910
+ let idTok = -1;
1911
+ let labelTok = -1;
1912
+ let originalTok = -1;
1913
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
1914
+ const key = t.textOf(p);
1915
+ if (key === "id") {
1916
+ idTok = this.structuralToken(idTok, p, "id");
1917
+ } else if (key === "label") {
1918
+ labelTok = labelTok < 0 ? p + 1 : labelTok;
1919
+ } else if (key === ORIGINAL_ID_KEY && options.restoreMangledIds) {
1920
+ originalTok = originalTok < 0 ? p + 1 : originalTok;
1921
+ }
1922
+ }
1923
+ if (idTok < 0) {
1924
+ throw new GraphFormatError("E_INVALID_ID", "node has no id", { reason: "missing id" });
1925
+ }
1926
+ const fileId = this.integerOf(idTok, "id");
1927
+ if (originalTok >= 0) {
1928
+ sinkId = this.restoredId(originalTok);
1929
+ } else {
1930
+ switch (options.nodeIdFrom) {
1931
+ case "label":
1932
+ if (labelTok < 0) {
1933
+ throw new GraphFormatError("E_INVALID_ID", "node has no label", {
1934
+ reason: "missing label"
1935
+ });
1936
+ }
1937
+ sinkId = this.coerceValueToken(labelTok);
1938
+ break;
1939
+ case "index":
1940
+ sinkId = ordinal;
1941
+ break;
1942
+ default:
1943
+ sinkId = this.coerceInteger(idTok, fileId);
1944
+ }
1945
+ }
1946
+ if (this.idMap !== null) {
1947
+ this.idMap.set(fileId, sinkId);
1948
+ }
1949
+ index = sink.addNode(sinkId);
1950
+ } catch (err) {
1951
+ this.recordNodeError(err, line);
1952
+ report.counts.skippedNodes++;
1953
+ return;
1954
+ }
1955
+ if (this.isDeclared(index)) {
1956
+ report.warning(
1957
+ "merged",
1958
+ DUPLICATE_NODE_CODE,
1959
+ `node ${JSON.stringify(sinkId)} is declared twice; later keys overwrite`,
1960
+ { line, element: String(sinkId) }
1961
+ );
1962
+ } else {
1963
+ this.markDeclared(index);
1964
+ report.counts.nodes++;
1965
+ }
1966
+ this.beginElement(this.seq++);
1967
+ try {
1968
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
1969
+ const key = t.textOf(p);
1970
+ if (key === "id" || key === ORIGINAL_ID_KEY && options.restoreMangledIds) {
1971
+ continue;
1972
+ }
1973
+ const plan = this.nodePlans.get(key);
1974
+ if (plan !== void 0) {
1975
+ this.writeNode(plan, index, p + 1);
1976
+ }
1977
+ }
1978
+ this.flushLists("node", index);
1979
+ } catch (err) {
1980
+ this.touched.length = 0;
1981
+ report.recordError(err, { line, element: String(sinkId) });
1982
+ }
1983
+ }
1984
+ /**
1985
+ * Push one edge block.
1986
+ * @param open - the block's `[`
1987
+ * @param ordinal - the edge's ordinal among edge blocks, for messages
1988
+ */
1989
+ importEdge(open, ordinal) {
1990
+ const t = this.requireTokens();
1991
+ const { sink, report, options } = this;
1992
+ const close = t.match[open];
1993
+ const line = t.line[open];
1994
+ const element = `edge #${ordinal}`;
1995
+ this.checkAbort();
1996
+ let edge;
1997
+ try {
1998
+ let sourceTok = -1;
1999
+ let targetTok = -1;
2000
+ let weightTok = -1;
2001
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
2002
+ const key = t.textOf(p);
2003
+ if (key === "source") {
2004
+ sourceTok = this.structuralToken(sourceTok, p, "source");
2005
+ } else if (key === "target") {
2006
+ targetTok = this.structuralToken(targetTok, p, "target");
2007
+ } else if (key === options.weightFrom) {
2008
+ weightTok = p + 1;
2009
+ }
2010
+ }
2011
+ if (sourceTok < 0 || targetTok < 0) {
2012
+ throw new GraphFormatError("E_UNKNOWN_NODE", `edge has no ${sourceTok < 0 ? "source" : "target"}`, {
2013
+ reason: "missing endpoint"
2014
+ });
2015
+ }
2016
+ const source = this.endpoint(sourceTok, "source");
2017
+ const target = this.endpoint(targetTok, "target");
2018
+ const weight = weightTok >= 0 ? this.weightOf(weightTok) : void 0;
2019
+ const before = sink.edgeCount;
2020
+ const sourceNew = sink.indexOf(source) === INVALID_INDEX;
2021
+ const targetNew = source !== target && sink.indexOf(target) === INVALID_INDEX;
2022
+ edge = this.requireResolver().addEdge(
2023
+ source,
2024
+ target,
2025
+ this.headerDirected ? "directed" : "undirected",
2026
+ weight,
2027
+ { line, element }
2028
+ );
2029
+ report.counts.edges += sink.edgeCount - before;
2030
+ report.counts.nodes += (sourceNew ? 1 : 0) + (targetNew ? 1 : 0);
2031
+ } catch (err) {
2032
+ this.recordEdgeError(err, line, element);
2033
+ report.counts.skippedEdges++;
2034
+ return;
2035
+ }
2036
+ this.beginElement(this.seq++);
2037
+ try {
2038
+ for (let p = open + 1; p < close; p = t.nextPair(p)) {
2039
+ const key = t.textOf(p);
2040
+ if (key === "source" || key === "target" || key === options.weightFrom) {
2041
+ continue;
2042
+ }
2043
+ const plan = this.edgePlans.get(key);
2044
+ if (plan !== void 0) {
2045
+ this.writeEdge(plan, edge, p + 1);
2046
+ }
2047
+ }
2048
+ this.flushLists("edge", edge);
2049
+ } catch (err) {
2050
+ this.touched.length = 0;
2051
+ report.recordError(err, { line, element });
2052
+ }
2053
+ }
2054
+ // ============================================================ ids and weights
2055
+ /**
2056
+ * Note a structural key token, refusing a repeat.
2057
+ * @param previous - the token seen before, or -1
2058
+ * @param p - the key token
2059
+ * @param key - the key name
2060
+ * @returns the value token
2061
+ */
2062
+ structuralToken(previous, p, key) {
2063
+ if (previous >= 0) {
2064
+ throw new GraphFormatError("E_INVALID_ID", `key "${key}" appears twice in one block`, {
2065
+ reason: "duplicate key",
2066
+ key
2067
+ });
2068
+ }
2069
+ return p + 1;
2070
+ }
2071
+ /**
2072
+ * The integer value of an id / source / target token (the GML grammar's integer ids; a string
2073
+ * id is refused, as the malformed corpus pins, although NetworkX would accept it).
2074
+ * @param v - the value token
2075
+ * @param key - the key name, for the error
2076
+ * @returns the number
2077
+ */
2078
+ integerOf(v, key) {
2079
+ const t = this.requireTokens();
2080
+ if (t.kind[v] !== TOKEN_INT) {
2081
+ throw new GraphFormatError("E_INVALID_ID", `${key} must be an integer, found ${describeValue(t, v)}`, {
2082
+ reason: "not an integer",
2083
+ key,
2084
+ value: t.kind[v] === TOKEN_OPEN ? "[...]" : t.textOf(v)
2085
+ });
2086
+ }
2087
+ return Number(t.textOf(v));
2088
+ }
2089
+ /**
2090
+ * Coerce an integer id token under the `ids` rule: a safe integer passes as a typed number
2091
+ * (so `01` is 1 and `-0` is 0), a larger one goes through the text rule (a string under
2092
+ * "canonical"), and "string" keeps the text as written.
2093
+ * @param v - the value token
2094
+ * @param n - its numeric value
2095
+ * @returns the id
2096
+ */
2097
+ coerceInteger(v, n) {
2098
+ if (Number.isSafeInteger(n) && this.coercer.mode !== "string") {
2099
+ return this.coercer.value(n);
2100
+ }
2101
+ return this.coercer.text(this.requireTokens().textOf(v));
2102
+ }
2103
+ /**
2104
+ * Coerce a label token under the `ids` rule: a string by its text, a number as a typed value.
2105
+ * @param v - the value token
2106
+ * @returns the id
2107
+ */
2108
+ coerceValueToken(v) {
2109
+ const t = this.requireTokens();
2110
+ switch (t.kind[v]) {
2111
+ case TOKEN_STRING:
2112
+ return this.coercer.text(t.stringOf(v));
2113
+ case TOKEN_INT:
2114
+ return this.coerceInteger(v, Number(t.textOf(v)));
2115
+ case TOKEN_OPEN:
2116
+ throw new GraphFormatError("E_INVALID_ID", "label must be a string or a number, found a record", {
2117
+ reason: "record label"
2118
+ });
2119
+ default:
2120
+ return this.coercer.value(numberOfText(t.textOf(v)));
2121
+ }
2122
+ }
2123
+ /**
2124
+ * The id a `graphty_originalId` key restores: typed, never coerced (the exporter wrote it exactly).
2125
+ * @param v - the value token
2126
+ * @returns the original id
2127
+ */
2128
+ restoredId(v) {
2129
+ const t = this.requireTokens();
2130
+ switch (t.kind[v]) {
2131
+ case TOKEN_STRING:
2132
+ return t.stringOf(v);
2133
+ case TOKEN_OPEN:
2134
+ throw new GraphFormatError("E_INVALID_ID", `${ORIGINAL_ID_KEY} must be a string or a number`, {
2135
+ reason: "record original id"
2136
+ });
2137
+ default:
2138
+ return numberOfText(t.textOf(v));
2139
+ }
2140
+ }
2141
+ /**
2142
+ * Resolve a source / target token to a sink id.
2143
+ * @param v - the value token
2144
+ * @param key - source or target
2145
+ * @returns the id
2146
+ */
2147
+ endpoint(v, key) {
2148
+ const n = this.integerOf(v, key);
2149
+ if (this.idMap !== null) {
2150
+ const mapped = this.idMap.get(n);
2151
+ if (mapped !== void 0) {
2152
+ return mapped;
2153
+ }
2154
+ }
2155
+ return this.coerceInteger(v, n);
2156
+ }
2157
+ /**
2158
+ * The weight of an edge from its weight token.
2159
+ * @param v - the value token
2160
+ * @returns the weight; E_INVALID_WEIGHT for a record or non-numeric text
2161
+ */
2162
+ weightOf(v) {
2163
+ const t = this.requireTokens();
2164
+ switch (t.kind[v]) {
2165
+ case TOKEN_STRING:
2166
+ return parseWeightText(t.stringOf(v));
2167
+ case TOKEN_OPEN:
2168
+ throw new GraphFormatError("E_INVALID_WEIGHT", "edge weight is a record", { value: "[...]" });
2169
+ default:
2170
+ return weightFromValue(numberOfText(t.textOf(v)));
2171
+ }
2172
+ }
2173
+ /**
2174
+ * Record a node-level error under the GML code it implies.
2175
+ * @param err - the thrown value
2176
+ * @param line - the block's line
2177
+ */
2178
+ recordNodeError(err, line) {
2179
+ const { report } = this;
2180
+ if (err instanceof GraphFormatError && err.code === "E_INVALID_ID") {
2181
+ const { reason } = err.details;
2182
+ switch (reason) {
2183
+ case "missing id":
2184
+ report.error("missing-value", MISSING_ID_CODE, err.message, { line, element: "node" });
2185
+ return;
2186
+ case "missing label":
2187
+ report.error("missing-value", MISSING_LABEL_CODE, err.message, { line, element: "node" });
2188
+ return;
2189
+ case "duplicate key":
2190
+ report.error("validation-error", REPEATED_KEY_CODE, err.message, { line, element: "node" });
2191
+ return;
2192
+ case "not an integer":
2193
+ report.error("validation-error", ID_TYPE_CODE, err.message, { line, element: "node" });
2194
+ return;
2195
+ }
2196
+ }
2197
+ report.recordError(err, { line, element: "node" });
2198
+ }
2199
+ /**
2200
+ * Record an edge-level error under the GML code it implies.
2201
+ * @param err - the thrown value
2202
+ * @param line - the block's line
2203
+ * @param element - the edge description
2204
+ */
2205
+ recordEdgeError(err, line, element) {
2206
+ const { report } = this;
2207
+ if (err instanceof GraphFormatError) {
2208
+ const { reason } = err.details;
2209
+ if (err.code === "E_UNKNOWN_NODE" && reason === "missing endpoint") {
2210
+ report.error("missing-value", MISSING_ENDPOINT_CODE, err.message, { line, element });
2211
+ return;
2212
+ }
2213
+ if (err.code === "E_INVALID_ID" && reason === "duplicate key") {
2214
+ report.error("validation-error", REPEATED_KEY_CODE, err.message, { line, element });
2215
+ return;
2216
+ }
2217
+ if (err.code === "E_INVALID_ID" && reason === "not an integer") {
2218
+ report.error("validation-error", ID_TYPE_CODE, err.message, { line, element });
2219
+ return;
2220
+ }
2221
+ }
2222
+ report.recordError(err, { line, element });
2223
+ }
2224
+ // ============================================================ values
2225
+ /**
2226
+ * Write one node value.
2227
+ * @param plan - the key's plan
2228
+ * @param index - the node index
2229
+ * @param v - the value token
2230
+ */
2231
+ writeNode(plan, index, v) {
2232
+ if (plan.position !== null) {
2233
+ this.writeGraphics(plan, index, v);
2234
+ return;
2235
+ }
2236
+ if (plan.list) {
2237
+ this.collectItem(plan, v, this.seqNow, this.touched);
2238
+ return;
2239
+ }
2240
+ this.sink.setNodeValue(this.handleOf(plan, "node"), index, this.scalarOf(plan, v));
2241
+ }
2242
+ /**
2243
+ * Write one edge value.
2244
+ * @param plan - the key's plan
2245
+ * @param edge - the edge index
2246
+ * @param v - the value token
2247
+ */
2248
+ writeEdge(plan, edge, v) {
2249
+ if (plan.list) {
2250
+ this.collectItem(plan, v, this.seqNow, this.touched);
2251
+ return;
2252
+ }
2253
+ this.sink.setEdgeValue(this.handleOf(plan, "edge"), edge, this.scalarOf(plan, v));
2254
+ }
2255
+ /**
2256
+ * Write one graph value.
2257
+ * @param plan - the key's plan
2258
+ * @param v - the value token
2259
+ * @param seq - TOP_SEQ or GRAPH_SEQ
2260
+ */
2261
+ writeGraph(plan, v, seq) {
2262
+ if (plan.list) {
2263
+ this.collectItem(plan, v, seq, this.graphTouched);
2264
+ return;
2265
+ }
2266
+ this.sink.setGraphValue(plan.name, this.scalarOf(plan, v), graphDecl(plan));
2267
+ }
2268
+ /**
2269
+ * Write a node's graphics record: the position column from x / y / z, the remaining keys to
2270
+ * the json column.
2271
+ * @param plan - the graphics plan
2272
+ * @param index - the node index
2273
+ * @param v - the value token (a record)
2274
+ */
2275
+ writeGraphics(plan, index, v) {
2276
+ const t = this.requireTokens();
2277
+ const { sink } = this;
2278
+ const record = parseRecord(t, v);
2279
+ const { x, y, z } = record;
2280
+ const positionPlan = plan.position;
2281
+ if (positionPlan !== null && typeof x === "number" && typeof y === "number") {
2282
+ sink.setNodeValue(this.handleOf(positionPlan, "node"), index, [x, y, typeof z === "number" ? z : 0]);
2283
+ delete record.x;
2284
+ delete record.y;
2285
+ if (typeof z === "number") {
2286
+ delete record.z;
2287
+ }
2288
+ if (plan.rest !== null && Object.keys(record).length > 0) {
2289
+ sink.setNodeValue(this.handleOf(plan.rest, "node"), index, record);
2290
+ }
2291
+ return;
2292
+ }
2293
+ if (plan.rest !== null) {
2294
+ sink.setNodeValue(this.handleOf(plan.rest, "node"), index, record);
2295
+ }
2296
+ }
2297
+ /**
2298
+ * Collect one list item.
2299
+ * @param plan - the list plan
2300
+ * @param v - the value token
2301
+ * @param seq - the sequence number of the element the item belongs to
2302
+ * @param touched - the plans touched by that element
2303
+ */
2304
+ collectItem(plan, v, seq, touched) {
2305
+ const t = this.requireTokens();
2306
+ if (plan.seq !== seq) {
2307
+ plan.seq = seq;
2308
+ plan.items = [];
2309
+ touched.push(plan);
2310
+ }
2311
+ if (t.kind[v] === TOKEN_STRING) {
2312
+ const raw = t.textOf(v);
2313
+ if (raw === LIST_START_MARKER || raw === EMPTY_LIST_TEXT || raw === EMPTY_TUPLE_TEXT) {
2314
+ return;
2315
+ }
2316
+ }
2317
+ plan.items.push(this.scalarOf(plan, v));
2318
+ }
2319
+ /**
2320
+ * Start a node or edge: later list items belong to it.
2321
+ * @param seq - the element's sequence number
2322
+ */
2323
+ beginElement(seq) {
2324
+ this.seqNow = seq;
2325
+ this.touched.length = 0;
2326
+ }
2327
+ /**
2328
+ * Write the lists collected for the current node or edge.
2329
+ * @param domain - node or edge
2330
+ * @param row - the row
2331
+ */
2332
+ flushLists(domain, row) {
2333
+ const { sink, touched } = this;
2334
+ for (const plan of touched) {
2335
+ const handle = this.handleOf(plan, domain);
2336
+ if (domain === "node") {
2337
+ sink.setNodeValue(handle, row, plan.items);
2338
+ } else {
2339
+ sink.setEdgeValue(handle, row, plan.items);
2340
+ }
2341
+ plan.items = null;
2342
+ }
2343
+ touched.length = 0;
2344
+ }
2345
+ /** Write the lists collected for the graph table. */
2346
+ flushGraphLists() {
2347
+ const { sink, graphTouched } = this;
2348
+ for (const plan of graphTouched) {
2349
+ sink.setGraphValue(plan.name, plan.items, graphDecl(plan));
2350
+ plan.items = null;
2351
+ }
2352
+ graphTouched.length = 0;
2353
+ }
2354
+ /**
2355
+ * The JS value of a scalar value token (or list item) under a plan's storage class.
2356
+ * @param plan - the plan
2357
+ * @param v - the value token
2358
+ * @returns the value to push
2359
+ */
2360
+ scalarOf(plan, v) {
2361
+ const t = this.requireTokens();
2362
+ const kind = t.kind[v];
2363
+ switch (plan.kind) {
2364
+ case "int":
2365
+ return Number(t.textOf(v));
2366
+ case "real":
2367
+ return numberOfText(t.textOf(v));
2368
+ case "string":
2369
+ return kind === TOKEN_STRING ? t.stringOf(v) : t.textOf(v);
2370
+ default:
2371
+ return kind === TOKEN_STRING ? t.stringOf(v) : scalarOrRecord(t, v);
2372
+ }
2373
+ }
2374
+ /**
2375
+ * The handle of a plan's column, declaring it on first use. A declaration the sink refuses
2376
+ * because a column of the name exists with another declaration falls back to that column
2377
+ * (reported once); a taken role is dropped (reported once).
2378
+ * @param plan - the plan
2379
+ * @param domain - node or edge
2380
+ * @returns the handle
2381
+ */
2382
+ handleOf(plan, domain) {
2383
+ if (plan.declared) {
2384
+ return plan.handle;
2385
+ }
2386
+ plan.declared = true;
2387
+ const { sink, report } = this;
2388
+ const decl = {
2389
+ name: plan.name,
2390
+ dtype: plan.dtype,
2391
+ nullable: true,
2392
+ origin: { format: "gml", id: plan.key, title: null, type: originTypeOf(plan), namespace: null }
2393
+ };
2394
+ if (plan.list) {
2395
+ decl.itemDtype = plan.itemDtype ?? "string";
2396
+ }
2397
+ if (plan.role === "position") {
2398
+ decl.components = 3;
2399
+ }
2400
+ if (plan.role !== null) {
2401
+ decl.role = plan.role;
2402
+ }
2403
+ const resolved = declareResolved(sink, domain, decl, report, { element: plan.name });
2404
+ plan.handle = resolved.handle;
2405
+ if (resolved.renamed) {
2406
+ plan.name = resolved.decl.name;
2407
+ }
2408
+ return plan.handle;
2409
+ }
2410
+ // ============================================================ bookkeeping
2411
+ /**
2412
+ * Whether a node index was declared by a node block of this import.
2413
+ * @param index - the node index
2414
+ * @returns true when declared
2415
+ */
2416
+ isDeclared(index) {
2417
+ const word = index >>> 5;
2418
+ return word < this.declared.length && (this.declared[word] >>> (index & 31) & 1) === 1;
2419
+ }
2420
+ /**
2421
+ * Mark a node index as declared.
2422
+ * @param index - the node index
2423
+ */
2424
+ markDeclared(index) {
2425
+ const word = index >>> 5;
2426
+ if (word >= this.declared.length) {
2427
+ const grown = new Uint32Array(Math.max(word + 1, this.declared.length * 2));
2428
+ grown.set(this.declared);
2429
+ this.declared = grown;
2430
+ }
2431
+ this.declared[word] |= 1 << (index & 31);
2432
+ }
2433
+ /** Check the abort signal every ABORT_CHECK_INTERVAL elements. */
2434
+ checkAbort() {
2435
+ if (++this.elementsSinceCheck >= ABORT_CHECK_INTERVAL) {
2436
+ this.elementsSinceCheck = 0;
2437
+ throwIfAborted(this.options.signal);
2438
+ }
2439
+ }
2440
+ /**
2441
+ * The tokens, which exist after run() lexed the text.
2442
+ * @returns the tokens
2443
+ */
2444
+ requireTokens() {
2445
+ if (this.tokens === null) {
2446
+ throw new GraphFormatError("E_UNSUPPORTED", "the GML text was not lexed", { reason: "no tokens" });
2447
+ }
2448
+ return this.tokens;
2449
+ }
2450
+ /**
2451
+ * The direction resolver, which exists once the header was set.
2452
+ * @returns the resolver
2453
+ */
2454
+ requireResolver() {
2455
+ if (this.resolver === null) {
2456
+ throw new GraphFormatError("E_DIRECTED", "the header was not set", { reason: "no resolver" });
2457
+ }
2458
+ return this.resolver;
2459
+ }
2460
+ }
2461
+ function kindBit(t, v) {
2462
+ switch (t.kind[v]) {
2463
+ case TOKEN_INT:
2464
+ return KIND_INT;
2465
+ case TOKEN_STRING:
2466
+ return KIND_STRING;
2467
+ case TOKEN_OPEN:
2468
+ return KIND_RECORD;
2469
+ default:
2470
+ return KIND_REAL;
2471
+ }
2472
+ }
2473
+ function kindOf(entry) {
2474
+ if ((entry.kinds & KIND_RECORD) !== 0) {
2475
+ return "record";
2476
+ }
2477
+ if ((entry.kinds & KIND_STRING) !== 0) {
2478
+ return "string";
2479
+ }
2480
+ if ((entry.kinds & KIND_REAL) !== 0) {
2481
+ return "real";
2482
+ }
2483
+ return "int";
2484
+ }
2485
+ function dtypeOfKind(kind, entry) {
2486
+ switch (kind) {
2487
+ case "int":
2488
+ return entry.wide ? "f64" : "i32";
2489
+ case "real":
2490
+ return "f64";
2491
+ case "string":
2492
+ return "string";
2493
+ default:
2494
+ return "json";
2495
+ }
2496
+ }
2497
+ function originTypeOf(plan) {
2498
+ return plan.kind;
2499
+ }
2500
+ function graphDecl(plan) {
2501
+ const decl = {
2502
+ name: plan.name,
2503
+ dtype: plan.dtype,
2504
+ nullable: true,
2505
+ origin: { format: "gml", id: plan.key, title: null, type: originTypeOf(plan), namespace: null }
2506
+ };
2507
+ if (plan.list) {
2508
+ decl.itemDtype = plan.itemDtype ?? "string";
2509
+ }
2510
+ if (plan.top) {
2511
+ decl.extra = { gmlTopLevel: true };
2512
+ }
2513
+ return decl;
2514
+ }
2515
+ function describeValue(t, v) {
2516
+ switch (t.kind[v]) {
2517
+ case TOKEN_OPEN:
2518
+ return "a record";
2519
+ case TOKEN_STRING:
2520
+ return `the string "${t.textOf(v)}"`;
2521
+ case TOKEN_REAL:
2522
+ return `the real ${t.textOf(v)}`;
2523
+ case TOKEN_WORD:
2524
+ return `the word ${t.textOf(v)}`;
2525
+ default:
2526
+ return t.textOf(v);
2527
+ }
2528
+ }
2529
+ const SNIFF_GRAPH_START = /^(?:\s|#[^\n]*\n)*graph\s*\[/;
2530
+ const SNIFF_GRAPH_ANYWHERE = /(?:^|\s)graph\s*\[/;
2531
+ const SNIFF_KEY_START = /^(?:\s|#[^\n]*\n)*[A-Za-z_][0-9A-Za-z_]*\s+(?:"|[+-]?[0-9.]|\[)/;
2532
+ function sniffGml(head) {
2533
+ let text = new TextDecoder("utf-8", { fatal: false }).decode(head);
2534
+ if (text.charCodeAt(0) === 65279) {
2535
+ text = text.slice(1);
2536
+ }
2537
+ if (SNIFF_GRAPH_START.test(text)) {
2538
+ return 0.95;
2539
+ }
2540
+ if (SNIFF_KEY_START.test(text)) {
2541
+ return SNIFF_GRAPH_ANYWHERE.test(text) ? 0.85 : 0.4;
2542
+ }
2543
+ return 0;
2544
+ }
2545
+ const gmlImporter = Object.freeze({
2546
+ format: "gml",
2547
+ extensions: Object.freeze([".gml"]),
2548
+ mimeTypes: Object.freeze(["text/x-gml", "text/plain"]),
2549
+ sniff: sniffGml,
2550
+ /**
2551
+ * Read a GML text into a sink.
2552
+ * @param input - the text, bytes or stream
2553
+ * @param sink - the sink
2554
+ * @param options - format-specific and common options
2555
+ * @returns the report
2556
+ */
2557
+ async import(input, sink, options) {
2558
+ const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
2559
+ const report = new ImportReportBuilder("gml", resolved.errorLimit);
2560
+ reportSinkOptions(sink, options, report);
2561
+ reportUnusedOptions(options, report, USED_OPTIONS);
2562
+ const text = await readText(input, report, resolved);
2563
+ throwIfAborted(resolved.signal);
2564
+ new GmlImport(sink, report, resolved, options).run(text);
2565
+ throwIfAborted(resolved.signal);
2566
+ return report.finish();
2567
+ }
2568
+ });
2569
+ const GML_ISSUE = Object.freeze({
2570
+ /** A grammar violation: an untokenisable bare token, an unclosed string or `[`, a stray `]`, a key without a value (fatal). */
2571
+ SYNTAX: SYNTAX_CODE,
2572
+ /** The input holds invalid UTF-8 (fatal). */
2573
+ INVALID_UTF8: INVALID_UTF8_CODE,
2574
+ /** No `graph [` block (fatal). */
2575
+ NO_GRAPH: NO_GRAPH_CODE,
2576
+ /** More than one `graph` block (fatal). */
2577
+ SECOND_GRAPH: SECOND_GRAPH_CODE,
2578
+ /** A node without an `id`. */
2579
+ MISSING_ID: MISSING_ID_CODE,
2580
+ /** A node without a `label` under nodeIdFrom "label". */
2581
+ MISSING_LABEL: MISSING_LABEL_CODE,
2582
+ /** An edge without `source` or `target`. */
2583
+ MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
2584
+ /** A node id, source or target that is not an integer. */
2585
+ ID_TYPE: ID_TYPE_CODE,
2586
+ /** A node id declared twice (later keys overwrite). */
2587
+ DUPLICATE_NODE: DUPLICATE_NODE_CODE,
2588
+ /** A structural key repeated in one element. */
2589
+ REPEATED_KEY: REPEATED_KEY_CODE,
2590
+ /** A `node` / `edge` key whose value is not a record. */
2591
+ ELEMENT_TYPE: ELEMENT_TYPE_CODE,
2592
+ /** A `directed` / `multigraph` flag that is not an integer. */
2593
+ FLAG_TYPE: FLAG_TYPE_CODE,
2594
+ /** A `directed` / `multigraph` flag outside 0 / 1, or repeated. */
2595
+ FLAG_VALUE: FLAG_VALUE_CODE,
2596
+ /** An integer beyond 2^53 rounded to f64. */
2597
+ PRECISION: PRECISION_CODE,
2598
+ /** A column renamed `<name>#<key>` because the sink held the name with another shape. */
2599
+ COLUMN_RENAMED: COLUMN_RENAMED_CODE,
2600
+ /** A column declared without its role because the sink already holds it. */
2601
+ ROLE_TAKEN: ROLE_TAKEN_CODE,
2602
+ /** Two id texts merged into one number under ids "number". */
2603
+ ID_MERGED: ID_MERGED_CODE,
2604
+ /** A common option the importer has no use for was given. */
2605
+ OPTION_IGNORED: OPTION_IGNORED_CODE,
2606
+ /** A builder-policy option the sink does not honour. */
2607
+ SINK_OPTION: SINK_OPTION_CODE,
2608
+ /** The sink refused the file's direction. */
2609
+ DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
2610
+ /** Edges forced to the policy's direction. */
2611
+ DIRECTION_FORCED: DIRECTION_FORCED_CODE,
2612
+ /** A mixed file under onMixedDirection "error" (fatal). */
2613
+ MIXED_DIRECTION: MIXED_DIRECTION_CODE,
2614
+ /** Under nodeIdFrom "label" / "index" the integer ids are not kept (a loss note). */
2615
+ ID_DROPPED: ID_DROPPED_CODE
2616
+ });
2617
+ const GML_LOSS = Object.freeze({
2618
+ /** A json column holds numbers; GML records cannot keep int versus real (design section 8.5). */
2619
+ RECORD_NUMBER_TYPE: RECORD_NUMBER_TYPE_CODE,
2620
+ /** A json column holds booleans, written 1 / 0. */
2621
+ RECORD_BOOLEAN: RECORD_BOOLEAN_CODE,
2622
+ /** A json column holds nulls, omitted. */
2623
+ RECORD_NULL: RECORD_NULL_CODE,
2624
+ /** An array inside an array; export() throws. */
2625
+ NESTED_ARRAY: NESTED_ARRAY_CODE,
2626
+ /** A json row that is an array, written as repeated keys. */
2627
+ JSON_ARRAY: JSON_ARRAY_CODE,
2628
+ /** A column name or record key outside the GML key grammar. */
2629
+ INVALID_KEY: INVALID_KEY_CODE,
2630
+ /** A column named like a structural key. */
2631
+ RESERVED_KEY: RESERVED_KEY_CODE,
2632
+ /** A key rewritten under sanitizeKeys "mangle". */
2633
+ KEY_MANGLED: KEY_MANGLED_CODE,
2634
+ /** A position column with more than three components. */
2635
+ POSITION_COMPONENTS: POSITION_COMPONENTS_CODE,
2636
+ /** A graphics record whose x / y / z the position column overrides. */
2637
+ GRAPHICS_OVERRIDDEN: GRAPHICS_OVERRIDDEN_CODE,
2638
+ /** A graphics record that cannot hold the position. */
2639
+ GRAPHICS_CONFLICT: GRAPHICS_CONFLICT_CODE
2640
+ });
2641
+ export {
2642
+ GML_ISSUE,
2643
+ GML_LOSS,
2644
+ gmlExporter,
2645
+ gmlImporter
2646
+ };
2647
+ //# sourceMappingURL=gml.js.map