@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
@@ -0,0 +1,3050 @@
1
+ import { GraphFormatError, INVALID_INDEX, MAX_COUNT } from "@graphty/graph-format";
2
+ import { c as childrenCsr } from "./children-CL3Cy0ez.js";
3
+ import { x as MIXED_DIRECTION_CODE, g as DIRECTION_FORCED_CODE, h as DIRECTION_REFUSED_CODE, W as SINK_OPTION_CODE, R as PRECISION_CODE, B as BAD_DEFAULT_CODE, a1 as UNKNOWN_ATTR_TYPE_CODE, V as ROLE_TAKEN_CODE, C as COLUMN_RENAMED_CODE, J as OPTION_IGNORED_CODE, n as ID_MERGED_CODE, Y as STRAY_TEXT_CODE, a2 as UNKNOWN_ELEMENT_CODE, H as HYPEREDGE_CODE, d as COUNT_HINT_CODE, y as MULTIPLE_GRAPHS_CODE, i as DUPLICATE_EDGE_ID_CODE, k as DUPLICATE_NODE_CODE, M as MISSING_ENDPOINT_CODE, w as MISSING_ID_CODE, j as DUPLICATE_KEY_CODE, G as NO_GRAPH_CODE, r as INVALID_UTF8_CODE, a7 as XML_SYNTAX_CODE, p as ID_TEXT_TYPE_CODE, U as ROLE_DROPPED_CODE, K as PARENTS_DROPPED_CODE, z as MUTUAL_AS_UNDIRECTED_CODE, aB as resolveExportOptions, au as joinText, ah as encodeChunks, a9 as capabilities, aa as checkCapabilities, L as LOSS, aw as pairFolding, aD as sanitizeIds, ai as explicitWeights, as as isNmtoken, aq as isCanonicalIntegerText, av as mangleNmtoken, an as formatInteger, al as formatF64, ak as formatF32, aC as resolveImportOptions, I as ImportReportBuilder, az as reportSinkOptions, aA as reportUnusedOptions, t as textChunks, a as throwIfAborted, s as IdCoercer, l as DirectionResolver, at as isWeightField, aL as declareAttribute, aI as uniqueColumnName, af as declareResolved, aZ as takenIn, ax as parseWeightText, aN as parseDeclaredValue, aO as losesPrecision } from "./writer-DxSKC7TL.js";
4
+ import { e as escapeXmlText, d as isWhitespace, k as isXmlName, f as escapeXmlAttribute, x as xmlIllegalTextNotes, t as tokenizeXml, X as XmlSyntaxError, l as localName } from "./escape-DyI8JofU.js";
5
+ const GRAPHML_NAMESPACE = "http://graphml.graphdrawing.org/xmlns";
6
+ const YFILES_NAMESPACE = "http://www.yworks.com/xml/graphml";
7
+ const XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
8
+ const SCHEMA_LOCATION = "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd";
9
+ const FORMAT = "graphml";
10
+ const EXTENSIONS = Object.freeze([".graphml", ".xml"]);
11
+ const MIME_TYPES = Object.freeze(["application/graphml+xml", "application/xml", "text/xml"]);
12
+ const ORIGINAL_ID_ATTRIBUTE = "graphty:originalId";
13
+ const ORIGINAL_ID_COLUMN = "graphty.originalId";
14
+ const EDGE_ID_COLUMN = "id";
15
+ const SOURCE_PORT_COLUMN = "sourceport";
16
+ const TARGET_PORT_COLUMN = "targetport";
17
+ const PARENT_COLUMN = "parent";
18
+ const HYPEREDGE_HUB_COLUMN = "graphty.hyperedge";
19
+ const LABEL_COLUMN = "label";
20
+ const RESERVED_EDGE_NAMES = /* @__PURE__ */ new Set([
21
+ EDGE_ID_COLUMN,
22
+ SOURCE_PORT_COLUMN,
23
+ TARGET_PORT_COLUMN
24
+ ]);
25
+ const RESERVED_NODE_NAMES = /* @__PURE__ */ new Set([PARENT_COLUMN]);
26
+ const META_KEY = "graphml";
27
+ const GRAPHML_ISSUE = Object.freeze({
28
+ /** Fatal: the input is not well-formed XML. */
29
+ XML_SYNTAX: XML_SYNTAX_CODE,
30
+ /** Fatal: the input holds invalid UTF-8. */
31
+ INVALID_UTF8: INVALID_UTF8_CODE,
32
+ /** Fatal: the root element is not `<graphml>`. */
33
+ NOT_GRAPHML: "E_NOT_GRAPHML",
34
+ /** Fatal: the document has no `<graph>`. */
35
+ NO_GRAPH: NO_GRAPH_CODE,
36
+ /** A `<key>` without an id. */
37
+ KEY_MISSING_ID: "E_GRAPHML_KEY_MISSING_ID",
38
+ /** Two `<key>` elements with the same id. */
39
+ DUPLICATE_KEY: DUPLICATE_KEY_CODE,
40
+ /** A key declared for hyperedges, ports or endpoints (never used). */
41
+ KEY_DOMAIN_UNSUPPORTED: "W_GRAPHML_KEY_DOMAIN_UNSUPPORTED",
42
+ /** A `<node>` without an id. */
43
+ MISSING_ID: MISSING_ID_CODE,
44
+ /** An `<edge>` without a source or a target. */
45
+ MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
46
+ /** A `<data>` whose key was never declared. */
47
+ UNKNOWN_KEY: "E_GRAPHML_UNKNOWN_KEY",
48
+ /** A `<data>` whose key is declared for another domain. */
49
+ KEY_DOMAIN: "W_GRAPHML_KEY_DOMAIN",
50
+ /** A `<data>` without a key attribute. */
51
+ DATA_MISSING_KEY: "E_GRAPHML_DATA_MISSING_KEY",
52
+ /** A `<key>` whose `for` is not a GraphML domain. */
53
+ KEY_FOR_INVALID: "E_GRAPHML_KEY_FOR_INVALID",
54
+ /** Data of a nested `<graph>`; only the top-level graph has attributes. */
55
+ NESTED_GRAPH_DATA: "W_GRAPHML_NESTED_GRAPH_DATA",
56
+ /** Data of a hyperedge expanded to a star or clique. */
57
+ HYPEREDGE_DATA_DROPPED: "W_GRAPHML_HYPEREDGE_DATA_DROPPED",
58
+ /** Nested elements in the `<data>` of a typed (non-yfiles) key. */
59
+ DATA_NESTED: "E_GRAPHML_DATA_NESTED",
60
+ /** A `<node>` declared twice. */
61
+ DUPLICATE_NODE: DUPLICATE_NODE_CODE,
62
+ /** An edge id already used by another edge. */
63
+ DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,
64
+ /** A `directed` attribute that is neither true nor false. */
65
+ INVALID_DIRECTED: "E_GRAPHML_INVALID_DIRECTED",
66
+ /** An `edgedefault` that is neither directed nor undirected. */
67
+ INVALID_EDGEDEFAULT: "E_GRAPHML_INVALID_EDGEDEFAULT",
68
+ /** A `<graph>` without edgedefault; the `defaultDirected` option applies. */
69
+ EDGEDEFAULT_MISSING: "W_GRAPHML_EDGEDEFAULT_MISSING",
70
+ /** A second top-level `<graph>`; its nodes and edges are merged into the first. */
71
+ MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
72
+ /** A `parse.nodes` / `parse.edges` hint the sink cannot reserve (ignored). */
73
+ COUNT_HINT: COUNT_HINT_CODE,
74
+ /** A hyperedge under `hyperedges: "error"`. */
75
+ HYPEREDGE: HYPEREDGE_CODE,
76
+ /** Hyperedges skipped under `hyperedges: "skip"`. */
77
+ HYPEREDGE_SKIPPED: "W_GRAPHML_HYPEREDGE_SKIPPED",
78
+ /** An endpoint of a hyperedge without a node, or with an unknown type. */
79
+ HYPEREDGE_ENDPOINT: "E_GRAPHML_HYPEREDGE_ENDPOINT",
80
+ /** `<port>` declarations (and their data) are not kept; sourceport / targetport edge attributes are. */
81
+ PORT_DECLARATION: "W_GRAPHML_PORT_DECLARATION",
82
+ /** A `<locator>` element. */
83
+ LOCATOR_DROPPED: "W_GRAPHML_LOCATOR_DROPPED",
84
+ /** A `<desc>` of a node, an edge or a hyperedge. */
85
+ DESC_DROPPED: "W_GRAPHML_DESC_DROPPED",
86
+ /** An element the GraphML schema does not define at that place. */
87
+ UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
88
+ /** Non-whitespace text where the schema allows only elements. */
89
+ STRAY_TEXT: STRAY_TEXT_CODE,
90
+ /** Two distinct id texts merged into one number under `ids: "number"`. */
91
+ ID_MERGED: ID_MERGED_CODE,
92
+ /** An option GraphML has no use for (`nodeIdFrom`: nodes are identified by their id attribute). */
93
+ OPTION_IGNORED: OPTION_IGNORED_CODE,
94
+ /** A yFiles key under `yfiles: "skip"`. */
95
+ YFILES_SKIPPED: "W_GRAPHML_YFILES_SKIPPED",
96
+ /** A key renamed `<name>#<id>` because the name was taken (design section 5.6). */
97
+ COLUMN_RENAMED: COLUMN_RENAMED_CODE,
98
+ /** A key declared without its role because the table already holds it. */
99
+ ROLE_TAKEN: ROLE_TAKEN_CODE,
100
+ /** A declared type the format does not define (kept as string). */
101
+ UNKNOWN_ATTR_TYPE: UNKNOWN_ATTR_TYPE_CODE,
102
+ /** A default that does not parse as the declared type. */
103
+ BAD_DEFAULT: BAD_DEFAULT_CODE,
104
+ /** A long value beyond 2^53 rounded. */
105
+ PRECISION: PRECISION_CODE,
106
+ /** A builder-policy option the sink does not honour. */
107
+ SINK_OPTION: SINK_OPTION_CODE,
108
+ /** The sink refused the file's direction. */
109
+ DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
110
+ /** Edges forced to the policy's direction. */
111
+ DIRECTION_FORCED: DIRECTION_FORCED_CODE,
112
+ /** A mixed file under onMixedDirection "error" (fatal). */
113
+ MIXED_DIRECTION: MIXED_DIRECTION_CODE
114
+ });
115
+ const GRAPHML_LOSS = Object.freeze({
116
+ /** yFiles nested XML kept as a JSON tree: structure preserved, not byte-exact. */
117
+ YFILES_JSON: "W_GRAPHML_YFILES_JSON",
118
+ /** A mutual pair written as one undirected edge; the mark is lost. */
119
+ MUTUAL_AS_UNDIRECTED: MUTUAL_AS_UNDIRECTED_CODE,
120
+ /** A `parents` (multi-parent) column cannot be written as nested graphs. */
121
+ PARENTS_DROPPED: PARENTS_DROPPED_CODE,
122
+ /** A role column GraphML has no slot for (kind, node ids, ...) written as a plain attribute. */
123
+ ROLE_DROPPED: ROLE_DROPPED_CODE,
124
+ /** Containment order differs from index order; node indices change after a round trip. */
125
+ HIERARCHY_REORDERED: "W_GRAPHML_HIERARCHY_REORDERED",
126
+ /** Nodes whose parent chain never reaches a root are written at the top level. */
127
+ PARENT_CYCLE: "W_GRAPHML_PARENT_CYCLE",
128
+ /** Node ids that change type after a round trip under the canonical rule (design section 4.1). */
129
+ ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
130
+ /** A numeric edge id column reads back as string. */
131
+ EDGE_ID_TEXT: "W_GRAPHML_EDGE_ID_TEXT",
132
+ /** A yfiles json value that is not a serialisable tree: export() will throw E_COLUMN_TYPE. */
133
+ YFILES_TREE: "E_GRAPHML_YFILES_TREE"
134
+ });
135
+ const ATTRIBUTE_PREFIX = "@_";
136
+ const TEXT_KEY = "#text";
137
+ class XmlTreeBuilder {
138
+ /** Create a builder whose implicit root is the containing element. */
139
+ constructor() {
140
+ this.frames = [];
141
+ this.root = { name: "", node: {}, hasAttrs: false, hasChildren: false, text: "" };
142
+ }
143
+ /**
144
+ * Whether the builder is inside a child element (as opposed to at the root level).
145
+ * @returns true while at least one child element is open
146
+ */
147
+ get open() {
148
+ return this.frames.length > 0;
149
+ }
150
+ /**
151
+ * An element starts.
152
+ * @param name - the element name as written
153
+ * @param attrs - its attributes
154
+ */
155
+ start(name, attrs) {
156
+ const node = {};
157
+ let hasAttrs = false;
158
+ for (const [key, value] of attrs) {
159
+ node[ATTRIBUTE_PREFIX + key] = value;
160
+ hasAttrs = true;
161
+ }
162
+ this.frames.push({ name, node, hasAttrs, hasChildren: false, text: "" });
163
+ }
164
+ /**
165
+ * Character data inside the current element.
166
+ * @param text - the text
167
+ */
168
+ text(text) {
169
+ const frame = this.frames.length > 0 ? this.frames[this.frames.length - 1] : this.root;
170
+ frame.text += text;
171
+ }
172
+ /** The current element ends; its value is attached to its parent. */
173
+ end() {
174
+ const frame = this.frames.pop();
175
+ if (frame === void 0) {
176
+ throw new GraphFormatError("E_COLUMN_TYPE", "XmlTreeBuilder.end() without a matching start()", {
177
+ reason: "tree builder underflow"
178
+ });
179
+ }
180
+ const parent = this.frames.length > 0 ? this.frames[this.frames.length - 1] : this.root;
181
+ attachChild(parent, frame.name, valueOf(frame));
182
+ }
183
+ /**
184
+ * The content of the containing element: its text when it has no child elements, otherwise
185
+ * the object of its children (with `#text` for non-whitespace mixed content).
186
+ * @returns the value to store in the json column
187
+ */
188
+ finish() {
189
+ if (this.frames.length > 0) {
190
+ throw new GraphFormatError("E_COLUMN_TYPE", "XmlTreeBuilder.finish() with open elements", {
191
+ reason: "tree builder open"
192
+ });
193
+ }
194
+ const { root } = this;
195
+ if (!root.hasChildren) {
196
+ return root.text;
197
+ }
198
+ if (!isWhitespace(root.text)) {
199
+ root.node[TEXT_KEY] = root.text;
200
+ }
201
+ return root.node;
202
+ }
203
+ }
204
+ function valueOf(frame) {
205
+ if (!frame.hasAttrs && !frame.hasChildren) {
206
+ return frame.text;
207
+ }
208
+ if (!isWhitespace(frame.text)) {
209
+ frame.node[TEXT_KEY] = frame.text;
210
+ }
211
+ return frame.node;
212
+ }
213
+ function attachChild(parent, name, value) {
214
+ parent.hasChildren = true;
215
+ const existing = parent.node[name];
216
+ if (existing === void 0) {
217
+ parent.node[name] = value;
218
+ } else if (Array.isArray(existing)) {
219
+ existing.push(value);
220
+ } else {
221
+ parent.node[name] = [existing, value];
222
+ }
223
+ }
224
+ function treeProblem(value) {
225
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
226
+ return null;
227
+ }
228
+ if (Array.isArray(value)) {
229
+ for (const item of value) {
230
+ if (Array.isArray(item)) {
231
+ return "nested arrays cannot be written as elements";
232
+ }
233
+ const problem = treeProblem(item);
234
+ if (problem !== null) {
235
+ return problem;
236
+ }
237
+ }
238
+ return null;
239
+ }
240
+ if (typeof value !== "object") {
241
+ return `a ${typeof value} cannot be written as XML`;
242
+ }
243
+ for (const [key, child] of Object.entries(value)) {
244
+ if (key === TEXT_KEY) {
245
+ if (child !== null && typeof child === "object") {
246
+ return "#text must be a scalar";
247
+ }
248
+ continue;
249
+ }
250
+ if (key.startsWith(ATTRIBUTE_PREFIX)) {
251
+ const name = key.slice(ATTRIBUTE_PREFIX.length);
252
+ if (!isXmlName(name)) {
253
+ return `"${name}" is not an XML attribute name`;
254
+ }
255
+ if (child !== null && typeof child === "object") {
256
+ return `attribute ${name} must be a scalar`;
257
+ }
258
+ continue;
259
+ }
260
+ if (!isXmlName(key)) {
261
+ return `"${key}" is not an XML element name`;
262
+ }
263
+ const problem = treeProblem(child);
264
+ if (problem !== null) {
265
+ return problem;
266
+ }
267
+ }
268
+ return null;
269
+ }
270
+ function writeXmlTree(value, indent, out) {
271
+ const problem = treeProblem(value);
272
+ if (problem !== null) {
273
+ throw new GraphFormatError("E_COLUMN_TYPE", `a yfiles value cannot be written as XML: ${problem}`, {
274
+ reason: "xml tree"
275
+ });
276
+ }
277
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
278
+ out.push(escapeXmlText(scalarText(value)));
279
+ return;
280
+ }
281
+ const object = value;
282
+ let text = null;
283
+ for (const [key, child] of Object.entries(object)) {
284
+ if (key === TEXT_KEY) {
285
+ text = scalarText(child);
286
+ } else if (!key.startsWith(ATTRIBUTE_PREFIX)) {
287
+ writeElements(key, child, indent, out);
288
+ }
289
+ }
290
+ if (text !== null) {
291
+ out.push(escapeXmlText(text));
292
+ }
293
+ }
294
+ function writeElements(name, value, indent, out) {
295
+ if (Array.isArray(value)) {
296
+ for (const item of value) {
297
+ writeElements(name, item, indent, out);
298
+ }
299
+ return;
300
+ }
301
+ if (value === null || typeof value !== "object") {
302
+ const text2 = scalarText(value);
303
+ out.push(text2.length === 0 ? `
304
+ ${indent}<${name}/>` : `
305
+ ${indent}<${name}>${escapeXmlText(text2)}</${name}>`);
306
+ return;
307
+ }
308
+ const object = value;
309
+ let attrs = "";
310
+ let text = null;
311
+ let hasChildren = false;
312
+ for (const [key, child] of Object.entries(object)) {
313
+ if (key === TEXT_KEY) {
314
+ text = scalarText(child);
315
+ } else if (key.startsWith(ATTRIBUTE_PREFIX)) {
316
+ attrs += ` ${key.slice(ATTRIBUTE_PREFIX.length)}="${escapeXmlAttribute(scalarText(child))}"`;
317
+ } else {
318
+ hasChildren = true;
319
+ }
320
+ }
321
+ if (!hasChildren) {
322
+ out.push(
323
+ text === null || text.length === 0 ? `
324
+ ${indent}<${name}${attrs}/>` : `
325
+ ${indent}<${name}${attrs}>${escapeXmlText(text)}</${name}>`
326
+ );
327
+ return;
328
+ }
329
+ out.push(`
330
+ ${indent}<${name}${attrs}>`);
331
+ const inner = `${indent} `;
332
+ for (const [key, child] of Object.entries(object)) {
333
+ if (key !== TEXT_KEY && !key.startsWith(ATTRIBUTE_PREFIX)) {
334
+ writeElements(key, child, inner, out);
335
+ }
336
+ }
337
+ if (text !== null) {
338
+ out.push(escapeXmlText(text));
339
+ }
340
+ out.push(`
341
+ ${indent}</${name}>`);
342
+ }
343
+ function scalarText(value) {
344
+ switch (typeof value) {
345
+ case "string":
346
+ return value;
347
+ case "number":
348
+ case "boolean":
349
+ case "bigint":
350
+ return String(value);
351
+ default:
352
+ return "";
353
+ }
354
+ }
355
+ const CAPABILITIES = capabilities({
356
+ mixedDirection: true,
357
+ multiEdges: true,
358
+ selfLoops: true,
359
+ edgeIds: "optional",
360
+ idCharset: "nmtoken",
361
+ dtypes: ["bool", "i32", "f32", "f64", "string"],
362
+ components: false,
363
+ lists: false,
364
+ json: false,
365
+ defaults: true,
366
+ options: false,
367
+ hierarchy: true,
368
+ temporal: "none",
369
+ graphAttributes: true,
370
+ positions: false,
371
+ viz: false
372
+ });
373
+ const STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
374
+ "directed",
375
+ "pair",
376
+ "mutual",
377
+ "weight",
378
+ "timeText",
379
+ "originalId"
380
+ ]);
381
+ const DROPPED_ROLES = /* @__PURE__ */ new Set([
382
+ "position",
383
+ "color",
384
+ "size",
385
+ "shape",
386
+ "thickness",
387
+ "start",
388
+ "end",
389
+ "timestamp",
390
+ "timestamps",
391
+ "spells",
392
+ "open"
393
+ ]);
394
+ const SLOT_ROLES = /* @__PURE__ */ new Set(["label", "id", "sourcePort", "targetPort", "parent"]);
395
+ const SLOT_ROLES_BY_DOMAIN = {
396
+ graph: /* @__PURE__ */ new Set(),
397
+ node: /* @__PURE__ */ new Set(["parent"]),
398
+ edge: /* @__PURE__ */ new Set(["id", "sourcePort", "targetPort"])
399
+ };
400
+ const ROLE_NAMES = Object.freeze({
401
+ label: LABEL_COLUMN,
402
+ id: EDGE_ID_COLUMN,
403
+ sourcePort: SOURCE_PORT_COLUMN,
404
+ targetPort: TARGET_PORT_COLUMN,
405
+ parent: PARENT_COLUMN
406
+ });
407
+ const DEFAULT_WEIGHT_NAME = "weight";
408
+ const TYPE_DTYPES = {
409
+ boolean: "bool",
410
+ int: "i32",
411
+ long: "f64",
412
+ float: "f32",
413
+ double: "f64",
414
+ string: "string"
415
+ };
416
+ const I32_MAX = 2147483647;
417
+ const DEFAULT_YFILES_TYPES = {
418
+ node: "nodegraphics",
419
+ edge: "edgegraphics",
420
+ graph: "resources"
421
+ };
422
+ function resolveFormatOptions(options) {
423
+ const pretty = options?.pretty ?? true;
424
+ if (typeof pretty !== "boolean") {
425
+ throw new GraphFormatError("E_UNSUPPORTED", "option pretty must be a boolean", {
426
+ option: "pretty",
427
+ found: typeof pretty
428
+ });
429
+ }
430
+ const edgedefault = options?.edgedefault ?? null;
431
+ if (edgedefault !== null && edgedefault !== "directed" && edgedefault !== "undirected") {
432
+ throw new GraphFormatError(
433
+ "E_UNSUPPORTED",
434
+ `option edgedefault: ${JSON.stringify(edgedefault)} is not "directed" or "undirected"`,
435
+ { option: "edgedefault", found: edgedefault }
436
+ );
437
+ }
438
+ return { pretty, edgedefault };
439
+ }
440
+ function graphmlMetaOf(snapshot) {
441
+ const value = snapshot.meta.extra[META_KEY];
442
+ if (typeof value !== "object" || value === null) {
443
+ return null;
444
+ }
445
+ const record = value;
446
+ const graphId = typeof record.graphId === "string" ? record.graphId : null;
447
+ const edgedefault = record.edgedefault === "directed" || record.edgedefault === "undirected" ? record.edgedefault : null;
448
+ const namespaces = {};
449
+ if (typeof record.namespaces === "object" && record.namespaces !== null) {
450
+ for (const [prefix, uri] of Object.entries(record.namespaces)) {
451
+ if (typeof uri === "string") {
452
+ namespaces[prefix] = uri;
453
+ }
454
+ }
455
+ }
456
+ return { graphId, edgedefault, namespaces };
457
+ }
458
+ function planExport(snapshot, options, format) {
459
+ const notes = checkCapabilities(snapshot, CAPABILITIES, options, {
460
+ roles: SLOT_ROLES,
461
+ roleNames: ROLE_NAMES
462
+ }).filter((n) => n.code !== LOSS.JSON);
463
+ const note = (code, message, column = null, count = null) => {
464
+ notes.push(Object.freeze({ code, message, column, count }));
465
+ };
466
+ notes.push(...xmlIllegalTextNotes(snapshot));
467
+ const meta = graphmlMetaOf(snapshot);
468
+ const keyIds = new KeyIds();
469
+ const graphColumns = planTable(snapshot.graph, "graph", notes, note);
470
+ const nodeColumns = planTable(snapshot.nodes, "node", notes, note);
471
+ const edgeColumns = planTable(snapshot.edges, "edge", notes, note);
472
+ const keys = planKeys([...graphColumns, ...nodeColumns, ...edgeColumns], keyIds);
473
+ reservedNameNotes(nodeColumns, RESERVED_NODE_NAMES, note);
474
+ reservedNameNotes(edgeColumns, RESERVED_EDGE_NAMES, note);
475
+ const weight = planWeight(snapshot, edgeColumns, keyIds, keys, note);
476
+ const idTypeChanges = countIdTypeChanges(snapshot);
477
+ if (idTypeChanges > 0) {
478
+ note(
479
+ GRAPHML_LOSS.ID_TEXT_TYPE,
480
+ `${idTypeChanges} node id(s) change type when read back under ids: "canonical" (string ids that are integer text, non-integer numbers)`,
481
+ null,
482
+ idTypeChanges
483
+ );
484
+ }
485
+ const edgeIdColumn = snapshot.edges.byRole("id");
486
+ if (edgeIdColumn !== null) {
487
+ planEdgeIds(snapshot, edgeIdColumn, options, note);
488
+ }
489
+ const unrepresentable = countUnrepresentableNodeIds(snapshot);
490
+ const needsOriginalIdKey = unrepresentable > 0 && options.sanitizeIds === "mangle";
491
+ const originalIdKey = needsOriginalIdKey ? keyIds.next(null) : null;
492
+ const folding = pairFolding(snapshot, { foldMutual: true });
493
+ if (folding.mutualCount > 0) {
494
+ note(
495
+ GRAPHML_LOSS.MUTUAL_AS_UNDIRECTED,
496
+ `${folding.mutualCount} mutual pair(s) are written as undirected edges; the mutual mark is lost`,
497
+ null,
498
+ folding.mutualCount
499
+ );
500
+ }
501
+ const edgedefault = planEdgedefault(snapshot, folding, format, meta);
502
+ const hierarchy = planHierarchy(snapshot);
503
+ if (hierarchy.reordered) {
504
+ note(
505
+ GRAPHML_LOSS.HIERARCHY_REORDERED,
506
+ "nodes are written in containment order (children nested under their parent); node indices change after a round trip",
507
+ hierarchy.parent?.meta.name ?? null
508
+ );
509
+ }
510
+ if (hierarchy.unreachable > 0) {
511
+ note(
512
+ GRAPHML_LOSS.PARENT_CYCLE,
513
+ `${hierarchy.unreachable} node(s) whose parent chain never reaches a root are written at the top level`,
514
+ hierarchy.parent?.meta.name ?? null,
515
+ hierarchy.unreachable
516
+ );
517
+ }
518
+ return {
519
+ options,
520
+ pretty: format.pretty,
521
+ notes,
522
+ keys,
523
+ graphColumns,
524
+ nodeColumns,
525
+ edgeColumns,
526
+ weight,
527
+ originalIdKey,
528
+ edgedefault,
529
+ folding,
530
+ edgeIdColumn,
531
+ sourcePortColumn: snapshot.edges.byRole("sourcePort"),
532
+ targetPortColumn: snapshot.edges.byRole("targetPort"),
533
+ hierarchy,
534
+ meta,
535
+ needsOriginalIdKey
536
+ };
537
+ }
538
+ function reservedNameNotes(plans, reserved, note) {
539
+ for (const plan of plans) {
540
+ const { column, attrName, keyId } = plan;
541
+ if (attrName === null || !reserved.has(attrName)) {
542
+ continue;
543
+ }
544
+ note(
545
+ LOSS.COLUMN_NAME_CHANGED,
546
+ `${plan.domain} column "${column.meta.name}" is titled like the importer's ${attrName} column and reads back as "${attrName}#${keyId}"`,
547
+ column.meta.name,
548
+ column.length - column.nullCount
549
+ );
550
+ }
551
+ }
552
+ class KeyIds {
553
+ constructor() {
554
+ this.used = /* @__PURE__ */ new Set();
555
+ this.counter = 0;
556
+ }
557
+ /**
558
+ * Take a key id.
559
+ * @param preferred - the id to keep when it is an NMTOKEN not handed out yet, or null
560
+ * @returns the id
561
+ */
562
+ next(preferred) {
563
+ if (preferred !== null && isNmtoken(preferred) && !this.used.has(preferred)) {
564
+ this.used.add(preferred);
565
+ return preferred;
566
+ }
567
+ for (; ; ) {
568
+ const candidate = `d${this.counter}`;
569
+ this.counter++;
570
+ if (!this.used.has(candidate)) {
571
+ this.used.add(candidate);
572
+ return candidate;
573
+ }
574
+ }
575
+ }
576
+ }
577
+ function planKeys(all, keyIds) {
578
+ const keys = [];
579
+ const byOrigin = /* @__PURE__ */ new Map();
580
+ for (const plan of all) {
581
+ const { origin } = plan.column.meta;
582
+ if (origin !== null && origin.format === FORMAT && origin.id !== null) {
583
+ const group = byOrigin.get(origin.id) ?? [];
584
+ group.push(plan);
585
+ byOrigin.set(origin.id, group);
586
+ }
587
+ }
588
+ const planned = /* @__PURE__ */ new Set();
589
+ for (const plan of all) {
590
+ if (planned.has(plan)) {
591
+ continue;
592
+ }
593
+ const { origin } = plan.column.meta;
594
+ const group = origin !== null && origin.format === FORMAT && origin.id !== null ? byOrigin.get(origin.id) ?? [plan] : [plan];
595
+ const shared = group.length > 1 && group.every((other) => other === plan || sameKey(plan, other));
596
+ const members = shared ? group : [plan];
597
+ const id = keyIds.next(origin !== null && origin.format === FORMAT ? origin.id : null);
598
+ for (const member of members) {
599
+ member.keyId = id;
600
+ planned.add(member);
601
+ }
602
+ const domains = members.map((member) => member.domain);
603
+ const yfilesName = plan.column.meta.origin?.title ?? plan.column.meta.name;
604
+ let { attrName } = plan;
605
+ if (plan.yfiles) {
606
+ attrName = yfilesName === id ? null : yfilesName;
607
+ }
608
+ keys.push({
609
+ id,
610
+ domains: domains.length > 1 ? ["graph", "node", "edge"] : domains,
611
+ attrName,
612
+ attrType: plan.attrType,
613
+ yfilesType: plan.yfilesType,
614
+ column: plan.column
615
+ });
616
+ }
617
+ return keys;
618
+ }
619
+ function planWeight(snapshot, edgeColumns, keyIds, keys, note) {
620
+ const weights = explicitWeights(snapshot);
621
+ const origin = snapshot.meta.weightOrigin;
622
+ const fromGraphml = weights.weighted && origin !== null && origin.format === FORMAT;
623
+ const attrName = fromGraphml && origin.title !== null ? origin.title : DEFAULT_WEIGHT_NAME;
624
+ for (const plan of edgeColumns) {
625
+ if (plan.attrName === attrName) {
626
+ note(
627
+ LOSS.WEIGHT_KEY_CLASH,
628
+ `edge column "${plan.column.meta.name}" is written as a key titled "${attrName}", which the importer reads as THE weight (weightFrom); it reads back as the weight, not as a column`,
629
+ plan.column.meta.name,
630
+ plan.column.length - plan.column.nullCount
631
+ );
632
+ }
633
+ }
634
+ if (!weights.weighted) {
635
+ return null;
636
+ }
637
+ let attrType = "double";
638
+ if (fromGraphml && origin.type !== null) {
639
+ const declared = origin.type.trim().toLowerCase();
640
+ if (declared === "float" || declared === "double" || declared === "long" || declared === "int") {
641
+ attrType = declared;
642
+ }
643
+ }
644
+ if ((attrType === "int" || attrType === "long") && !weightsIntegral(snapshot, weights)) {
645
+ attrType = "double";
646
+ }
647
+ const id = keyIds.next(fromGraphml ? origin.id : null);
648
+ keys.push({ id, domains: ["edge"], attrName, attrType, yfilesType: null, column: null });
649
+ return { keyId: id, attrType, weights };
650
+ }
651
+ function planEdgeIds(snapshot, column, options, note) {
652
+ if (column.dtype !== "string" && column.dtype !== "dict") {
653
+ note(
654
+ GRAPHML_LOSS.EDGE_ID_TEXT,
655
+ `edge id column "${column.meta.name}" is ${column.dtype}; GraphML edge ids read back as strings`,
656
+ column.meta.name,
657
+ snapshot.edgeCount - column.nullCount
658
+ );
659
+ }
660
+ const bad = countBadEdgeIds(column);
661
+ if (bad === 0) {
662
+ return;
663
+ }
664
+ if (options.sanitizeIds === "mangle") {
665
+ note(LOSS.ID_MANGLED, `${bad} edge id(s) outside the nmtoken charset are rewritten`, column.meta.name, bad);
666
+ } else {
667
+ note(
668
+ LOSS.ID_CHARSET,
669
+ `${bad} edge id(s) outside the nmtoken charset; export() will throw unless sanitizeIds is "mangle"`,
670
+ column.meta.name,
671
+ bad
672
+ );
673
+ }
674
+ }
675
+ function planEdgedefault(snapshot, folding, format, meta) {
676
+ if (format.edgedefault !== null) {
677
+ return format.edgedefault;
678
+ }
679
+ if (!snapshot.directed) {
680
+ return "undirected";
681
+ }
682
+ let directedCount = 0;
683
+ let undirectedCount = 0;
684
+ for (let e = 0; e < snapshot.edgeCount; e++) {
685
+ if (folding.folded(e)) {
686
+ continue;
687
+ }
688
+ if (isDirectedEdge(folding, e)) {
689
+ directedCount++;
690
+ } else {
691
+ undirectedCount++;
692
+ }
693
+ }
694
+ if (undirectedCount === 0) {
695
+ return "directed";
696
+ }
697
+ const recorded = meta === null ? null : meta.edgedefault;
698
+ if (recorded !== null) {
699
+ return recorded;
700
+ }
701
+ return undirectedCount > directedCount ? "undirected" : "directed";
702
+ }
703
+ function sameKey(a, b) {
704
+ return a.attrName === b.attrName && a.attrType === b.attrType && a.yfilesType === b.yfilesType && a.domain !== b.domain && JSON.stringify(a.column.meta.default ?? null) === JSON.stringify(b.column.meta.default ?? null);
705
+ }
706
+ function planTable(table, domain, notes, note) {
707
+ const plans = [];
708
+ const columns = [...table];
709
+ const names = new Set(columns.map((column) => column.meta.name));
710
+ for (const column of columns) {
711
+ const { meta } = column;
712
+ const { role, name } = meta;
713
+ if (role === "parents") {
714
+ note(
715
+ GRAPHML_LOSS.PARENTS_DROPPED,
716
+ `${domain} column "${name}" (parents) cannot be written: nested graphs hold one parent per node`,
717
+ name,
718
+ column.length - column.nullCount
719
+ );
720
+ continue;
721
+ }
722
+ if (role !== null && (STRUCTURAL_ROLES.has(role) || DROPPED_ROLES.has(role) || SLOT_ROLES_BY_DOMAIN[domain].has(role))) {
723
+ continue;
724
+ }
725
+ if (role !== null && role !== "label" && SLOT_ROLES.has(role)) {
726
+ withdrawNameChange(notes, name);
727
+ note(
728
+ LOSS.ROLE,
729
+ `${domain} column "${name}" (${role}) is written as a plain attribute; GraphML has no ${role} slot for a ${domain} and the role is lost`,
730
+ name,
731
+ column.length - column.nullCount
732
+ );
733
+ }
734
+ const yfiles = meta.dtype === "json" && meta.origin?.namespace === "yfiles";
735
+ if (meta.dtype === "json" && !yfiles) {
736
+ note(
737
+ LOSS.JSON,
738
+ `${domain} column "${name}" holds nested values; written as JSON text, which reads back as string`,
739
+ name,
740
+ column.length - column.nullCount
741
+ );
742
+ }
743
+ if (yfiles) {
744
+ const bad = countBadTrees(column);
745
+ if (bad > 0) {
746
+ note(
747
+ GRAPHML_LOSS.YFILES_TREE,
748
+ `${bad} value(s) of yfiles column "${name}" are not XML trees; export() will throw E_COLUMN_TYPE`,
749
+ name,
750
+ bad
751
+ );
752
+ }
753
+ }
754
+ const { origin } = meta;
755
+ const fromGraphml = origin !== null && origin.format === FORMAT;
756
+ let attrName = null;
757
+ if (!yfiles) {
758
+ attrName = fromGraphml && origin.title !== null ? origin.title : name;
759
+ if (role === "label" && domain !== "graph" && attrName !== LABEL_COLUMN) {
760
+ attrName = labelSlot(column, domain, names, notes, note);
761
+ } else if (role === null && domain !== "graph" && attrName === LABEL_COLUMN) {
762
+ note(
763
+ LOSS.ROLE_ASSUMED,
764
+ `${domain} column "${name}" has no role but is titled "${LABEL_COLUMN}", which the importer reads back with the label role`,
765
+ name,
766
+ column.length - column.nullCount
767
+ );
768
+ }
769
+ }
770
+ plans.push({
771
+ column,
772
+ domain,
773
+ keyId: "",
774
+ attrType: yfiles ? null : attrTypeFor(column),
775
+ attrName,
776
+ yfilesType: yfiles ? origin?.type ?? DEFAULT_YFILES_TYPES[domain] : null,
777
+ yfiles
778
+ });
779
+ }
780
+ return plans;
781
+ }
782
+ function labelSlot(column, domain, names, notes, note) {
783
+ const { name } = column.meta;
784
+ if (!names.has(LABEL_COLUMN)) {
785
+ return LABEL_COLUMN;
786
+ }
787
+ withdrawNameChange(notes, name);
788
+ note(
789
+ LOSS.ROLE,
790
+ `${domain} column "${name}" (label) is written as a plain attribute: the label slot (a key titled "${LABEL_COLUMN}") is taken by column "${LABEL_COLUMN}" and the role is lost`,
791
+ name,
792
+ column.length - column.nullCount
793
+ );
794
+ return name;
795
+ }
796
+ function withdrawNameChange(notes, name) {
797
+ const at = notes.findIndex((n) => n.code === LOSS.COLUMN_NAME_CHANGED && n.column === name);
798
+ if (at >= 0) {
799
+ notes.splice(at, 1);
800
+ }
801
+ }
802
+ function attrTypeFor(column) {
803
+ const { meta } = column;
804
+ const { origin } = meta;
805
+ const declared = origin !== null && origin.format === FORMAT && origin.type !== null ? origin.type : null;
806
+ if (meta.components > 1) {
807
+ return "string";
808
+ }
809
+ switch (meta.dtype) {
810
+ case "bool":
811
+ return "boolean";
812
+ case "i32":
813
+ return "int";
814
+ case "f32":
815
+ return "float";
816
+ case "f64": {
817
+ if (declared !== null && declared.trim().toLowerCase() === "long" && columnIntegral(column)) {
818
+ return "long";
819
+ }
820
+ return "double";
821
+ }
822
+ case "u8":
823
+ return "int";
824
+ case "u32":
825
+ return columnMax(column) <= I32_MAX ? "int" : "long";
826
+ case "string":
827
+ if (declared !== null && TYPE_DTYPES[declared.trim().toLowerCase()] === void 0) {
828
+ return declared;
829
+ }
830
+ return "string";
831
+ case "dict":
832
+ case "list":
833
+ case "json":
834
+ return "string";
835
+ default: {
836
+ const { dtype } = meta;
837
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown dtype ${String(dtype)}`, { dtype });
838
+ }
839
+ }
840
+ }
841
+ function columnIntegral(column) {
842
+ if (column.dtype !== "f64") {
843
+ return false;
844
+ }
845
+ const { data } = column;
846
+ for (let r = 0; r < column.length; r++) {
847
+ if (column.isSet(r) && !Number.isInteger(data[r])) {
848
+ return false;
849
+ }
850
+ }
851
+ return true;
852
+ }
853
+ function columnMax(column) {
854
+ if (column.dtype !== "u32") {
855
+ return 0;
856
+ }
857
+ let max = 0;
858
+ const { data } = column;
859
+ for (let r = 0; r < column.length; r++) {
860
+ if (column.isSet(r) && data[r] > max) {
861
+ max = data[r];
862
+ }
863
+ }
864
+ return max;
865
+ }
866
+ function weightsIntegral(snapshot, weights) {
867
+ for (let e = 0; e < snapshot.edgeCount; e++) {
868
+ if (weights.isExplicit(e) && !Number.isInteger(weights.value(e))) {
869
+ return false;
870
+ }
871
+ }
872
+ return true;
873
+ }
874
+ function countBadTrees(column) {
875
+ if (column.dtype !== "json") {
876
+ return 0;
877
+ }
878
+ let bad = 0;
879
+ for (let r = 0; r < column.length; r++) {
880
+ if (column.isSet(r) && treeProblem(column.values[r]) !== null) {
881
+ bad++;
882
+ }
883
+ }
884
+ return bad;
885
+ }
886
+ function countIdTypeChanges(snapshot) {
887
+ const { ids } = snapshot;
888
+ if (ids.kind === "identity" || ids.kind === "dense") {
889
+ return 0;
890
+ }
891
+ let count = 0;
892
+ for (let i = 0; i < ids.size; i++) {
893
+ const id = ids.idOf(i);
894
+ if (typeof id === "string" ? isNmtoken(id) && isCanonicalIntegerText(id) : !Number.isSafeInteger(id)) {
895
+ count++;
896
+ }
897
+ }
898
+ return count;
899
+ }
900
+ function countUnrepresentableNodeIds(snapshot) {
901
+ const { ids } = snapshot;
902
+ if (ids.kind === "identity" || ids.kind === "dense") {
903
+ return 0;
904
+ }
905
+ let count = 0;
906
+ for (let i = 0; i < ids.size; i++) {
907
+ if (!isNmtoken(String(ids.idOf(i)))) {
908
+ count++;
909
+ }
910
+ }
911
+ return count;
912
+ }
913
+ function countBadEdgeIds(column) {
914
+ let bad = 0;
915
+ for (let e = 0; e < column.length; e++) {
916
+ if (column.isSet(e) && !isNmtoken(edgeIdText(column, e))) {
917
+ bad++;
918
+ }
919
+ }
920
+ return bad;
921
+ }
922
+ function edgeIdText(column, e) {
923
+ const value = column.value(e);
924
+ if (typeof value === "string") {
925
+ return value;
926
+ }
927
+ if (typeof value === "number") {
928
+ return Number.isInteger(value) ? formatInteger(value) : formatF64(value);
929
+ }
930
+ return String(value);
931
+ }
932
+ function isDirectedEdge(folding, e) {
933
+ return folding.mateOf(e) === INVALID_INDEX && folding.sourceDirected(e);
934
+ }
935
+ function planHierarchy(snapshot) {
936
+ const parent = snapshot.nodes.byRole("parent");
937
+ const csr = childrenCsr(snapshot, { column: parent !== null && parent.dtype === "u32" ? parent : null });
938
+ const walk = csr.depthFirst();
939
+ const roots = walk.order.filter((u) => walk.depth[u] === 0);
940
+ return {
941
+ parent: csr.column,
942
+ roots,
943
+ childStart: csr.rowPtr,
944
+ childList: csr.children,
945
+ reordered: walk.reordered,
946
+ unreachable: csr.unreachable
947
+ };
948
+ }
949
+ function cellText(column, row, attrType) {
950
+ if (column.meta.components > 1) {
951
+ const value = column.value(row);
952
+ return JSON.stringify(Array.from(value));
953
+ }
954
+ switch (column.dtype) {
955
+ case "bool":
956
+ return column.value(row) === true ? "true" : "false";
957
+ case "i32":
958
+ case "u32":
959
+ case "u8":
960
+ return String(column.data[row]);
961
+ case "f32":
962
+ return formatF32(column.data[row]);
963
+ case "f64": {
964
+ const value = column.data[row];
965
+ return (attrType === "long" || attrType === "int") && Number.isInteger(value) ? formatInteger(value) : formatF64(value);
966
+ }
967
+ case "string":
968
+ return column.valueAt(row);
969
+ case "dict":
970
+ return column.dictionary[column.codes[row]];
971
+ case "list":
972
+ return JSON.stringify(Array.from(column.sliceOf(row)));
973
+ case "json":
974
+ return JSON.stringify(column.values[row]) ?? "";
975
+ default: {
976
+ const dtype = column;
977
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown dtype ${String(dtype)}`, {});
978
+ }
979
+ }
980
+ }
981
+ function defaultText(column, attrType) {
982
+ const value = column.meta.default;
983
+ if (value === void 0) {
984
+ return null;
985
+ }
986
+ switch (typeof value) {
987
+ case "boolean":
988
+ return value ? "true" : "false";
989
+ case "number":
990
+ if (column.dtype === "f32") {
991
+ return formatF32(value);
992
+ }
993
+ return (attrType === "long" || attrType === "int") && Number.isInteger(value) ? formatInteger(value) : formatF64(value);
994
+ case "string":
995
+ return value;
996
+ default:
997
+ return JSON.stringify(value) ?? "";
998
+ }
999
+ }
1000
+ function writeData(plan, row, indent, out) {
1001
+ const { column } = plan;
1002
+ if (!column.isSet(row)) {
1003
+ return;
1004
+ }
1005
+ if (plan.yfiles && column.dtype === "json") {
1006
+ writeTreeData(plan.keyId, column.values[row], indent, out);
1007
+ return;
1008
+ }
1009
+ out.push(`
1010
+ ${indent}<data key="${plan.keyId}">${escapeXmlText(cellText(column, row, plan.attrType))}</data>`);
1011
+ }
1012
+ function writeTreeData(keyId, value, indent, out) {
1013
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1014
+ const parts = [];
1015
+ writeXmlTree(value, indent, parts);
1016
+ out.push(`
1017
+ ${indent}<data key="${keyId}">${parts.join("")}</data>`);
1018
+ return;
1019
+ }
1020
+ out.push(`
1021
+ ${indent}<data key="${keyId}">`);
1022
+ writeXmlTree(value, `${indent} `, out);
1023
+ out.push(`
1024
+ ${indent}</data>`);
1025
+ }
1026
+ function keyElement(key, indent) {
1027
+ const domain = key.domains.length === 3 ? "all" : key.domains[0];
1028
+ let text = `${indent}<key id="${escapeXmlAttribute(key.id)}" for="${domain}"`;
1029
+ if (key.attrName !== null) {
1030
+ text += ` attr.name="${escapeXmlAttribute(key.attrName)}"`;
1031
+ }
1032
+ if (key.attrType !== null) {
1033
+ text += ` attr.type="${escapeXmlAttribute(key.attrType)}"`;
1034
+ }
1035
+ if (key.yfilesType !== null) {
1036
+ text += ` yfiles.type="${escapeXmlAttribute(key.yfilesType)}"`;
1037
+ }
1038
+ const { column } = key;
1039
+ const desc = column?.meta.extra.desc;
1040
+ const hasDefault = column !== null && column.meta.default !== void 0;
1041
+ if (column === null || typeof desc !== "string" && !hasDefault) {
1042
+ return `${text}/>
1043
+ `;
1044
+ }
1045
+ const parts = [`${text}>`];
1046
+ const inner = `${indent} `;
1047
+ if (typeof desc === "string") {
1048
+ parts.push(`
1049
+ ${inner}<desc>${escapeXmlText(desc)}</desc>`);
1050
+ }
1051
+ if (hasDefault) {
1052
+ if (key.yfilesType !== null) {
1053
+ const value = column.meta.default;
1054
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1055
+ parts.push(`
1056
+ ${inner}<default>`);
1057
+ writeXmlTree(value, `${inner} `, parts);
1058
+ parts.push(`
1059
+ ${inner}</default>`);
1060
+ } else {
1061
+ const tree = [];
1062
+ writeXmlTree(value, inner, tree);
1063
+ parts.push(`
1064
+ ${inner}<default>${tree.join("")}</default>`);
1065
+ }
1066
+ } else {
1067
+ parts.push(`
1068
+ ${inner}<default>${escapeXmlText(defaultText(column, key.attrType) ?? "")}</default>`);
1069
+ }
1070
+ }
1071
+ parts.push(`
1072
+ ${indent}</key>
1073
+ `);
1074
+ return parts.join("");
1075
+ }
1076
+ function sanitizeEdgeIds(column, mode, edgeCount) {
1077
+ const out = new Array(edgeCount).fill(null);
1078
+ if (column === null) {
1079
+ return out;
1080
+ }
1081
+ const used = /* @__PURE__ */ new Set();
1082
+ const bad = [];
1083
+ for (let e = 0; e < edgeCount; e++) {
1084
+ if (!column.isSet(e)) {
1085
+ continue;
1086
+ }
1087
+ const text = edgeIdText(column, e);
1088
+ if (isNmtoken(text)) {
1089
+ out[e] = text;
1090
+ used.add(text);
1091
+ } else {
1092
+ bad.push(e);
1093
+ }
1094
+ }
1095
+ if (bad.length > 0 && mode === "error") {
1096
+ throw new GraphFormatError(
1097
+ "E_INVALID_ID",
1098
+ `${bad.length} edge id(s) cannot be written as nmtoken (first: ${JSON.stringify(edgeIdText(column, bad[0]))} at edge ${bad[0]}); pass sanitizeIds: "mangle" to rewrite them`,
1099
+ { reason: "charset", charset: "nmtoken", count: bad.length, edge: bad[0] }
1100
+ );
1101
+ }
1102
+ for (const e of bad) {
1103
+ const base = mangleNmtoken(edgeIdText(column, e));
1104
+ let candidate = base;
1105
+ for (let k = 2; used.has(candidate); k++) {
1106
+ candidate = `${base}_${k}`;
1107
+ }
1108
+ used.add(candidate);
1109
+ out[e] = candidate;
1110
+ }
1111
+ return out;
1112
+ }
1113
+ function* writeGraphml(snapshot, plan) {
1114
+ const ids = sanitizeIds(snapshot, "nmtoken", plan.options.sanitizeIds);
1115
+ const edgeIds = sanitizeEdgeIds(plan.edgeIdColumn, plan.options.sanitizeIds, snapshot.edgeCount);
1116
+ const i1 = plan.pretty ? " " : "";
1117
+ const i2 = plan.pretty ? " " : "";
1118
+ const i3 = plan.pretty ? " " : "";
1119
+ const step = plan.pretty ? " " : "";
1120
+ yield '<?xml version="1.0" encoding="UTF-8"?>\n';
1121
+ let root = `<graphml xmlns="${GRAPHML_NAMESPACE}"`;
1122
+ const namespaces = { ...plan.meta?.namespaces ?? {} };
1123
+ if ((plan.nodeColumns.some((c) => c.yfiles) || plan.edgeColumns.some((c) => c.yfiles) || plan.graphColumns.some((c) => c.yfiles)) && !Object.values(namespaces).includes(YFILES_NAMESPACE)) {
1124
+ namespaces.y = YFILES_NAMESPACE;
1125
+ }
1126
+ if (!Object.values(namespaces).includes(XSI_NAMESPACE)) {
1127
+ namespaces.xsi = XSI_NAMESPACE;
1128
+ }
1129
+ for (const [prefix, uri] of Object.entries(namespaces)) {
1130
+ root += ` xmlns:${prefix}="${escapeXmlAttribute(uri)}"`;
1131
+ }
1132
+ const xsiPrefix = Object.entries(namespaces).find(([, uri]) => uri === XSI_NAMESPACE)?.[0] ?? "xsi";
1133
+ root += ` ${xsiPrefix}:schemaLocation="${SCHEMA_LOCATION}">
1134
+ `;
1135
+ yield root;
1136
+ for (const key of plan.keys) {
1137
+ yield keyElement(key, i1);
1138
+ }
1139
+ if (plan.originalIdKey !== null) {
1140
+ yield `${i1}<key id="${plan.originalIdKey}" for="node" attr.name="${ORIGINAL_ID_ATTRIBUTE}" attr.type="string"/>
1141
+ `;
1142
+ }
1143
+ const graphId = plan.meta?.graphId ?? "G";
1144
+ yield `${i1}<graph id="${escapeXmlAttribute(graphId)}" edgedefault="${plan.edgedefault}">`;
1145
+ const { description } = snapshot.meta;
1146
+ if (description !== null) {
1147
+ yield `
1148
+ ${i2}<desc>${escapeXmlText(description)}</desc>`;
1149
+ }
1150
+ const graphParts = [];
1151
+ for (const column of plan.graphColumns) {
1152
+ writeData(column, 0, i2, graphParts);
1153
+ }
1154
+ if (graphParts.length > 0) {
1155
+ yield graphParts.join("");
1156
+ }
1157
+ const { hierarchy } = plan;
1158
+ const nested = hierarchy.childList.length > 0;
1159
+ const stackNode = [];
1160
+ const stackCursor = [];
1161
+ const written = nested ? new Uint8Array(snapshot.nodeCount) : null;
1162
+ for (const rootNode of hierarchy.roots) {
1163
+ yield* writeNodeSubtree(rootNode, plan, ids, i2, step, stackNode, stackCursor, written);
1164
+ }
1165
+ const list = snapshot.edgeList();
1166
+ const { folding } = plan;
1167
+ for (let e = 0; e < snapshot.edgeCount; e++) {
1168
+ if (folding.folded(e)) {
1169
+ continue;
1170
+ }
1171
+ const parts = [`
1172
+ ${i2}<edge`];
1173
+ const id = edgeIds[e];
1174
+ if (id !== null) {
1175
+ parts.push(` id="${escapeXmlAttribute(id)}"`);
1176
+ }
1177
+ parts.push(
1178
+ ` source="${escapeXmlAttribute(String(ids.idAt(list.src[e])))}" target="${escapeXmlAttribute(String(ids.idAt(list.dst[e])))}"`
1179
+ );
1180
+ const directed = snapshot.directed && isDirectedEdge(folding, e);
1181
+ if (directed !== (plan.edgedefault === "directed")) {
1182
+ parts.push(` directed="${directed ? "true" : "false"}"`);
1183
+ }
1184
+ const sourcePort = portText(plan.sourcePortColumn, e);
1185
+ if (sourcePort !== null) {
1186
+ parts.push(` sourceport="${escapeXmlAttribute(sourcePort)}"`);
1187
+ }
1188
+ const targetPort = portText(plan.targetPortColumn, e);
1189
+ if (targetPort !== null) {
1190
+ parts.push(` targetport="${escapeXmlAttribute(targetPort)}"`);
1191
+ }
1192
+ const open = parts.length;
1193
+ if (plan.weight !== null) {
1194
+ const { keyId, attrType } = plan.weight;
1195
+ const weight = plan.weight.weights.text(e, attrType === "int" || attrType === "long");
1196
+ if (weight !== null) {
1197
+ parts.push(`
1198
+ ${i3}<data key="${keyId}">${escapeXmlText(weight)}</data>`);
1199
+ }
1200
+ }
1201
+ for (const column of plan.edgeColumns) {
1202
+ writeData(column, e, i3, parts);
1203
+ }
1204
+ if (parts.length === open) {
1205
+ parts.push("/>");
1206
+ } else {
1207
+ parts.splice(open, 0, ">");
1208
+ parts.push(`
1209
+ ${i2}</edge>`);
1210
+ }
1211
+ yield parts.join("");
1212
+ }
1213
+ yield `
1214
+ ${i1}</graph>
1215
+ </graphml>
1216
+ `;
1217
+ }
1218
+ function portText(column, e) {
1219
+ if (column === null || !column.isSet(e)) {
1220
+ return null;
1221
+ }
1222
+ const value = column.value(e);
1223
+ return typeof value === "string" ? value : String(value);
1224
+ }
1225
+ function* writeNodeSubtree(root, plan, ids, indent, step, stackNode, stackCursor, written) {
1226
+ const { hierarchy } = plan;
1227
+ const hasChildren = (u) => hierarchy.childStart[u + 1] > hierarchy.childStart[u];
1228
+ const openNode = (u, depth) => {
1229
+ const pad = indent + step.repeat(depth * 2);
1230
+ const parts = [`
1231
+ ${pad}<node id="${escapeXmlAttribute(String(ids.idAt(u)))}"`];
1232
+ const open = parts.length;
1233
+ if (plan.originalIdKey !== null && ids.isChanged(u)) {
1234
+ parts.push(
1235
+ `
1236
+ ${pad}${step}<data key="${plan.originalIdKey}">${escapeXmlText(String(ids.originalAt(u)))}</data>`
1237
+ );
1238
+ }
1239
+ for (const column of plan.nodeColumns) {
1240
+ writeData(column, u, pad + step, parts);
1241
+ }
1242
+ if (hasChildren(u)) {
1243
+ parts.splice(open, 0, ">");
1244
+ parts.push(
1245
+ `
1246
+ ${pad}${step}<graph id="${escapeXmlAttribute(`${String(ids.idAt(u))}:`)}" edgedefault="${plan.edgedefault}">`
1247
+ );
1248
+ return parts.join("");
1249
+ }
1250
+ if (parts.length === open) {
1251
+ parts.push("/>");
1252
+ } else {
1253
+ parts.splice(open, 0, ">");
1254
+ parts.push(`
1255
+ ${pad}</node>`);
1256
+ }
1257
+ return parts.join("");
1258
+ };
1259
+ const closeNode = (depth) => {
1260
+ const pad = indent + step.repeat(depth * 2);
1261
+ return `
1262
+ ${pad}${step}</graph>
1263
+ ${pad}</node>`;
1264
+ };
1265
+ if (written !== null) {
1266
+ written[root] = 1;
1267
+ }
1268
+ yield openNode(root, 0);
1269
+ if (!hasChildren(root)) {
1270
+ return;
1271
+ }
1272
+ stackNode.push(root);
1273
+ stackCursor.push(hierarchy.childStart[root]);
1274
+ while (stackNode.length > 0) {
1275
+ const top = stackNode.length - 1;
1276
+ const u = stackNode[top];
1277
+ const cursor = stackCursor[top];
1278
+ if (cursor >= hierarchy.childStart[u + 1]) {
1279
+ stackNode.pop();
1280
+ stackCursor.pop();
1281
+ yield closeNode(top);
1282
+ continue;
1283
+ }
1284
+ stackCursor[top] = cursor + 1;
1285
+ const v = hierarchy.childList[cursor];
1286
+ if (written !== null && written[v] === 1) {
1287
+ continue;
1288
+ }
1289
+ if (written !== null) {
1290
+ written[v] = 1;
1291
+ }
1292
+ yield openNode(v, top + 1);
1293
+ if (hasChildren(v)) {
1294
+ stackNode.push(v);
1295
+ stackCursor.push(hierarchy.childStart[v]);
1296
+ }
1297
+ }
1298
+ }
1299
+ const graphmlExporter = Object.freeze({
1300
+ format: FORMAT,
1301
+ capabilities: CAPABILITIES,
1302
+ /**
1303
+ * Pre-flight: every loss the export would incur.
1304
+ * @param snapshot - the snapshot
1305
+ * @param options - format-specific and common options
1306
+ * @returns the notes
1307
+ */
1308
+ check(snapshot, options) {
1309
+ const plan = planExport(snapshot, resolveExportOptions(options), resolveFormatOptions(options));
1310
+ return Object.freeze(plan.notes);
1311
+ },
1312
+ /**
1313
+ * Write the document as UTF-8 chunks.
1314
+ * @param snapshot - the snapshot
1315
+ * @param options - format-specific and common options
1316
+ * @returns the chunks
1317
+ */
1318
+ export(snapshot, options) {
1319
+ const plan = planExport(snapshot, resolveExportOptions(options), resolveFormatOptions(options));
1320
+ return encodeChunks(writeGraphml(snapshot, plan));
1321
+ },
1322
+ /**
1323
+ * Write the document as one string.
1324
+ * @param snapshot - the snapshot
1325
+ * @param options - format-specific and common options
1326
+ * @returns the document
1327
+ */
1328
+ exportToString(snapshot, options) {
1329
+ const plan = planExport(snapshot, resolveExportOptions(options), resolveFormatOptions(options));
1330
+ return joinText(writeGraphml(snapshot, plan));
1331
+ }
1332
+ });
1333
+ const FORMAT_DEFAULTS = { ids: "canonical", defaultDirected: false, weightFrom: "weight" };
1334
+ const SNIFF_BYTES = 4096;
1335
+ const YFILES_MODES = /* @__PURE__ */ new Set(["json", "skip"]);
1336
+ const ABORT_CHECK_INTERVAL = 64;
1337
+ const USED_OPTIONS = /* @__PURE__ */ new Set([
1338
+ "ids",
1339
+ "addMissingNodes",
1340
+ "duplicateEdges",
1341
+ "selfLoops",
1342
+ "onMixedDirection",
1343
+ "defaultDirected",
1344
+ "weightFrom",
1345
+ "weightDtype",
1346
+ "long",
1347
+ "restoreMangledIds",
1348
+ "hyperedges",
1349
+ "errorLimit",
1350
+ "signal",
1351
+ "onProgress"
1352
+ ]);
1353
+ class IndexFlags {
1354
+ constructor() {
1355
+ this.words = new Uint32Array(64);
1356
+ }
1357
+ /**
1358
+ * Whether an index is marked.
1359
+ * @param index - the node index
1360
+ * @returns true when set
1361
+ */
1362
+ has(index) {
1363
+ const w = index >>> 5;
1364
+ return w < this.words.length && (this.words[w] >>> (index & 31) & 1) === 1;
1365
+ }
1366
+ /**
1367
+ * Mark an index.
1368
+ * @param index - the node index
1369
+ */
1370
+ add(index) {
1371
+ const w = index >>> 5;
1372
+ if (w >= this.words.length) {
1373
+ let size = this.words.length * 2;
1374
+ while (size <= w) {
1375
+ size *= 2;
1376
+ }
1377
+ const grown = new Uint32Array(size);
1378
+ grown.set(this.words);
1379
+ this.words = grown;
1380
+ }
1381
+ this.words[w] |= 1 << (index & 31);
1382
+ }
1383
+ }
1384
+ class GraphmlReader {
1385
+ /**
1386
+ * Create a reader for one import call.
1387
+ * @param sink - the sink
1388
+ * @param report - the report
1389
+ * @param options - the resolved common options
1390
+ * @param yfilesMode - the resolved yfiles option
1391
+ */
1392
+ constructor(sink, report, options, yfilesMode) {
1393
+ this.ctx = [];
1394
+ this.keys = /* @__PURE__ */ new Map();
1395
+ this.graphs = [];
1396
+ this.nodes = [];
1397
+ this.declared = new IndexFlags();
1398
+ this.edgeIds = /* @__PURE__ */ new Set();
1399
+ this.elementsSinceCheck = 0;
1400
+ this.restored = /* @__PURE__ */ new Map();
1401
+ this.graphColumnNames = /* @__PURE__ */ new Set();
1402
+ this.pendingKey = null;
1403
+ this.edge = null;
1404
+ this.hyperedge = null;
1405
+ this.content = {
1406
+ key: null,
1407
+ pendingKey: null,
1408
+ domain: "node",
1409
+ text: "",
1410
+ tree: null,
1411
+ line: 0
1412
+ };
1413
+ this.descTarget = null;
1414
+ this.descText = "";
1415
+ this.description = null;
1416
+ this.graphSeen = false;
1417
+ this.graphId = null;
1418
+ this.edgedefault = null;
1419
+ this.namespaces = {};
1420
+ this.weightOrigin = null;
1421
+ this.yfilesLossNoted = false;
1422
+ this.hyperedgeCount = 0;
1423
+ this.edgeIdHandle = INVALID_INDEX;
1424
+ this.sourcePortHandle = INVALID_INDEX;
1425
+ this.targetPortHandle = INVALID_INDEX;
1426
+ this.parentHandle = INVALID_INDEX;
1427
+ this.hubHandle = INVALID_INDEX;
1428
+ this.sink = sink;
1429
+ this.report = report;
1430
+ this.options = options;
1431
+ this.yfilesMode = yfilesMode;
1432
+ this.coercer = new IdCoercer(options.ids);
1433
+ this.direction = new DirectionResolver(sink, report, options.onMixedDirection);
1434
+ }
1435
+ // ------------------------------------------------------------------ tokenizer events
1436
+ /**
1437
+ * An element starts.
1438
+ * @param name - the element name
1439
+ * @param attrs - its attributes
1440
+ * @param line - the line
1441
+ */
1442
+ start(name, attrs, line) {
1443
+ const parent = this.ctx.length === 0 ? null : this.ctx[this.ctx.length - 1];
1444
+ switch (parent) {
1445
+ case 9:
1446
+ this.captureStart(name, attrs);
1447
+ return;
1448
+ case 10:
1449
+ case 1:
1450
+ this.ctx.push(
1451
+ 10
1452
+ /* Skip */
1453
+ );
1454
+ return;
1455
+ case 8:
1456
+ case 3:
1457
+ this.captureStart(name, attrs);
1458
+ return;
1459
+ }
1460
+ const local = localName(name);
1461
+ switch (parent) {
1462
+ case null:
1463
+ if (local !== "graphml") {
1464
+ this.report.fail(GRAPHML_ISSUE.NOT_GRAPHML, `the root element is <${name}>, not <graphml>`, {
1465
+ line
1466
+ });
1467
+ }
1468
+ this.readRoot(attrs);
1469
+ this.ctx.push(
1470
+ 0
1471
+ /* Graphml */
1472
+ );
1473
+ return;
1474
+ case 0:
1475
+ this.startInGraphml(name, local, attrs, line);
1476
+ return;
1477
+ case 2:
1478
+ this.startInKey(name, local, line);
1479
+ return;
1480
+ case 4:
1481
+ this.startInGraph(name, local, attrs, line);
1482
+ return;
1483
+ case 5:
1484
+ this.startInNode(name, local, attrs, line);
1485
+ return;
1486
+ case 6:
1487
+ this.startInEdge(name, local, attrs, line);
1488
+ return;
1489
+ case 7:
1490
+ this.startInHyperedge(name, local, attrs, line);
1491
+ return;
1492
+ default:
1493
+ this.unknownElement(name, line);
1494
+ }
1495
+ }
1496
+ /**
1497
+ * An element ends.
1498
+ * @param name - the element name
1499
+ * @param _line - the line (unused: issues of an element cite its start line)
1500
+ */
1501
+ end(name, _line) {
1502
+ const ctx = this.ctx.pop();
1503
+ switch (ctx) {
1504
+ case 9:
1505
+ this.captureEnd();
1506
+ return;
1507
+ case 10:
1508
+ case 0:
1509
+ case void 0:
1510
+ return;
1511
+ case 1:
1512
+ this.finishDesc();
1513
+ return;
1514
+ case 2:
1515
+ this.finishKey();
1516
+ return;
1517
+ case 3:
1518
+ this.finishDefault();
1519
+ return;
1520
+ case 8:
1521
+ this.finishData();
1522
+ return;
1523
+ case 5:
1524
+ this.finishNode();
1525
+ return;
1526
+ case 6:
1527
+ this.finishEdge();
1528
+ return;
1529
+ case 7:
1530
+ this.finishHyperedge();
1531
+ return;
1532
+ case 4:
1533
+ this.graphs.pop();
1534
+ return;
1535
+ default: {
1536
+ const value = ctx;
1537
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown reader state ${String(value)}`, { name });
1538
+ }
1539
+ }
1540
+ }
1541
+ /**
1542
+ * Character data.
1543
+ * @param text - the text
1544
+ * @param line - the line
1545
+ */
1546
+ text(text, line) {
1547
+ const ctx = this.ctx.length === 0 ? null : this.ctx[this.ctx.length - 1];
1548
+ switch (ctx) {
1549
+ case 9:
1550
+ this.content.tree?.text(text);
1551
+ return;
1552
+ case 8:
1553
+ case 3:
1554
+ if (this.content.tree === null) {
1555
+ this.content.text += text;
1556
+ } else {
1557
+ this.content.tree.text(text);
1558
+ }
1559
+ return;
1560
+ case 1:
1561
+ this.descText += text;
1562
+ return;
1563
+ case 10:
1564
+ return;
1565
+ default:
1566
+ if (!isWhitespace(text)) {
1567
+ this.report.warnOnce(
1568
+ "validation-error",
1569
+ GRAPHML_ISSUE.STRAY_TEXT,
1570
+ "text where GraphML allows only elements was ignored",
1571
+ { line }
1572
+ );
1573
+ }
1574
+ }
1575
+ }
1576
+ /**
1577
+ * After the last event: the document must have held a graph; the metadata is written.
1578
+ */
1579
+ finish() {
1580
+ if (!this.graphSeen) {
1581
+ this.report.fail(GRAPHML_ISSUE.NO_GRAPH, "the document has no <graph> element");
1582
+ }
1583
+ const meta = { graphId: this.graphId, edgedefault: this.edgedefault, namespaces: this.namespaces };
1584
+ this.sink.setMeta({
1585
+ sourceFormat: FORMAT,
1586
+ description: this.description ?? void 0,
1587
+ weightOrigin: this.weightOrigin === null ? void 0 : {
1588
+ format: FORMAT,
1589
+ id: this.weightOrigin.id,
1590
+ title: this.weightOrigin.title,
1591
+ type: this.weightOrigin.type,
1592
+ namespace: null
1593
+ },
1594
+ extra: { [META_KEY]: meta }
1595
+ });
1596
+ }
1597
+ // ------------------------------------------------------------------ structure
1598
+ /**
1599
+ * Record the root element's namespace declarations.
1600
+ * @param attrs - the root attributes
1601
+ */
1602
+ readRoot(attrs) {
1603
+ for (const [key, value] of attrs) {
1604
+ if (key.startsWith("xmlns:") && value !== GRAPHML_NAMESPACE && value !== XSI_NAMESPACE) {
1605
+ this.namespaces[key.slice(6)] = value;
1606
+ }
1607
+ }
1608
+ }
1609
+ /**
1610
+ * A child of `<graphml>`.
1611
+ * @param name - the element name
1612
+ * @param local - its local name
1613
+ * @param attrs - its attributes
1614
+ * @param line - the line
1615
+ */
1616
+ startInGraphml(name, local, attrs, line) {
1617
+ switch (local) {
1618
+ case "desc":
1619
+ this.beginDesc("graph");
1620
+ return;
1621
+ case "key":
1622
+ this.beginKey(attrs, line);
1623
+ return;
1624
+ case "graph":
1625
+ this.beginGraph(attrs, line, INVALID_INDEX);
1626
+ return;
1627
+ case "data":
1628
+ this.beginData(attrs, line, "graph");
1629
+ return;
1630
+ default:
1631
+ this.unknownElement(name, line);
1632
+ }
1633
+ }
1634
+ /**
1635
+ * A child of `<key>`.
1636
+ * @param name - the element name
1637
+ * @param local - its local name
1638
+ * @param line - the line
1639
+ */
1640
+ startInKey(name, local, line) {
1641
+ switch (local) {
1642
+ case "desc":
1643
+ this.beginDesc("key");
1644
+ return;
1645
+ case "default":
1646
+ this.content.key = null;
1647
+ this.content.pendingKey = this.pendingKey;
1648
+ this.content.text = "";
1649
+ this.content.tree = null;
1650
+ this.content.line = line;
1651
+ this.ctx.push(
1652
+ 3
1653
+ /* Default */
1654
+ );
1655
+ return;
1656
+ default:
1657
+ this.unknownElement(name, line);
1658
+ }
1659
+ }
1660
+ /**
1661
+ * A child of `<graph>`.
1662
+ * @param name - the element name
1663
+ * @param local - its local name
1664
+ * @param attrs - its attributes
1665
+ * @param line - the line
1666
+ */
1667
+ startInGraph(name, local, attrs, line) {
1668
+ switch (local) {
1669
+ case "desc":
1670
+ if (this.graphs.length === 1) {
1671
+ this.beginDesc("graph");
1672
+ } else {
1673
+ this.ctx.push(
1674
+ 10
1675
+ /* Skip */
1676
+ );
1677
+ }
1678
+ return;
1679
+ case "data":
1680
+ if (this.graphs.length === 1) {
1681
+ this.beginData(attrs, line, "graph");
1682
+ } else {
1683
+ this.report.warnOnce(
1684
+ "unsupported",
1685
+ GRAPHML_ISSUE.NESTED_GRAPH_DATA,
1686
+ "data of a nested <graph> cannot be kept; only the top-level graph has attributes",
1687
+ { line }
1688
+ );
1689
+ this.ctx.push(
1690
+ 10
1691
+ /* Skip */
1692
+ );
1693
+ }
1694
+ return;
1695
+ case "node":
1696
+ this.beginNode(attrs, line);
1697
+ return;
1698
+ case "edge":
1699
+ this.beginEdge(attrs, line);
1700
+ return;
1701
+ case "hyperedge":
1702
+ this.beginHyperedge(attrs, line);
1703
+ return;
1704
+ case "locator":
1705
+ this.report.warnOnce(
1706
+ "unsupported",
1707
+ GRAPHML_ISSUE.LOCATOR_DROPPED,
1708
+ "<locator> references are not followed",
1709
+ { line }
1710
+ );
1711
+ this.ctx.push(
1712
+ 10
1713
+ /* Skip */
1714
+ );
1715
+ return;
1716
+ default:
1717
+ this.unknownElement(name, line);
1718
+ }
1719
+ }
1720
+ /**
1721
+ * A child of `<node>`.
1722
+ * @param name - the element name
1723
+ * @param local - its local name
1724
+ * @param attrs - its attributes
1725
+ * @param line - the line
1726
+ */
1727
+ startInNode(name, local, attrs, line) {
1728
+ switch (local) {
1729
+ case "desc":
1730
+ this.report.warnOnce(
1731
+ "unsupported",
1732
+ GRAPHML_ISSUE.DESC_DROPPED,
1733
+ "node and edge <desc> texts are not kept",
1734
+ {
1735
+ line
1736
+ }
1737
+ );
1738
+ this.ctx.push(
1739
+ 10
1740
+ /* Skip */
1741
+ );
1742
+ return;
1743
+ case "data":
1744
+ this.beginData(attrs, line, "node");
1745
+ return;
1746
+ case "port":
1747
+ this.report.warnOnce(
1748
+ "unsupported",
1749
+ GRAPHML_ISSUE.PORT_DECLARATION,
1750
+ "<port> declarations are not kept; sourceport / targetport edge attributes are",
1751
+ { line }
1752
+ );
1753
+ this.ctx.push(
1754
+ 10
1755
+ /* Skip */
1756
+ );
1757
+ return;
1758
+ case "graph": {
1759
+ const node = this.nodes[this.nodes.length - 1];
1760
+ this.materializeNode(node);
1761
+ this.beginGraph(attrs, line, node.index);
1762
+ return;
1763
+ }
1764
+ case "locator":
1765
+ this.report.warnOnce(
1766
+ "unsupported",
1767
+ GRAPHML_ISSUE.LOCATOR_DROPPED,
1768
+ "<locator> references are not followed",
1769
+ { line }
1770
+ );
1771
+ this.ctx.push(
1772
+ 10
1773
+ /* Skip */
1774
+ );
1775
+ return;
1776
+ default:
1777
+ this.unknownElement(name, line);
1778
+ }
1779
+ }
1780
+ /**
1781
+ * A child of `<edge>`.
1782
+ * @param name - the element name
1783
+ * @param local - its local name
1784
+ * @param attrs - its attributes
1785
+ * @param line - the line
1786
+ */
1787
+ startInEdge(name, local, attrs, line) {
1788
+ switch (local) {
1789
+ case "desc":
1790
+ this.report.warnOnce(
1791
+ "unsupported",
1792
+ GRAPHML_ISSUE.DESC_DROPPED,
1793
+ "node and edge <desc> texts are not kept",
1794
+ {
1795
+ line
1796
+ }
1797
+ );
1798
+ this.ctx.push(
1799
+ 10
1800
+ /* Skip */
1801
+ );
1802
+ return;
1803
+ case "data":
1804
+ this.beginData(attrs, line, "edge");
1805
+ return;
1806
+ default:
1807
+ this.unknownElement(name, line);
1808
+ }
1809
+ }
1810
+ /**
1811
+ * A child of `<hyperedge>` (under the star / clique policies).
1812
+ * @param name - the element name
1813
+ * @param local - its local name
1814
+ * @param attrs - its attributes
1815
+ * @param line - the line
1816
+ */
1817
+ startInHyperedge(name, local, attrs, line) {
1818
+ switch (local) {
1819
+ case "endpoint":
1820
+ this.readEndpoint(attrs, line);
1821
+ this.ctx.push(
1822
+ 10
1823
+ /* Skip */
1824
+ );
1825
+ return;
1826
+ case "data":
1827
+ this.report.warnOnce(
1828
+ "unsupported",
1829
+ GRAPHML_ISSUE.HYPEREDGE_DATA_DROPPED,
1830
+ "hyperedge data are not kept by the star / clique expansions",
1831
+ { line }
1832
+ );
1833
+ this.ctx.push(
1834
+ 10
1835
+ /* Skip */
1836
+ );
1837
+ return;
1838
+ case "desc":
1839
+ this.ctx.push(
1840
+ 10
1841
+ /* Skip */
1842
+ );
1843
+ return;
1844
+ default:
1845
+ this.unknownElement(name, line);
1846
+ }
1847
+ }
1848
+ /**
1849
+ * Report an element the schema does not define at this place and skip its subtree.
1850
+ * @param name - the element name
1851
+ * @param line - the line
1852
+ */
1853
+ unknownElement(name, line) {
1854
+ this.report.warnOnce(
1855
+ "unsupported",
1856
+ GRAPHML_ISSUE.UNKNOWN_ELEMENT,
1857
+ `element <${name}> is not GraphML and was skipped`,
1858
+ {
1859
+ line,
1860
+ element: name
1861
+ }
1862
+ );
1863
+ this.ctx.push(
1864
+ 10
1865
+ /* Skip */
1866
+ );
1867
+ }
1868
+ /**
1869
+ * Open a `<desc>`.
1870
+ * @param target - what the text describes
1871
+ */
1872
+ beginDesc(target) {
1873
+ this.descTarget = target;
1874
+ this.descText = "";
1875
+ this.ctx.push(
1876
+ 1
1877
+ /* Desc */
1878
+ );
1879
+ }
1880
+ /** Close a `<desc>`. */
1881
+ finishDesc() {
1882
+ const text = this.descText.trim();
1883
+ if (this.descTarget === "key" && this.pendingKey !== null) {
1884
+ this.pendingKey.desc = text;
1885
+ } else if (this.descTarget === "graph" && text.length > 0) {
1886
+ this.description = text;
1887
+ }
1888
+ this.descTarget = null;
1889
+ this.descText = "";
1890
+ }
1891
+ // ------------------------------------------------------------------ graphs
1892
+ /**
1893
+ * Open a `<graph>`: the top-level one sets the sink's direction (rule 1 of design section
1894
+ * 8.4); a nested one records its container as the parent of its nodes.
1895
+ * @param attrs - the graph attributes
1896
+ * @param line - the line
1897
+ * @param parent - the containing node's index, or INVALID_INDEX
1898
+ */
1899
+ beginGraph(attrs, line, parent) {
1900
+ const top = this.graphs.length === 0;
1901
+ const edgedefault = attrs.get("edgedefault");
1902
+ let directed;
1903
+ if (edgedefault === "directed" || edgedefault === "undirected") {
1904
+ directed = edgedefault === "directed";
1905
+ } else if (edgedefault === void 0) {
1906
+ directed = top ? this.options.defaultDirected : this.graphs[this.graphs.length - 1].directed;
1907
+ this.report.warning(
1908
+ "validation-error",
1909
+ GRAPHML_ISSUE.EDGEDEFAULT_MISSING,
1910
+ `<graph> has no edgedefault; read as ${directed ? "directed" : "undirected"}`,
1911
+ { line }
1912
+ );
1913
+ } else {
1914
+ directed = top ? this.options.defaultDirected : this.graphs[this.graphs.length - 1].directed;
1915
+ this.report.error(
1916
+ "validation-error",
1917
+ GRAPHML_ISSUE.INVALID_EDGEDEFAULT,
1918
+ `edgedefault "${edgedefault}" is neither directed nor undirected; read as ${directed ? "directed" : "undirected"}`,
1919
+ { line }
1920
+ );
1921
+ }
1922
+ if (top && this.graphSeen) {
1923
+ this.report.warnOnce(
1924
+ "unsupported",
1925
+ GRAPHML_ISSUE.MULTIPLE_GRAPHS,
1926
+ "the document holds more than one top-level <graph>; their nodes and edges are merged into one graph",
1927
+ { line, element: attrs.get("id") ?? null }
1928
+ );
1929
+ } else if (top) {
1930
+ this.graphSeen = true;
1931
+ this.graphId = attrs.get("id") ?? null;
1932
+ this.edgedefault = directed ? "directed" : "undirected";
1933
+ this.direction.setHeader(directed, { line });
1934
+ const nodes = this.hint(attrs.get("parse.nodes"), "parse.nodes", line);
1935
+ const edges = this.hint(attrs.get("parse.edges"), "parse.edges", line);
1936
+ if (nodes !== null || edges !== null) {
1937
+ try {
1938
+ this.sink.reserve(nodes ?? void 0, edges ?? void 0);
1939
+ } catch (err) {
1940
+ if (!(err instanceof GraphFormatError)) {
1941
+ throw err;
1942
+ }
1943
+ this.report.warning(
1944
+ "validation-error",
1945
+ GRAPHML_ISSUE.COUNT_HINT,
1946
+ `count hint refused: ${err.message}`,
1947
+ {
1948
+ line
1949
+ }
1950
+ );
1951
+ }
1952
+ }
1953
+ }
1954
+ this.graphs.push({ directed, parent });
1955
+ this.ctx.push(
1956
+ 4
1957
+ /* Graph */
1958
+ );
1959
+ }
1960
+ /**
1961
+ * A `parse.nodes` / `parse.edges` hint the sink can reserve; anything else is reported and ignored.
1962
+ * @param text - the attribute text, or undefined
1963
+ * @param name - the attribute name
1964
+ * @param line - the line
1965
+ * @returns the count, or null
1966
+ */
1967
+ hint(text, name, line) {
1968
+ if (text === void 0) {
1969
+ return null;
1970
+ }
1971
+ const n = hint(text);
1972
+ if (n === null || n > MAX_COUNT) {
1973
+ this.report.warning(
1974
+ "validation-error",
1975
+ GRAPHML_ISSUE.COUNT_HINT,
1976
+ `<graph ${name}="${text}"> is not a count the sink can reserve; ignored`,
1977
+ { line, element: name }
1978
+ );
1979
+ return null;
1980
+ }
1981
+ return n;
1982
+ }
1983
+ // ------------------------------------------------------------------ keys
1984
+ /**
1985
+ * Open a `<key>`; the column is declared at `</key>` once its default is known.
1986
+ * @param attrs - the key attributes
1987
+ * @param line - the line
1988
+ */
1989
+ beginKey(attrs, line) {
1990
+ const id = attrs.get("id") ?? null;
1991
+ const forText = attrs.get("for") ?? "all";
1992
+ let domains;
1993
+ switch (forText) {
1994
+ case "all":
1995
+ domains = ["graph", "node", "edge"];
1996
+ break;
1997
+ case "graph":
1998
+ case "node":
1999
+ case "edge":
2000
+ domains = [forText];
2001
+ break;
2002
+ case "hyperedge":
2003
+ case "port":
2004
+ case "endpoint":
2005
+ this.report.warning(
2006
+ "unsupported",
2007
+ GRAPHML_ISSUE.KEY_DOMAIN_UNSUPPORTED,
2008
+ `key "${id ?? ""}" is declared for ${forText} elements, which are not kept`,
2009
+ { line, element: id }
2010
+ );
2011
+ domains = [];
2012
+ break;
2013
+ default:
2014
+ this.report.error(
2015
+ "validation-error",
2016
+ GRAPHML_ISSUE.KEY_FOR_INVALID,
2017
+ `key "${id ?? ""}" has an unknown for="${forText}"`,
2018
+ { line, element: id }
2019
+ );
2020
+ domains = [];
2021
+ }
2022
+ this.pendingKey = {
2023
+ id,
2024
+ attrName: attrs.get("attr.name") ?? null,
2025
+ attrType: attrs.get("attr.type") ?? null,
2026
+ yfilesType: attrs.get("yfiles.type") ?? null,
2027
+ domains,
2028
+ line,
2029
+ desc: "",
2030
+ defaultText: null,
2031
+ defaultTree: void 0
2032
+ };
2033
+ this.ctx.push(
2034
+ 2
2035
+ /* Key */
2036
+ );
2037
+ }
2038
+ /** Close a `<key>`: declare its columns. */
2039
+ finishKey() {
2040
+ const key = this.pendingKey;
2041
+ this.pendingKey = null;
2042
+ if (key === null) {
2043
+ return;
2044
+ }
2045
+ const where = { line: key.line, element: key.id };
2046
+ if (key.id === null || key.id.length === 0) {
2047
+ this.report.error("validation-error", GRAPHML_ISSUE.KEY_MISSING_ID, "<key> without an id", where);
2048
+ return;
2049
+ }
2050
+ if (this.keys.has(key.id)) {
2051
+ this.report.error(
2052
+ "validation-error",
2053
+ GRAPHML_ISSUE.DUPLICATE_KEY,
2054
+ `key "${key.id}" is declared twice`,
2055
+ where
2056
+ );
2057
+ return;
2058
+ }
2059
+ const yfiles = key.yfilesType !== null;
2060
+ const name = key.attrName !== null && key.attrName.length > 0 ? key.attrName : key.id;
2061
+ const skipped = yfiles && this.yfilesMode === "skip";
2062
+ if (skipped) {
2063
+ this.report.warning(
2064
+ "unsupported",
2065
+ GRAPHML_ISSUE.YFILES_SKIPPED,
2066
+ `yFiles key "${key.id}" (${key.yfilesType ?? ""}) skipped per the yfiles option`,
2067
+ where
2068
+ );
2069
+ }
2070
+ let weight = false;
2071
+ let originalId = false;
2072
+ let node = null;
2073
+ let edge = null;
2074
+ let graph = null;
2075
+ for (const domain of key.domains) {
2076
+ if (skipped) {
2077
+ continue;
2078
+ }
2079
+ if (domain === "edge" && !yfiles && isWeightField(name, this.options.weightFrom)) {
2080
+ weight = true;
2081
+ this.weightOrigin ??= { id: key.id, title: key.attrName, type: key.attrType };
2082
+ continue;
2083
+ }
2084
+ if (domain === "node" && !yfiles && name === ORIGINAL_ID_ATTRIBUTE) {
2085
+ if (this.options.restoreMangledIds) {
2086
+ originalId = true;
2087
+ continue;
2088
+ }
2089
+ node = this.declareOriginalIdColumn(key);
2090
+ continue;
2091
+ }
2092
+ try {
2093
+ const target = yfiles ? this.declareYfiles(key, name, domain) : this.declareTyped(key, name, domain);
2094
+ if (domain === "node") {
2095
+ node = target;
2096
+ } else if (domain === "edge") {
2097
+ edge = target;
2098
+ } else {
2099
+ graph = target;
2100
+ }
2101
+ } catch (err) {
2102
+ this.report.recordError(err, where);
2103
+ }
2104
+ }
2105
+ this.keys.set(key.id, {
2106
+ id: key.id,
2107
+ name,
2108
+ node,
2109
+ edge,
2110
+ graph,
2111
+ weight,
2112
+ originalId,
2113
+ yfiles,
2114
+ skipped,
2115
+ domains: key.domains
2116
+ });
2117
+ }
2118
+ /**
2119
+ * Declare the column of a typed (or untyped, then string) key in one domain.
2120
+ * @param key - the key
2121
+ * @param name - the resolved attribute name
2122
+ * @param domain - the domain
2123
+ * @returns the column target
2124
+ */
2125
+ declareTyped(key, name, domain) {
2126
+ const id = key.id ?? "";
2127
+ const declared = declareAttribute({
2128
+ format: FORMAT,
2129
+ id,
2130
+ title: key.attrName !== null && key.attrName.length > 0 ? key.attrName : null,
2131
+ type: key.attrType,
2132
+ defaultText: key.defaultTree === void 0 ? key.defaultText : null,
2133
+ listSyntax: "comma",
2134
+ role: domain !== "graph" && name === LABEL_COLUMN ? "label" : null,
2135
+ long: this.options.long,
2136
+ taken: this.takenIn(domain)
2137
+ });
2138
+ for (const issue of declared.issues) {
2139
+ this.report.warning(issue.category, issue.code, issue.message, { line: key.line, element: id });
2140
+ }
2141
+ if (key.defaultTree !== void 0) {
2142
+ this.report.warning(
2143
+ "validation-error",
2144
+ GRAPHML_ISSUE.DATA_NESTED,
2145
+ `the <default> of key "${id}" holds elements; a ${key.attrType ?? "string"} default must be text`,
2146
+ { line: key.line, element: id }
2147
+ );
2148
+ }
2149
+ const { decl } = declared;
2150
+ if (key.desc.length > 0) {
2151
+ decl.extra = { desc: key.desc };
2152
+ }
2153
+ return this.declareTarget(domain, decl, declared.spec, declared.listSyntax);
2154
+ }
2155
+ /**
2156
+ * Declare the json column of a yFiles key in one domain.
2157
+ * @param key - the key
2158
+ * @param name - the resolved attribute name
2159
+ * @param domain - the domain
2160
+ * @returns the column target
2161
+ */
2162
+ declareYfiles(key, name, domain) {
2163
+ const id = key.id ?? "";
2164
+ const columnName = uniqueColumnName(name, id, this.takenIn(domain));
2165
+ const decl = {
2166
+ name: columnName,
2167
+ dtype: "json",
2168
+ nullable: true,
2169
+ origin: {
2170
+ format: FORMAT,
2171
+ id,
2172
+ title: key.attrName !== null && key.attrName !== columnName ? key.attrName : null,
2173
+ type: key.yfilesType,
2174
+ namespace: "yfiles"
2175
+ }
2176
+ };
2177
+ if (key.defaultTree !== void 0) {
2178
+ decl.default = key.defaultTree;
2179
+ } else if (key.defaultText !== null) {
2180
+ decl.default = key.defaultText;
2181
+ }
2182
+ if (key.desc.length > 0) {
2183
+ decl.extra = { desc: key.desc };
2184
+ }
2185
+ return this.declareTarget(domain, decl, null, "comma");
2186
+ }
2187
+ /**
2188
+ * Declare the `graphty.originalId` column that keeps the exporter's original ids when
2189
+ * restoreMangledIds is off.
2190
+ * @param key - the key
2191
+ * @returns the column target
2192
+ */
2193
+ declareOriginalIdColumn(key) {
2194
+ const decl = {
2195
+ name: uniqueColumnName(ORIGINAL_ID_COLUMN, key.id, this.takenIn("node")),
2196
+ dtype: "string",
2197
+ nullable: true,
2198
+ role: "originalId",
2199
+ origin: { format: FORMAT, id: key.id, title: key.attrName, type: key.attrType, namespace: null }
2200
+ };
2201
+ return this.declareTarget("node", decl, null, "comma");
2202
+ }
2203
+ /**
2204
+ * Push a declaration into the sink for one domain through the shared design section 5.6 rule:
2205
+ * a role another column already holds is dropped (reported), a name the sink holds with
2206
+ * another shape is renamed `<name>#<id>` (reported).
2207
+ * @param domain - the domain
2208
+ * @param decl - the declaration
2209
+ * @param spec - the value spec, or null for json
2210
+ * @param listSyntax - the list syntax
2211
+ * @returns the target
2212
+ */
2213
+ declareTarget(domain, decl, spec, listSyntax) {
2214
+ if (domain === "graph") {
2215
+ this.sink.setGraphValue(decl.name, void 0, decl);
2216
+ this.graphColumnNames.add(decl.name);
2217
+ return { name: decl.name, handle: INVALID_INDEX, decl, spec, listSyntax };
2218
+ }
2219
+ const resolved = declareResolved(this.sink, domain, decl, this.report, { element: decl.name });
2220
+ return { name: resolved.decl.name, handle: resolved.handle, decl: resolved.decl, spec, listSyntax };
2221
+ }
2222
+ /**
2223
+ * The `taken` predicate of a domain: the reserved XML-derived names plus the sink's columns.
2224
+ * @param domain - the domain
2225
+ * @returns the predicate
2226
+ */
2227
+ takenIn(domain) {
2228
+ if (domain === "graph") {
2229
+ return (name) => this.graphColumnNames.has(name);
2230
+ }
2231
+ const reserved = domain === "node" ? RESERVED_NODE_NAMES : RESERVED_EDGE_NAMES;
2232
+ const inSink = takenIn(this.sink, domain);
2233
+ return (name) => reserved.has(name) || inSink(name);
2234
+ }
2235
+ // ------------------------------------------------------------------ data and defaults
2236
+ /**
2237
+ * Open a `<data>`.
2238
+ * @param attrs - its attributes
2239
+ * @param line - the line
2240
+ * @param domain - the domain of the element holding it
2241
+ */
2242
+ beginData(attrs, line, domain) {
2243
+ const keyId = attrs.get("key");
2244
+ const { content } = this;
2245
+ content.key = null;
2246
+ content.pendingKey = null;
2247
+ content.domain = domain;
2248
+ content.text = "";
2249
+ content.tree = null;
2250
+ content.line = line;
2251
+ if (keyId === void 0) {
2252
+ this.report.error("validation-error", GRAPHML_ISSUE.DATA_MISSING_KEY, "<data> without a key attribute", {
2253
+ line
2254
+ });
2255
+ } else {
2256
+ const key = this.keys.get(keyId);
2257
+ if (key === void 0) {
2258
+ this.report.error(
2259
+ "validation-error",
2260
+ GRAPHML_ISSUE.UNKNOWN_KEY,
2261
+ `<data> references undeclared key "${keyId}"`,
2262
+ { line, element: keyId }
2263
+ );
2264
+ } else if (!key.skipped && !key.domains.includes(domain)) {
2265
+ this.report.warnOnce(
2266
+ "validation-error",
2267
+ GRAPHML_ISSUE.KEY_DOMAIN,
2268
+ `key "${keyId}" is declared for ${key.domains.join(" / ")} elements but used on a ${domain}; ignored`,
2269
+ { line, element: keyId }
2270
+ );
2271
+ } else {
2272
+ content.key = key;
2273
+ }
2274
+ }
2275
+ this.ctx.push(
2276
+ 8
2277
+ /* Data */
2278
+ );
2279
+ }
2280
+ /**
2281
+ * A child element inside `<data>` / `<default>`: the content becomes a tree.
2282
+ * @param name - the element name
2283
+ * @param attrs - its attributes
2284
+ */
2285
+ captureStart(name, attrs) {
2286
+ const { content } = this;
2287
+ if (content.tree === null) {
2288
+ content.tree = new XmlTreeBuilder();
2289
+ if (content.text.length > 0) {
2290
+ content.tree.text(content.text);
2291
+ content.text = "";
2292
+ }
2293
+ }
2294
+ content.tree.start(name, attrs);
2295
+ this.ctx.push(
2296
+ 9
2297
+ /* Capture */
2298
+ );
2299
+ }
2300
+ /** A captured element ends. */
2301
+ captureEnd() {
2302
+ this.content.tree?.end();
2303
+ }
2304
+ /** Close a `<default>`: keep its text or tree for the key. */
2305
+ finishDefault() {
2306
+ const { content } = this;
2307
+ const key = content.pendingKey;
2308
+ if (key === null) {
2309
+ return;
2310
+ }
2311
+ if (content.tree !== null) {
2312
+ key.defaultTree = content.tree.finish();
2313
+ } else {
2314
+ key.defaultText = content.text;
2315
+ }
2316
+ content.tree = null;
2317
+ content.text = "";
2318
+ content.pendingKey = null;
2319
+ }
2320
+ /** Close a `<data>`: parse the value and route it to its element. */
2321
+ finishData() {
2322
+ const { content } = this;
2323
+ const { key } = content;
2324
+ const value = content.tree === null ? content.text : content.tree.finish();
2325
+ content.tree = null;
2326
+ content.text = "";
2327
+ content.key = null;
2328
+ if (key === null || key.skipped) {
2329
+ return;
2330
+ }
2331
+ const where = { line: content.line, element: key.id };
2332
+ switch (content.domain) {
2333
+ case "node": {
2334
+ const node = this.nodes[this.nodes.length - 1];
2335
+ if (key.originalId) {
2336
+ if (typeof value === "string") {
2337
+ node.restoredId = value;
2338
+ }
2339
+ return;
2340
+ }
2341
+ if (key.node === null) {
2342
+ return;
2343
+ }
2344
+ const parsed = this.parseValue(key.node, value, where);
2345
+ if (parsed === void 0) {
2346
+ return;
2347
+ }
2348
+ if (node.index === INVALID_INDEX) {
2349
+ node.pending.push(key.node.handle, parsed);
2350
+ } else if (!node.failed) {
2351
+ this.sink.setNodeValue(key.node.handle, node.index, parsed);
2352
+ }
2353
+ return;
2354
+ }
2355
+ case "edge": {
2356
+ const { edge } = this;
2357
+ if (edge === null) {
2358
+ return;
2359
+ }
2360
+ if (key.weight) {
2361
+ if (typeof value !== "string") {
2362
+ this.report.error(
2363
+ "validation-error",
2364
+ GRAPHML_ISSUE.DATA_NESTED,
2365
+ "a weight value must be text",
2366
+ where
2367
+ );
2368
+ return;
2369
+ }
2370
+ try {
2371
+ edge.weight = parseWeightText(value);
2372
+ } catch (err) {
2373
+ this.skipEdge(edge);
2374
+ this.report.recordError(err, where);
2375
+ }
2376
+ return;
2377
+ }
2378
+ if (key.edge === null) {
2379
+ return;
2380
+ }
2381
+ const parsed = this.parseValue(key.edge, value, where);
2382
+ if (parsed !== void 0) {
2383
+ edge.pending.push(key.edge.handle, parsed);
2384
+ }
2385
+ return;
2386
+ }
2387
+ case "graph": {
2388
+ if (key.graph === null) {
2389
+ return;
2390
+ }
2391
+ const parsed = this.parseValue(key.graph, value, where);
2392
+ if (parsed !== void 0) {
2393
+ try {
2394
+ this.sink.setGraphValue(key.graph.name, parsed, key.graph.decl);
2395
+ } catch (err) {
2396
+ this.report.recordError(err, where);
2397
+ }
2398
+ }
2399
+ return;
2400
+ }
2401
+ default: {
2402
+ const { domain } = content;
2403
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown data domain ${String(domain)}`, {});
2404
+ }
2405
+ }
2406
+ }
2407
+ /**
2408
+ * Parse the content of a `<data>` by its column's declaration.
2409
+ * @param target - the column
2410
+ * @param value - the text, or the tree of a nested subtree
2411
+ * @param where - the location for issues
2412
+ * @returns the value to write, or undefined when there is none (blank, or an error was recorded)
2413
+ */
2414
+ parseValue(target, value, where) {
2415
+ if (target.spec === null) {
2416
+ if (!this.yfilesLossNoted) {
2417
+ this.yfilesLossNoted = true;
2418
+ this.report.loss(
2419
+ GRAPHML_LOSS.YFILES_JSON,
2420
+ "yFiles nested XML is kept as a JSON tree: structure preserved, not byte-exact",
2421
+ target.name
2422
+ );
2423
+ }
2424
+ return value;
2425
+ }
2426
+ if (typeof value !== "string") {
2427
+ this.report.error(
2428
+ "validation-error",
2429
+ GRAPHML_ISSUE.DATA_NESTED,
2430
+ `<data> of the ${target.spec.declared} attribute "${target.name}" holds elements, not text`,
2431
+ where
2432
+ );
2433
+ return void 0;
2434
+ }
2435
+ const { spec } = target;
2436
+ if (spec.kind !== "string" && isWhitespace(value)) {
2437
+ return void 0;
2438
+ }
2439
+ try {
2440
+ const parsed = parseDeclaredValue(value, spec, target.listSyntax);
2441
+ if (spec.precision && losesPrecision(spec, value)) {
2442
+ this.report.warnOnce(
2443
+ "precision",
2444
+ PRECISION_CODE,
2445
+ `long values of "${target.name}" beyond 2^53 are stored as the nearest double`,
2446
+ { line: where.line, element: target.name }
2447
+ );
2448
+ }
2449
+ return parsed;
2450
+ } catch (err) {
2451
+ this.report.recordError(err, where);
2452
+ return void 0;
2453
+ }
2454
+ }
2455
+ // ------------------------------------------------------------------ nodes
2456
+ /**
2457
+ * Open a `<node>`; it is added to the sink when its data is complete (or a nested graph
2458
+ * starts), so a `graphty:originalId` value can replace its id.
2459
+ * @param attrs - the node attributes
2460
+ * @param line - the line
2461
+ */
2462
+ beginNode(attrs, line) {
2463
+ const graph = this.graphs[this.graphs.length - 1];
2464
+ const state = {
2465
+ id: null,
2466
+ idText: null,
2467
+ index: INVALID_INDEX,
2468
+ parent: graph.parent,
2469
+ pending: [],
2470
+ restoredId: null,
2471
+ failed: false,
2472
+ line
2473
+ };
2474
+ const idText = attrs.get("id");
2475
+ if (idText === void 0 || idText.length === 0) {
2476
+ this.skipNode(state);
2477
+ this.report.error("missing-value", GRAPHML_ISSUE.MISSING_ID, "<node> without an id", { line });
2478
+ } else {
2479
+ try {
2480
+ state.id = this.coercer.text(idText);
2481
+ state.idText = idText;
2482
+ this.reportMerge(idText, line);
2483
+ } catch (err) {
2484
+ this.skipNode(state);
2485
+ this.report.recordError(err, { line, element: idText });
2486
+ }
2487
+ }
2488
+ this.nodes.push(state);
2489
+ this.ctx.push(
2490
+ 5
2491
+ /* Node */
2492
+ );
2493
+ }
2494
+ /**
2495
+ * Add a node to the sink and flush its pending values.
2496
+ * @param node - the node state
2497
+ */
2498
+ materializeNode(node) {
2499
+ if (node.index !== INVALID_INDEX || node.failed) {
2500
+ return;
2501
+ }
2502
+ const id = node.restoredId ?? node.id;
2503
+ if (id === null) {
2504
+ node.failed = true;
2505
+ return;
2506
+ }
2507
+ if (node.restoredId !== null && node.idText !== null) {
2508
+ this.restored.set(node.idText, node.restoredId);
2509
+ }
2510
+ try {
2511
+ const existing = this.sink.indexOf(id);
2512
+ const index = this.sink.addNode(id);
2513
+ if (existing !== INVALID_INDEX && this.declared.has(index)) {
2514
+ this.report.warning(
2515
+ "validation-error",
2516
+ GRAPHML_ISSUE.DUPLICATE_NODE,
2517
+ `node "${String(id)}" is declared twice; the declarations are merged`,
2518
+ { line: node.line, element: String(id) }
2519
+ );
2520
+ } else {
2521
+ this.report.counts.nodes++;
2522
+ }
2523
+ this.declared.add(index);
2524
+ node.index = index;
2525
+ this.checkAbort();
2526
+ if (node.parent !== INVALID_INDEX) {
2527
+ this.sink.setNodeValue(this.parentColumn(), index, node.parent);
2528
+ }
2529
+ const { pending } = node;
2530
+ for (let i = 0; i < pending.length; i += 2) {
2531
+ this.sink.setNodeValue(pending[i], index, pending[i + 1]);
2532
+ }
2533
+ pending.length = 0;
2534
+ } catch (err) {
2535
+ this.skipNode(node);
2536
+ this.report.recordError(err, { line: node.line, element: String(id) });
2537
+ }
2538
+ }
2539
+ /**
2540
+ * Mark a node element skipped (once) before its error is recorded, so the report's counts
2541
+ * include it even when that error is the one that exceeds the limit.
2542
+ * @param node - the node state
2543
+ */
2544
+ skipNode(node) {
2545
+ if (!node.failed) {
2546
+ node.failed = true;
2547
+ this.report.counts.skippedNodes++;
2548
+ }
2549
+ }
2550
+ /** Close a `<node>`. */
2551
+ finishNode() {
2552
+ const node = this.nodes.pop();
2553
+ if (node !== void 0) {
2554
+ this.materializeNode(node);
2555
+ }
2556
+ }
2557
+ /**
2558
+ * The `parent` column, declared on first use.
2559
+ * @returns the handle
2560
+ */
2561
+ parentColumn() {
2562
+ if (this.parentHandle === INVALID_INDEX) {
2563
+ this.parentHandle = this.sink.declareNodeColumn({
2564
+ name: PARENT_COLUMN,
2565
+ dtype: "u32",
2566
+ role: "parent",
2567
+ refersTo: "node",
2568
+ nullable: true,
2569
+ origin: { format: FORMAT, id: null, title: null, type: "graph", namespace: null }
2570
+ });
2571
+ }
2572
+ return this.parentHandle;
2573
+ }
2574
+ // ------------------------------------------------------------------ edges
2575
+ /**
2576
+ * Open an `<edge>`; it is pushed at `</edge>` once its weight and data are known.
2577
+ * @param attrs - the edge attributes
2578
+ * @param line - the line
2579
+ */
2580
+ beginEdge(attrs, line) {
2581
+ const graph = this.graphs[this.graphs.length - 1];
2582
+ const edge = {
2583
+ source: null,
2584
+ target: null,
2585
+ id: attrs.get("id") ?? null,
2586
+ kind: graph.directed ? "directed" : "undirected",
2587
+ sourcePort: attrs.get("sourceport") ?? null,
2588
+ targetPort: attrs.get("targetport") ?? null,
2589
+ weight: void 0,
2590
+ pending: [],
2591
+ failed: false,
2592
+ line
2593
+ };
2594
+ const where = { line, element: edge.id };
2595
+ const sourceText = attrs.get("source");
2596
+ const targetText = attrs.get("target");
2597
+ if (sourceText === void 0 || sourceText.length === 0) {
2598
+ this.skipEdge(edge);
2599
+ this.report.error("missing-value", GRAPHML_ISSUE.MISSING_ENDPOINT, "<edge> without a source", where);
2600
+ }
2601
+ if (targetText === void 0 || targetText.length === 0) {
2602
+ this.skipEdge(edge);
2603
+ this.report.error("missing-value", GRAPHML_ISSUE.MISSING_ENDPOINT, "<edge> without a target", where);
2604
+ }
2605
+ if (!edge.failed && sourceText !== void 0 && targetText !== void 0) {
2606
+ try {
2607
+ edge.source = this.endpointId(sourceText, line);
2608
+ edge.target = this.endpointId(targetText, line);
2609
+ } catch (err) {
2610
+ this.skipEdge(edge);
2611
+ this.report.recordError(err, where);
2612
+ }
2613
+ }
2614
+ const directedText = attrs.get("directed");
2615
+ if (directedText !== void 0) {
2616
+ if (directedText === "true") {
2617
+ edge.kind = "directed";
2618
+ } else if (directedText === "false") {
2619
+ edge.kind = "undirected";
2620
+ } else {
2621
+ this.skipEdge(edge);
2622
+ this.report.error(
2623
+ "validation-error",
2624
+ GRAPHML_ISSUE.INVALID_DIRECTED,
2625
+ `directed="${directedText}" is neither true nor false`,
2626
+ where
2627
+ );
2628
+ }
2629
+ }
2630
+ this.edge = edge;
2631
+ this.ctx.push(
2632
+ 6
2633
+ /* Edge */
2634
+ );
2635
+ }
2636
+ /**
2637
+ * Close an `<edge>`: push it through the direction resolver and write its values.
2638
+ */
2639
+ finishEdge() {
2640
+ const { edge } = this;
2641
+ this.edge = null;
2642
+ if (edge === null) {
2643
+ return;
2644
+ }
2645
+ if (edge.failed || edge.source === null || edge.target === null) {
2646
+ this.skipEdge(edge);
2647
+ return;
2648
+ }
2649
+ const element = edge.id ?? `${String(edge.source)} -> ${String(edge.target)}`;
2650
+ const where = { line: edge.line, element };
2651
+ try {
2652
+ if (!this.options.addMissingNodes) {
2653
+ this.requireNode(edge.source);
2654
+ this.requireNode(edge.target);
2655
+ }
2656
+ if (edge.id !== null && this.edgeIds.has(edge.id)) {
2657
+ throw new GraphFormatError("E_DUPLICATE_EDGE_ID", `edge id "${edge.id}" is used twice`, {
2658
+ id: edge.id
2659
+ });
2660
+ }
2661
+ const before = this.sink.edgeCount;
2662
+ this.countEndpoints(edge.source, edge.target);
2663
+ const e = this.direction.addEdge(edge.source, edge.target, edge.kind, edge.weight, where);
2664
+ this.report.counts.edges += this.sink.edgeCount - before;
2665
+ this.checkAbort();
2666
+ if (edge.id !== null) {
2667
+ this.edgeIds.add(edge.id);
2668
+ this.sink.setEdgeValue(this.edgeIdColumn(), e, edge.id);
2669
+ }
2670
+ if (edge.sourcePort !== null) {
2671
+ this.sink.setEdgeValue(this.portColumn("source"), e, edge.sourcePort);
2672
+ }
2673
+ if (edge.targetPort !== null) {
2674
+ this.sink.setEdgeValue(this.portColumn("target"), e, edge.targetPort);
2675
+ }
2676
+ const { pending } = edge;
2677
+ for (let i = 0; i < pending.length; i += 2) {
2678
+ this.sink.setEdgeValue(pending[i], e, pending[i + 1]);
2679
+ }
2680
+ } catch (err) {
2681
+ this.skipEdge(edge);
2682
+ this.report.recordError(err, where);
2683
+ }
2684
+ }
2685
+ /**
2686
+ * Mark an edge (or hyperedge) element skipped once, before its error is recorded.
2687
+ * @param edge - the edge state
2688
+ */
2689
+ skipEdge(edge) {
2690
+ if (!edge.failed) {
2691
+ edge.failed = true;
2692
+ this.report.counts.skippedEdges++;
2693
+ }
2694
+ }
2695
+ /**
2696
+ * E_UNKNOWN_NODE when an endpoint was never declared (addMissingNodes false enforced by the
2697
+ * importer, whatever the sink allows).
2698
+ * @param id - the endpoint id
2699
+ */
2700
+ requireNode(id) {
2701
+ if (this.sink.indexOf(id) === INVALID_INDEX) {
2702
+ throw new GraphFormatError("E_UNKNOWN_NODE", `edge endpoint "${String(id)}" is not a declared node`, {
2703
+ id
2704
+ });
2705
+ }
2706
+ }
2707
+ /**
2708
+ * The edge `id` column (role id, unique), declared on first use.
2709
+ * @returns the handle
2710
+ */
2711
+ edgeIdColumn() {
2712
+ if (this.edgeIdHandle === INVALID_INDEX) {
2713
+ this.edgeIdHandle = this.sink.declareEdgeColumn({
2714
+ name: EDGE_ID_COLUMN,
2715
+ dtype: "string",
2716
+ role: "id",
2717
+ unique: true,
2718
+ nullable: true,
2719
+ origin: { format: FORMAT, id: null, title: null, type: "id", namespace: null }
2720
+ });
2721
+ }
2722
+ return this.edgeIdHandle;
2723
+ }
2724
+ /**
2725
+ * The sourceport / targetport column, declared on first use.
2726
+ * @param side - which end
2727
+ * @returns the handle
2728
+ */
2729
+ portColumn(side) {
2730
+ if (side === "source") {
2731
+ if (this.sourcePortHandle === INVALID_INDEX) {
2732
+ this.sourcePortHandle = this.sink.declareEdgeColumn({
2733
+ name: SOURCE_PORT_COLUMN,
2734
+ dtype: "string",
2735
+ role: "sourcePort",
2736
+ nullable: true,
2737
+ origin: { format: FORMAT, id: null, title: null, type: "sourceport", namespace: null }
2738
+ });
2739
+ }
2740
+ return this.sourcePortHandle;
2741
+ }
2742
+ if (this.targetPortHandle === INVALID_INDEX) {
2743
+ this.targetPortHandle = this.sink.declareEdgeColumn({
2744
+ name: TARGET_PORT_COLUMN,
2745
+ dtype: "string",
2746
+ role: "targetPort",
2747
+ nullable: true,
2748
+ origin: { format: FORMAT, id: null, title: null, type: "targetport", namespace: null }
2749
+ });
2750
+ }
2751
+ return this.targetPortHandle;
2752
+ }
2753
+ // ------------------------------------------------------------------ hyperedges
2754
+ /**
2755
+ * Open a `<hyperedge>` per the hyperedges option.
2756
+ * @param attrs - its attributes
2757
+ * @param line - the line
2758
+ */
2759
+ beginHyperedge(attrs, line) {
2760
+ const id = attrs.get("id") ?? null;
2761
+ const where = { line, element: id };
2762
+ switch (this.options.hyperedges) {
2763
+ case "error":
2764
+ this.report.error(
2765
+ "unsupported",
2766
+ GRAPHML_ISSUE.HYPEREDGE,
2767
+ 'hyperedge refused (hyperedges: "error")',
2768
+ where
2769
+ );
2770
+ throw this.report.abort("hyperedge refused", { code: GRAPHML_ISSUE.HYPEREDGE });
2771
+ case "skip":
2772
+ this.report.warnOnce(
2773
+ "unsupported",
2774
+ GRAPHML_ISSUE.HYPEREDGE_SKIPPED,
2775
+ 'hyperedges are skipped (hyperedges: "skip"); use "star" or "clique" to expand them',
2776
+ where
2777
+ );
2778
+ this.report.counts.skippedEdges++;
2779
+ this.ctx.push(
2780
+ 10
2781
+ /* Skip */
2782
+ );
2783
+ return;
2784
+ case "star":
2785
+ case "clique":
2786
+ this.hyperedge = { id, endpoints: [], failed: false, line };
2787
+ this.ctx.push(
2788
+ 7
2789
+ /* Hyperedge */
2790
+ );
2791
+ return;
2792
+ default: {
2793
+ const policy = this.options.hyperedges;
2794
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown hyperedges policy ${String(policy)}`, {
2795
+ option: "hyperedges"
2796
+ });
2797
+ }
2798
+ }
2799
+ }
2800
+ /**
2801
+ * An `<endpoint>` of the open hyperedge.
2802
+ * @param attrs - its attributes
2803
+ * @param line - the line
2804
+ */
2805
+ readEndpoint(attrs, line) {
2806
+ const { hyperedge } = this;
2807
+ if (hyperedge === null) {
2808
+ return;
2809
+ }
2810
+ const where = { line, element: hyperedge.id };
2811
+ const nodeText = attrs.get("node");
2812
+ const typeText = attrs.get("type") ?? "undir";
2813
+ if (nodeText === void 0 || nodeText.length === 0) {
2814
+ this.skipEdge(hyperedge);
2815
+ this.report.error("missing-value", GRAPHML_ISSUE.HYPEREDGE_ENDPOINT, "<endpoint> without a node", where);
2816
+ return;
2817
+ }
2818
+ if (typeText !== "in" && typeText !== "out" && typeText !== "undir") {
2819
+ this.skipEdge(hyperedge);
2820
+ this.report.error(
2821
+ "validation-error",
2822
+ GRAPHML_ISSUE.HYPEREDGE_ENDPOINT,
2823
+ `endpoint type "${typeText}" is not in, out or undir`,
2824
+ where
2825
+ );
2826
+ return;
2827
+ }
2828
+ try {
2829
+ const node = this.endpointId(nodeText, line);
2830
+ hyperedge.endpoints.push({ node, type: typeText });
2831
+ } catch (err) {
2832
+ this.skipEdge(hyperedge);
2833
+ this.report.recordError(err, where);
2834
+ }
2835
+ }
2836
+ /** Close a `<hyperedge>`: expand it to a star or a clique. */
2837
+ finishHyperedge() {
2838
+ const { hyperedge } = this;
2839
+ this.hyperedge = null;
2840
+ if (hyperedge === null) {
2841
+ return;
2842
+ }
2843
+ if (hyperedge.failed) {
2844
+ return;
2845
+ }
2846
+ const where = { line: hyperedge.line, element: hyperedge.id };
2847
+ try {
2848
+ if (this.options.hyperedges === "star") {
2849
+ this.expandStar(hyperedge, where);
2850
+ } else {
2851
+ this.expandClique(hyperedge, where);
2852
+ }
2853
+ } catch (err) {
2854
+ this.skipEdge(hyperedge);
2855
+ this.report.recordError(err, where);
2856
+ }
2857
+ }
2858
+ /**
2859
+ * Star expansion: a synthetic hub node (marked in the `graphty.hyperedge` column) joined to
2860
+ * every endpoint; `in` endpoints point at the hub, `out` endpoints are pointed at by it,
2861
+ * `undir` endpoints are undirected.
2862
+ * @param hyperedge - the hyperedge
2863
+ * @param where - the location for issues
2864
+ */
2865
+ expandStar(hyperedge, where) {
2866
+ this.hyperedgeCount++;
2867
+ let hubId = hyperedge.id ?? `hyperedge${this.hyperedgeCount}`;
2868
+ for (let n = 2; this.sink.indexOf(hubId) !== INVALID_INDEX; n++) {
2869
+ hubId = `${hyperedge.id ?? `hyperedge${this.hyperedgeCount}`}#${n}`;
2870
+ }
2871
+ const hub = this.sink.addNode(hubId);
2872
+ this.declared.add(hub);
2873
+ this.report.counts.nodes++;
2874
+ if (this.hubHandle === INVALID_INDEX) {
2875
+ this.hubHandle = this.sink.declareNodeColumn({
2876
+ name: HYPEREDGE_HUB_COLUMN,
2877
+ dtype: "bool",
2878
+ nullable: true,
2879
+ origin: { format: FORMAT, id: null, title: null, type: "hyperedge", namespace: null }
2880
+ });
2881
+ }
2882
+ this.sink.setNodeValue(this.hubHandle, hub, true);
2883
+ for (const endpoint of hyperedge.endpoints) {
2884
+ const before = this.sink.edgeCount;
2885
+ switch (endpoint.type) {
2886
+ case "in":
2887
+ this.direction.addEdge(endpoint.node, hubId, "directed", void 0, where);
2888
+ break;
2889
+ case "out":
2890
+ this.direction.addEdge(hubId, endpoint.node, "directed", void 0, where);
2891
+ break;
2892
+ case "undir":
2893
+ this.direction.addEdge(endpoint.node, hubId, "undirected", void 0, where);
2894
+ break;
2895
+ default: {
2896
+ const { type } = endpoint;
2897
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown endpoint type ${String(type)}`, {});
2898
+ }
2899
+ }
2900
+ this.report.counts.edges += this.sink.edgeCount - before;
2901
+ }
2902
+ }
2903
+ /**
2904
+ * Clique expansion: every `in` endpoint points at every `out` endpoint; `undir` endpoints are
2905
+ * joined undirected to every other endpoint; two endpoints of the same direction are not joined.
2906
+ * @param hyperedge - the hyperedge
2907
+ * @param where - the location for issues
2908
+ */
2909
+ expandClique(hyperedge, where) {
2910
+ const { endpoints } = hyperedge;
2911
+ for (let i = 0; i < endpoints.length; i++) {
2912
+ for (let j = i + 1; j < endpoints.length; j++) {
2913
+ const a = endpoints[i];
2914
+ const b = endpoints[j];
2915
+ const before = this.sink.edgeCount;
2916
+ if (a.type === "undir" || b.type === "undir") {
2917
+ this.direction.addEdge(a.node, b.node, "undirected", void 0, where);
2918
+ } else if (a.type === "in" && b.type === "out") {
2919
+ this.direction.addEdge(a.node, b.node, "directed", void 0, where);
2920
+ } else if (a.type === "out" && b.type === "in") {
2921
+ this.direction.addEdge(b.node, a.node, "directed", void 0, where);
2922
+ } else {
2923
+ continue;
2924
+ }
2925
+ this.report.counts.edges += this.sink.edgeCount - before;
2926
+ }
2927
+ }
2928
+ }
2929
+ // ------------------------------------------------------------------ options and ids
2930
+ /**
2931
+ * Count the endpoints of an edge the file never declared as nodes of the sink (created under
2932
+ * addMissingNodes), so the report describes what the sink holds (design section 8.6).
2933
+ * @param source - the source id
2934
+ * @param target - the target id
2935
+ */
2936
+ countEndpoints(source, target) {
2937
+ if (!this.options.addMissingNodes) {
2938
+ return;
2939
+ }
2940
+ let created = 0;
2941
+ if (this.sink.indexOf(source) === INVALID_INDEX) {
2942
+ created++;
2943
+ }
2944
+ if (source !== target && this.sink.indexOf(target) === INVALID_INDEX) {
2945
+ created++;
2946
+ }
2947
+ this.report.counts.nodes += created;
2948
+ }
2949
+ /** Check the cancellation signal every ABORT_CHECK_INTERVAL elements. */
2950
+ checkAbort() {
2951
+ if (++this.elementsSinceCheck >= ABORT_CHECK_INTERVAL) {
2952
+ this.elementsSinceCheck = 0;
2953
+ throwIfAborted(this.options.signal);
2954
+ }
2955
+ }
2956
+ /**
2957
+ * The id an edge endpoint refers to: the original id when the text is a mangled id restored
2958
+ * from `graphty:originalId` (the exporter writes endpoints with the mangled id), else the
2959
+ * coerced text.
2960
+ * @param text - the source / target / endpoint node text
2961
+ * @param line - the line
2962
+ * @returns the node id
2963
+ */
2964
+ endpointId(text, line) {
2965
+ const restored = this.restored.get(text);
2966
+ if (restored !== void 0) {
2967
+ return restored;
2968
+ }
2969
+ const id = this.coercer.text(text);
2970
+ this.reportMerge(text, line);
2971
+ return id;
2972
+ }
2973
+ /**
2974
+ * Report a merge the coercer detected under ids: "number".
2975
+ * @param text - the id text just coerced
2976
+ * @param line - the line
2977
+ */
2978
+ reportMerge(text, line) {
2979
+ const merge = this.coercer.lastMerge;
2980
+ if (merge !== null) {
2981
+ this.report.warning(
2982
+ "coercion",
2983
+ GRAPHML_ISSUE.ID_MERGED,
2984
+ `id "${merge.text}" merged with "${merge.previousText}" as ${merge.id} under ids: "number"`,
2985
+ { line, element: text }
2986
+ );
2987
+ }
2988
+ }
2989
+ }
2990
+ function hint(text) {
2991
+ if (text === void 0 || !/^[0-9]+$/.test(text)) {
2992
+ return null;
2993
+ }
2994
+ const n = Number(text);
2995
+ return Number.isSafeInteger(n) ? n : null;
2996
+ }
2997
+ function resolveYfiles(options) {
2998
+ const value = options?.yfiles;
2999
+ if (value === void 0) {
3000
+ return "json";
3001
+ }
3002
+ if (typeof value !== "string" || !YFILES_MODES.has(value)) {
3003
+ throw new GraphFormatError("E_UNSUPPORTED", `option yfiles: ${JSON.stringify(value)} is not "json" or "skip"`, {
3004
+ option: "yfiles",
3005
+ found: value
3006
+ });
3007
+ }
3008
+ return value;
3009
+ }
3010
+ function sniffGraphml(head) {
3011
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(head.subarray(0, SNIFF_BYTES));
3012
+ const root = text.indexOf("<graphml");
3013
+ if (root >= 0) {
3014
+ return text.includes(GRAPHML_NAMESPACE, root) ? 1 : 0.9;
3015
+ }
3016
+ return /^\s*<(\?xml|!--|!DOCTYPE)/.test(text) ? 0.05 : 0;
3017
+ }
3018
+ async function importGraphml(input, sink, options) {
3019
+ const common = resolveImportOptions(options, FORMAT_DEFAULTS);
3020
+ const yfilesMode = resolveYfiles(options);
3021
+ const report = new ImportReportBuilder(FORMAT, common.errorLimit);
3022
+ reportSinkOptions(sink, options, report, true);
3023
+ reportUnusedOptions(options, report, USED_OPTIONS);
3024
+ const reader = new GraphmlReader(sink, report, common, yfilesMode);
3025
+ try {
3026
+ await tokenizeXml(textChunks(input, report, common), reader);
3027
+ } catch (err) {
3028
+ if (err instanceof XmlSyntaxError) {
3029
+ report.fail(GRAPHML_ISSUE.XML_SYNTAX, err.message, { line: err.line });
3030
+ }
3031
+ throw err;
3032
+ }
3033
+ reader.finish();
3034
+ throwIfAborted(common.signal);
3035
+ return report.finish();
3036
+ }
3037
+ const graphmlImporter = Object.freeze({
3038
+ format: FORMAT,
3039
+ extensions: EXTENSIONS,
3040
+ mimeTypes: MIME_TYPES,
3041
+ sniff: sniffGraphml,
3042
+ import: importGraphml
3043
+ });
3044
+ export {
3045
+ GRAPHML_ISSUE as G,
3046
+ GRAPHML_LOSS as a,
3047
+ graphmlImporter as b,
3048
+ graphmlExporter as g
3049
+ };
3050
+ //# sourceMappingURL=importer-GozH8DkN.js.map