@graphty/graph-io 0.3.25 → 0.3.27

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 (581) hide show
  1. package/README.md +49 -414
  2. package/dist/chunks/children-CL3Cy0ez.js.map +1 -1
  3. package/dist/chunks/choose-DrXtu_96.js +56 -0
  4. package/dist/chunks/choose-DrXtu_96.js.map +1 -0
  5. package/dist/chunks/constants-ikG85dT8.js +231 -0
  6. package/dist/chunks/constants-ikG85dT8.js.map +1 -0
  7. package/dist/chunks/{escape-B-tkk_Cl.js → escape-D1HPTUrP.js} +390 -99
  8. package/dist/chunks/escape-D1HPTUrP.js.map +1 -0
  9. package/dist/chunks/export-CHd4tQE2.js +4333 -0
  10. package/dist/chunks/export-CHd4tQE2.js.map +1 -0
  11. package/dist/chunks/{importer-eUREhBGj.js → exporter-D23CKNqF.js} +4969 -3201
  12. package/dist/chunks/exporter-D23CKNqF.js.map +1 -0
  13. package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-DfWTgrti.js} +23 -21
  14. package/dist/chunks/exporter-DfWTgrti.js.map +1 -0
  15. package/dist/chunks/exporter-DhtW6FxK.js +3465 -0
  16. package/dist/chunks/exporter-DhtW6FxK.js.map +1 -0
  17. package/dist/chunks/{exporter-BGrsWamJ.js → exporter-IHv4nkCm.js} +38 -229
  18. package/dist/chunks/exporter-IHv4nkCm.js.map +1 -0
  19. package/dist/chunks/{importer-BWY2FFc5.js → importer-B1wmPQjr.js} +255 -77
  20. package/dist/chunks/importer-B1wmPQjr.js.map +1 -0
  21. package/dist/chunks/{importer-D_e7e7LX.js → importer-B2W_58pz.js} +399 -146
  22. package/dist/chunks/importer-B2W_58pz.js.map +1 -0
  23. package/dist/chunks/importer-BSALvkui.js +2811 -0
  24. package/dist/chunks/importer-BSALvkui.js.map +1 -0
  25. package/dist/chunks/{importer-vi3bSdtw.js → importer-BVRE--Un.js} +510 -77
  26. package/dist/chunks/importer-BVRE--Un.js.map +1 -0
  27. package/dist/chunks/{importer-DET1tg6s.js → importer-CIAzt-wo.js} +1689 -312
  28. package/dist/chunks/importer-CIAzt-wo.js.map +1 -0
  29. package/dist/chunks/{importer-DIrbbnAf.js → importer-DpEW5zeG.js} +585 -226
  30. package/dist/chunks/importer-DpEW5zeG.js.map +1 -0
  31. package/dist/chunks/{importer-zGLo8Dg_.js → importer-z_FL4VMb.js} +488 -161
  32. package/dist/chunks/importer-z_FL4VMb.js.map +1 -0
  33. package/dist/chunks/json-elements-BAmNBZok.js +1225 -0
  34. package/dist/chunks/json-elements-BAmNBZok.js.map +1 -0
  35. package/dist/chunks/ontology-export-Bab4eUA8.js +357 -0
  36. package/dist/chunks/ontology-export-Bab4eUA8.js.map +1 -0
  37. package/dist/chunks/{records-dbRkxwaq.js → records-CdlvL4RO.js} +298 -114
  38. package/dist/chunks/records-CdlvL4RO.js.map +1 -0
  39. package/dist/chunks/{text-Dr0Ifpag.js → text-DzXjAiTl.js} +29 -11
  40. package/dist/chunks/text-DzXjAiTl.js.map +1 -0
  41. package/dist/chunks/{writer-C7flM-Ih.js → writer-DxjgHwSe.js} +19 -1
  42. package/dist/chunks/writer-DxjgHwSe.js.map +1 -0
  43. package/dist/csv.js +952 -262
  44. package/dist/csv.js.map +1 -1
  45. package/dist/cx.js +6 -3
  46. package/dist/cx2.js +2 -2
  47. package/dist/cys.js +6 -3
  48. package/dist/dot.js +1 -1
  49. package/dist/gexf.js +554 -122
  50. package/dist/gexf.js.map +1 -1
  51. package/dist/gml.js +344 -87
  52. package/dist/gml.js.map +1 -1
  53. package/dist/graph-io.js +1105 -213
  54. package/dist/graph-io.js.map +1 -1
  55. package/dist/graphml.js +3 -2
  56. package/dist/graphml.js.map +1 -1
  57. package/dist/json.js +3 -2
  58. package/dist/neo4j.js +565 -158
  59. package/dist/neo4j.js.map +1 -1
  60. package/dist/obo.js +6 -3
  61. package/dist/pajek.js +1 -1
  62. package/dist/src/children.d.ts +18 -6
  63. package/dist/src/children.d.ts.map +1 -1
  64. package/dist/src/children.js +6 -3
  65. package/dist/src/children.js.map +1 -1
  66. package/dist/src/common/attributes.d.ts +29 -11
  67. package/dist/src/common/attributes.d.ts.map +1 -1
  68. package/dist/src/common/attributes.js +25 -7
  69. package/dist/src/common/attributes.js.map +1 -1
  70. package/dist/src/common/cell-budget.d.ts +9 -2
  71. package/dist/src/common/cell-budget.d.ts.map +1 -1
  72. package/dist/src/common/cell-budget.js +11 -3
  73. package/dist/src/common/cell-budget.js.map +1 -1
  74. package/dist/src/common/choose.d.ts +19 -0
  75. package/dist/src/common/choose.d.ts.map +1 -0
  76. package/dist/src/common/choose.js +72 -0
  77. package/dist/src/common/choose.js.map +1 -0
  78. package/dist/src/common/codes.d.ts +456 -66
  79. package/dist/src/common/codes.d.ts.map +1 -1
  80. package/dist/src/common/codes.js +458 -66
  81. package/dist/src/common/codes.js.map +1 -1
  82. package/dist/src/common/compare.d.ts +81 -0
  83. package/dist/src/common/compare.d.ts.map +1 -0
  84. package/dist/src/common/compare.js +394 -0
  85. package/dist/src/common/compare.js.map +1 -0
  86. package/dist/src/common/cx-export.d.ts +123 -0
  87. package/dist/src/common/cx-export.d.ts.map +1 -0
  88. package/dist/src/common/cx-export.js +334 -0
  89. package/dist/src/common/cx-export.js.map +1 -0
  90. package/dist/src/common/declared-types.d.ts +22 -2
  91. package/dist/src/common/declared-types.d.ts.map +1 -1
  92. package/dist/src/common/declared-types.js +21 -3
  93. package/dist/src/common/declared-types.js.map +1 -1
  94. package/dist/src/common/direction.d.ts +74 -50
  95. package/dist/src/common/direction.d.ts.map +1 -1
  96. package/dist/src/common/direction.js +47 -27
  97. package/dist/src/common/direction.js.map +1 -1
  98. package/dist/src/common/escape.d.ts +21 -9
  99. package/dist/src/common/escape.d.ts.map +1 -1
  100. package/dist/src/common/escape.js +39 -22
  101. package/dist/src/common/escape.js.map +1 -1
  102. package/dist/src/common/export.d.ts +144 -32
  103. package/dist/src/common/export.d.ts.map +1 -1
  104. package/dist/src/common/export.js +216 -42
  105. package/dist/src/common/export.js.map +1 -1
  106. package/dist/src/common/format.d.ts +23 -1
  107. package/dist/src/common/format.d.ts.map +1 -1
  108. package/dist/src/common/format.js +26 -1
  109. package/dist/src/common/format.js.map +1 -1
  110. package/dist/src/common/ids.d.ts +9 -2
  111. package/dist/src/common/ids.d.ts.map +1 -1
  112. package/dist/src/common/ids.js +6 -2
  113. package/dist/src/common/ids.js.map +1 -1
  114. package/dist/src/common/input.d.ts +122 -28
  115. package/dist/src/common/input.d.ts.map +1 -1
  116. package/dist/src/common/input.js +890 -124
  117. package/dist/src/common/input.js.map +1 -1
  118. package/dist/src/common/json-elements.d.ts +144 -20
  119. package/dist/src/common/json-elements.d.ts.map +1 -1
  120. package/dist/src/common/json-elements.js +869 -189
  121. package/dist/src/common/json-elements.js.map +1 -1
  122. package/dist/src/common/line-format.d.ts +90 -0
  123. package/dist/src/common/line-format.d.ts.map +1 -0
  124. package/dist/src/common/line-format.js +185 -0
  125. package/dist/src/common/line-format.js.map +1 -0
  126. package/dist/src/common/lists.d.ts +6 -1
  127. package/dist/src/common/lists.d.ts.map +1 -1
  128. package/dist/src/common/lists.js +3 -1
  129. package/dist/src/common/lists.js.map +1 -1
  130. package/dist/src/common/ontology-export.d.ts +167 -0
  131. package/dist/src/common/ontology-export.d.ts.map +1 -0
  132. package/dist/src/common/ontology-export.js +359 -0
  133. package/dist/src/common/ontology-export.js.map +1 -0
  134. package/dist/src/common/ontology.d.ts +25 -3
  135. package/dist/src/common/ontology.d.ts.map +1 -1
  136. package/dist/src/common/ontology.js +30 -9
  137. package/dist/src/common/ontology.js.map +1 -1
  138. package/dist/src/common/options.d.ts +62 -12
  139. package/dist/src/common/options.d.ts.map +1 -1
  140. package/dist/src/common/options.js +89 -11
  141. package/dist/src/common/options.js.map +1 -1
  142. package/dist/src/common/plural.d.ts +31 -0
  143. package/dist/src/common/plural.d.ts.map +1 -0
  144. package/dist/src/common/plural.js +46 -0
  145. package/dist/src/common/plural.js.map +1 -0
  146. package/dist/src/common/report.d.ts +72 -14
  147. package/dist/src/common/report.d.ts.map +1 -1
  148. package/dist/src/common/report.js +146 -22
  149. package/dist/src/common/report.js.map +1 -1
  150. package/dist/src/common/temporal.d.ts +22 -5
  151. package/dist/src/common/temporal.d.ts.map +1 -1
  152. package/dist/src/common/temporal.js +13 -2
  153. package/dist/src/common/temporal.js.map +1 -1
  154. package/dist/src/common/text.d.ts +39 -23
  155. package/dist/src/common/text.d.ts.map +1 -1
  156. package/dist/src/common/text.js +51 -21
  157. package/dist/src/common/text.js.map +1 -1
  158. package/dist/src/common/weights.d.ts +37 -4
  159. package/dist/src/common/weights.d.ts.map +1 -1
  160. package/dist/src/common/weights.js +61 -2
  161. package/dist/src/common/weights.js.map +1 -1
  162. package/dist/src/common/writer.d.ts +19 -3
  163. package/dist/src/common/writer.d.ts.map +1 -1
  164. package/dist/src/common/writer.js +30 -2
  165. package/dist/src/common/writer.js.map +1 -1
  166. package/dist/src/common/xml.d.ts +125 -9
  167. package/dist/src/common/xml.d.ts.map +1 -1
  168. package/dist/src/common/xml.js +402 -85
  169. package/dist/src/common/xml.js.map +1 -1
  170. package/dist/src/common/zip.d.ts +44 -4
  171. package/dist/src/common/zip.d.ts.map +1 -1
  172. package/dist/src/common/zip.js +134 -5
  173. package/dist/src/common/zip.js.map +1 -1
  174. package/dist/src/formats/csv/exporter.d.ts +80 -24
  175. package/dist/src/formats/csv/exporter.d.ts.map +1 -1
  176. package/dist/src/formats/csv/exporter.js +146 -33
  177. package/dist/src/formats/csv/exporter.js.map +1 -1
  178. package/dist/src/formats/csv/header.d.ts +38 -9
  179. package/dist/src/formats/csv/header.d.ts.map +1 -1
  180. package/dist/src/formats/csv/header.js +46 -10
  181. package/dist/src/formats/csv/header.js.map +1 -1
  182. package/dist/src/formats/csv/importer.d.ts +187 -37
  183. package/dist/src/formats/csv/importer.d.ts.map +1 -1
  184. package/dist/src/formats/csv/importer.js +835 -204
  185. package/dist/src/formats/csv/importer.js.map +1 -1
  186. package/dist/src/formats/csv/index.d.ts +100 -19
  187. package/dist/src/formats/csv/index.d.ts.map +1 -1
  188. package/dist/src/formats/csv/index.js +94 -21
  189. package/dist/src/formats/csv/index.js.map +1 -1
  190. package/dist/src/formats/csv/records.d.ts +95 -10
  191. package/dist/src/formats/csv/records.d.ts.map +1 -1
  192. package/dist/src/formats/csv/records.js +333 -122
  193. package/dist/src/formats/csv/records.js.map +1 -1
  194. package/dist/src/formats/csv/values.d.ts +1 -0
  195. package/dist/src/formats/csv/values.d.ts.map +1 -1
  196. package/dist/src/formats/csv/values.js +1 -0
  197. package/dist/src/formats/csv/values.js.map +1 -1
  198. package/dist/src/formats/cx/exporter.d.ts +102 -0
  199. package/dist/src/formats/cx/exporter.d.ts.map +1 -0
  200. package/dist/src/formats/cx/exporter.js +1217 -0
  201. package/dist/src/formats/cx/exporter.js.map +1 -0
  202. package/dist/src/formats/cx/importer.d.ts +79 -32
  203. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  204. package/dist/src/formats/cx/importer.js +682 -287
  205. package/dist/src/formats/cx/importer.js.map +1 -1
  206. package/dist/src/formats/cx/index.d.ts +4 -3
  207. package/dist/src/formats/cx/index.d.ts.map +1 -1
  208. package/dist/src/formats/cx/index.js +4 -3
  209. package/dist/src/formats/cx/index.js.map +1 -1
  210. package/dist/src/formats/cx2/exporter.d.ts +29 -11
  211. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  212. package/dist/src/formats/cx2/exporter.js +39 -268
  213. package/dist/src/formats/cx2/exporter.js.map +1 -1
  214. package/dist/src/formats/cx2/importer.d.ts +78 -28
  215. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  216. package/dist/src/formats/cx2/importer.js +227 -80
  217. package/dist/src/formats/cx2/importer.js.map +1 -1
  218. package/dist/src/formats/cx2/index.d.ts +2 -1
  219. package/dist/src/formats/cx2/index.d.ts.map +1 -1
  220. package/dist/src/formats/cx2/index.js +2 -1
  221. package/dist/src/formats/cx2/index.js.map +1 -1
  222. package/dist/src/formats/cys/constants.d.ts +66 -17
  223. package/dist/src/formats/cys/constants.d.ts.map +1 -1
  224. package/dist/src/formats/cys/constants.js +59 -18
  225. package/dist/src/formats/cys/constants.js.map +1 -1
  226. package/dist/src/formats/cys/exporter.d.ts +118 -0
  227. package/dist/src/formats/cys/exporter.d.ts.map +1 -0
  228. package/dist/src/formats/cys/exporter.js +1072 -0
  229. package/dist/src/formats/cys/exporter.js.map +1 -0
  230. package/dist/src/formats/cys/importer.d.ts +22 -8
  231. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  232. package/dist/src/formats/cys/importer.js +510 -149
  233. package/dist/src/formats/cys/importer.js.map +1 -1
  234. package/dist/src/formats/cys/index.d.ts +4 -2
  235. package/dist/src/formats/cys/index.d.ts.map +1 -1
  236. package/dist/src/formats/cys/index.js +4 -2
  237. package/dist/src/formats/cys/index.js.map +1 -1
  238. package/dist/src/formats/cys/session.d.ts +46 -7
  239. package/dist/src/formats/cys/session.d.ts.map +1 -1
  240. package/dist/src/formats/cys/session.js +101 -27
  241. package/dist/src/formats/cys/session.js.map +1 -1
  242. package/dist/src/formats/cys/tables.d.ts +9 -2
  243. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  244. package/dist/src/formats/cys/tables.js +29 -7
  245. package/dist/src/formats/cys/tables.js.map +1 -1
  246. package/dist/src/formats/dot/exporter.d.ts +42 -14
  247. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  248. package/dist/src/formats/dot/exporter.js +56 -29
  249. package/dist/src/formats/dot/exporter.js.map +1 -1
  250. package/dist/src/formats/dot/importer.d.ts +91 -29
  251. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  252. package/dist/src/formats/dot/importer.js +190 -39
  253. package/dist/src/formats/dot/importer.js.map +1 -1
  254. package/dist/src/formats/dot/index.d.ts +2 -1
  255. package/dist/src/formats/dot/index.d.ts.map +1 -1
  256. package/dist/src/formats/dot/index.js +2 -1
  257. package/dist/src/formats/dot/index.js.map +1 -1
  258. package/dist/src/formats/dot/names.d.ts +44 -11
  259. package/dist/src/formats/dot/names.d.ts.map +1 -1
  260. package/dist/src/formats/dot/names.js +44 -11
  261. package/dist/src/formats/dot/names.js.map +1 -1
  262. package/dist/src/formats/dot/tokenizer.d.ts +9 -2
  263. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
  264. package/dist/src/formats/dot/tokenizer.js +20 -5
  265. package/dist/src/formats/dot/tokenizer.js.map +1 -1
  266. package/dist/src/formats/gexf/exporter.d.ts +46 -15
  267. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  268. package/dist/src/formats/gexf/exporter.js +45 -15
  269. package/dist/src/formats/gexf/exporter.js.map +1 -1
  270. package/dist/src/formats/gexf/importer.d.ts +87 -23
  271. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  272. package/dist/src/formats/gexf/importer.js +471 -90
  273. package/dist/src/formats/gexf/importer.js.map +1 -1
  274. package/dist/src/formats/gexf/index.d.ts +84 -21
  275. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  276. package/dist/src/formats/gexf/index.js +80 -23
  277. package/dist/src/formats/gexf/index.js.map +1 -1
  278. package/dist/src/formats/gexf/schema.d.ts +67 -15
  279. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  280. package/dist/src/formats/gexf/schema.js +59 -13
  281. package/dist/src/formats/gexf/schema.js.map +1 -1
  282. package/dist/src/formats/gml/exporter.d.ts +69 -21
  283. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  284. package/dist/src/formats/gml/exporter.js +74 -29
  285. package/dist/src/formats/gml/exporter.js.map +1 -1
  286. package/dist/src/formats/gml/importer.d.ts +116 -25
  287. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  288. package/dist/src/formats/gml/importer.js +290 -61
  289. package/dist/src/formats/gml/importer.js.map +1 -1
  290. package/dist/src/formats/gml/index.d.ts +87 -24
  291. package/dist/src/formats/gml/index.d.ts.map +1 -1
  292. package/dist/src/formats/gml/index.js +79 -26
  293. package/dist/src/formats/gml/index.js.map +1 -1
  294. package/dist/src/formats/gml/syntax.d.ts +67 -18
  295. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  296. package/dist/src/formats/gml/syntax.js +110 -28
  297. package/dist/src/formats/gml/syntax.js.map +1 -1
  298. package/dist/src/formats/graphml/constants.d.ts +174 -44
  299. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  300. package/dist/src/formats/graphml/constants.js +165 -44
  301. package/dist/src/formats/graphml/constants.js.map +1 -1
  302. package/dist/src/formats/graphml/exporter.d.ts +17 -9
  303. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  304. package/dist/src/formats/graphml/exporter.js +33 -12
  305. package/dist/src/formats/graphml/exporter.js.map +1 -1
  306. package/dist/src/formats/graphml/importer.d.ts +19 -7
  307. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  308. package/dist/src/formats/graphml/importer.js +466 -39
  309. package/dist/src/formats/graphml/importer.js.map +1 -1
  310. package/dist/src/formats/graphml/index.d.ts +2 -1
  311. package/dist/src/formats/graphml/index.d.ts.map +1 -1
  312. package/dist/src/formats/graphml/index.js +2 -1
  313. package/dist/src/formats/graphml/index.js.map +1 -1
  314. package/dist/src/formats/graphml/tree.d.ts +6 -2
  315. package/dist/src/formats/graphml/tree.d.ts.map +1 -1
  316. package/dist/src/formats/graphml/tree.js +6 -2
  317. package/dist/src/formats/graphml/tree.js.map +1 -1
  318. package/dist/src/formats/graphml/yfiles.d.ts +8 -3
  319. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  320. package/dist/src/formats/graphml/yfiles.js +36 -15
  321. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  322. package/dist/src/formats/json/dialect.d.ts +88 -24
  323. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  324. package/dist/src/formats/json/dialect.js +109 -19
  325. package/dist/src/formats/json/dialect.js.map +1 -1
  326. package/dist/src/formats/json/exporter.d.ts +134 -22
  327. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  328. package/dist/src/formats/json/exporter.js +135 -54
  329. package/dist/src/formats/json/exporter.js.map +1 -1
  330. package/dist/src/formats/json/importer.d.ts +173 -52
  331. package/dist/src/formats/json/importer.d.ts.map +1 -1
  332. package/dist/src/formats/json/importer.js +899 -220
  333. package/dist/src/formats/json/importer.js.map +1 -1
  334. package/dist/src/formats/json/index.d.ts +3 -2
  335. package/dist/src/formats/json/index.d.ts.map +1 -1
  336. package/dist/src/formats/json/index.js +3 -2
  337. package/dist/src/formats/json/index.js.map +1 -1
  338. package/dist/src/formats/json/obographs-export.d.ts +68 -0
  339. package/dist/src/formats/json/obographs-export.d.ts.map +1 -0
  340. package/dist/src/formats/json/obographs-export.js +941 -0
  341. package/dist/src/formats/json/obographs-export.js.map +1 -0
  342. package/dist/src/formats/json/obographs.d.ts +26 -0
  343. package/dist/src/formats/json/obographs.d.ts.map +1 -1
  344. package/dist/src/formats/json/obographs.js +297 -80
  345. package/dist/src/formats/json/obographs.js.map +1 -1
  346. package/dist/src/formats/neo4j/exporter.d.ts +109 -21
  347. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  348. package/dist/src/formats/neo4j/exporter.js +239 -86
  349. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  350. package/dist/src/formats/neo4j/header.d.ts +17 -4
  351. package/dist/src/formats/neo4j/header.d.ts.map +1 -1
  352. package/dist/src/formats/neo4j/header.js +36 -4
  353. package/dist/src/formats/neo4j/header.js.map +1 -1
  354. package/dist/src/formats/neo4j/importer.d.ts +107 -27
  355. package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
  356. package/dist/src/formats/neo4j/importer.js +415 -63
  357. package/dist/src/formats/neo4j/importer.js.map +1 -1
  358. package/dist/src/formats/neo4j/index.d.ts +73 -23
  359. package/dist/src/formats/neo4j/index.d.ts.map +1 -1
  360. package/dist/src/formats/neo4j/index.js +65 -25
  361. package/dist/src/formats/neo4j/index.js.map +1 -1
  362. package/dist/src/formats/obo/exporter.d.ts +150 -0
  363. package/dist/src/formats/obo/exporter.d.ts.map +1 -0
  364. package/dist/src/formats/obo/exporter.js +1736 -0
  365. package/dist/src/formats/obo/exporter.js.map +1 -0
  366. package/dist/src/formats/obo/importer.d.ts +95 -19
  367. package/dist/src/formats/obo/importer.d.ts.map +1 -1
  368. package/dist/src/formats/obo/importer.js +475 -104
  369. package/dist/src/formats/obo/importer.js.map +1 -1
  370. package/dist/src/formats/obo/index.d.ts +6 -3
  371. package/dist/src/formats/obo/index.d.ts.map +1 -1
  372. package/dist/src/formats/obo/index.js +6 -3
  373. package/dist/src/formats/obo/index.js.map +1 -1
  374. package/dist/src/formats/obo/syntax.d.ts +108 -4
  375. package/dist/src/formats/obo/syntax.d.ts.map +1 -1
  376. package/dist/src/formats/obo/syntax.js +211 -34
  377. package/dist/src/formats/obo/syntax.js.map +1 -1
  378. package/dist/src/formats/pajek/exporter.d.ts +54 -16
  379. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  380. package/dist/src/formats/pajek/exporter.js +67 -35
  381. package/dist/src/formats/pajek/exporter.js.map +1 -1
  382. package/dist/src/formats/pajek/importer.d.ts +84 -22
  383. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  384. package/dist/src/formats/pajek/importer.js +303 -83
  385. package/dist/src/formats/pajek/importer.js.map +1 -1
  386. package/dist/src/formats/pajek/index.d.ts +2 -1
  387. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  388. package/dist/src/formats/pajek/index.js +2 -1
  389. package/dist/src/formats/pajek/index.js.map +1 -1
  390. package/dist/src/formats/pajek/syntax.d.ts +77 -14
  391. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  392. package/dist/src/formats/pajek/syntax.js +186 -54
  393. package/dist/src/formats/pajek/syntax.js.map +1 -1
  394. package/dist/src/formats/xgmml/columns.d.ts +1 -0
  395. package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
  396. package/dist/src/formats/xgmml/columns.js +4 -0
  397. package/dist/src/formats/xgmml/columns.js.map +1 -1
  398. package/dist/src/formats/xgmml/constants.d.ts +186 -56
  399. package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
  400. package/dist/src/formats/xgmml/constants.js +181 -57
  401. package/dist/src/formats/xgmml/constants.js.map +1 -1
  402. package/dist/src/formats/xgmml/document.d.ts +65 -11
  403. package/dist/src/formats/xgmml/document.d.ts.map +1 -1
  404. package/dist/src/formats/xgmml/document.js +157 -28
  405. package/dist/src/formats/xgmml/document.js.map +1 -1
  406. package/dist/src/formats/xgmml/emit.d.ts +61 -3
  407. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  408. package/dist/src/formats/xgmml/emit.js +129 -26
  409. package/dist/src/formats/xgmml/emit.js.map +1 -1
  410. package/dist/src/formats/xgmml/exporter.d.ts +13 -6
  411. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  412. package/dist/src/formats/xgmml/exporter.js +21 -22
  413. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  414. package/dist/src/formats/xgmml/importer.d.ts +33 -12
  415. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  416. package/dist/src/formats/xgmml/importer.js +35 -72
  417. package/dist/src/formats/xgmml/importer.js.map +1 -1
  418. package/dist/src/formats/xgmml/index.d.ts +1 -0
  419. package/dist/src/formats/xgmml/index.d.ts.map +1 -1
  420. package/dist/src/formats/xgmml/index.js +1 -0
  421. package/dist/src/formats/xgmml/index.js.map +1 -1
  422. package/dist/src/formats/xgmml/values.d.ts +8 -1
  423. package/dist/src/formats/xgmml/values.d.ts.map +1 -1
  424. package/dist/src/formats/xgmml/values.js +8 -3
  425. package/dist/src/formats/xgmml/values.js.map +1 -1
  426. package/dist/src/index.d.ts +23 -18
  427. package/dist/src/index.d.ts.map +1 -1
  428. package/dist/src/index.js +28 -16
  429. package/dist/src/index.js.map +1 -1
  430. package/dist/src/registry.d.ts +478 -92
  431. package/dist/src/registry.d.ts.map +1 -1
  432. package/dist/src/registry.js +807 -88
  433. package/dist/src/registry.js.map +1 -1
  434. package/dist/src/sniff.d.ts +61 -19
  435. package/dist/src/sniff.d.ts.map +1 -1
  436. package/dist/src/sniff.js +121 -29
  437. package/dist/src/sniff.js.map +1 -1
  438. package/dist/src/types.d.ts +362 -112
  439. package/dist/src/types.d.ts.map +1 -1
  440. package/dist/src/types.js +28 -7
  441. package/dist/src/types.js.map +1 -1
  442. package/dist/xgmml.js +2 -2
  443. package/docs/samples/README.md +25 -0
  444. package/docs/samples/got-edges.csv +353 -0
  445. package/docs/samples/got-network.graphml +1384 -0
  446. package/docs/samples/got-nodes.csv +108 -0
  447. package/docs/samples/got.cx +930 -0
  448. package/docs/samples/got.cx2 +465 -0
  449. package/docs/samples/got.gexf +471 -0
  450. package/docs/samples/got.gml +2650 -0
  451. package/docs/samples/got.json +2546 -0
  452. package/docs/samples/got.net +461 -0
  453. package/docs/samples/movies-nodes.csv +9 -0
  454. package/docs/samples/movies-rels.csv +8 -0
  455. package/docs/samples/networks.cys +0 -0
  456. package/docs/samples/proteins.xgmml +9 -0
  457. package/docs/samples/teams.gv +24 -0
  458. package/docs/samples/vehicles.obo +25 -0
  459. package/package.json +9 -4
  460. package/src/children.ts +18 -6
  461. package/src/common/attributes.ts +29 -11
  462. package/src/common/cell-budget.ts +11 -3
  463. package/src/common/choose.ts +89 -0
  464. package/src/common/codes.ts +490 -66
  465. package/src/common/compare.ts +472 -0
  466. package/src/common/cx-export.ts +432 -0
  467. package/src/common/declared-types.ts +40 -6
  468. package/src/common/direction.ts +78 -51
  469. package/src/common/escape.ts +39 -22
  470. package/src/common/export.ts +284 -47
  471. package/src/common/format.ts +30 -1
  472. package/src/common/ids.ts +14 -3
  473. package/src/common/input.ts +1057 -134
  474. package/src/common/json-elements.ts +1035 -199
  475. package/src/common/line-format.ts +249 -0
  476. package/src/common/lists.ts +7 -2
  477. package/src/common/ontology-export.ts +461 -0
  478. package/src/common/ontology.ts +31 -9
  479. package/src/common/options.ts +110 -15
  480. package/src/common/plural.ts +49 -0
  481. package/src/common/report.ts +168 -25
  482. package/src/common/temporal.ts +22 -5
  483. package/src/common/text.ts +62 -23
  484. package/src/common/weights.ts +74 -4
  485. package/src/common/writer.ts +37 -3
  486. package/src/common/xml.ts +479 -90
  487. package/src/common/zip.ts +169 -7
  488. package/src/formats/csv/exporter.ts +202 -47
  489. package/src/formats/csv/header.ts +50 -11
  490. package/src/formats/csv/importer.ts +1133 -245
  491. package/src/formats/csv/index.ts +106 -19
  492. package/src/formats/csv/records.ts +396 -122
  493. package/src/formats/csv/values.ts +1 -0
  494. package/src/formats/cx/exporter.ts +1483 -0
  495. package/src/formats/cx/importer.ts +890 -302
  496. package/src/formats/cx/index.ts +4 -3
  497. package/src/formats/cx2/exporter.ts +62 -339
  498. package/src/formats/cx2/importer.ts +318 -87
  499. package/src/formats/cx2/index.ts +2 -1
  500. package/src/formats/cys/constants.ts +62 -17
  501. package/src/formats/cys/exporter.ts +1368 -0
  502. package/src/formats/cys/importer.ts +659 -157
  503. package/src/formats/cys/index.ts +4 -2
  504. package/src/formats/cys/session.ts +142 -34
  505. package/src/formats/cys/tables.ts +47 -9
  506. package/src/formats/dot/exporter.ts +83 -37
  507. package/src/formats/dot/importer.ts +248 -54
  508. package/src/formats/dot/index.ts +2 -1
  509. package/src/formats/dot/names.ts +44 -11
  510. package/src/formats/dot/tokenizer.ts +25 -6
  511. package/src/formats/gexf/exporter.ts +57 -18
  512. package/src/formats/gexf/importer.ts +636 -105
  513. package/src/formats/gexf/index.ts +90 -21
  514. package/src/formats/gexf/schema.ts +67 -15
  515. package/src/formats/gml/exporter.ts +90 -36
  516. package/src/formats/gml/importer.ts +388 -67
  517. package/src/formats/gml/index.ts +85 -24
  518. package/src/formats/gml/syntax.ts +113 -28
  519. package/src/formats/graphml/constants.ts +178 -44
  520. package/src/formats/graphml/exporter.ts +52 -18
  521. package/src/formats/graphml/importer.ts +640 -60
  522. package/src/formats/graphml/index.ts +2 -1
  523. package/src/formats/graphml/tree.ts +6 -2
  524. package/src/formats/graphml/yfiles.ts +37 -15
  525. package/src/formats/json/dialect.ts +128 -29
  526. package/src/formats/json/exporter.ts +205 -68
  527. package/src/formats/json/importer.ts +1163 -253
  528. package/src/formats/json/index.ts +3 -1
  529. package/src/formats/json/obographs-export.ts +1197 -0
  530. package/src/formats/json/obographs.ts +353 -92
  531. package/src/formats/neo4j/exporter.ts +321 -102
  532. package/src/formats/neo4j/header.ts +44 -6
  533. package/src/formats/neo4j/importer.ts +512 -73
  534. package/src/formats/neo4j/index.ts +75 -23
  535. package/src/formats/obo/exporter.ts +2210 -0
  536. package/src/formats/obo/importer.ts +622 -109
  537. package/src/formats/obo/index.ts +6 -3
  538. package/src/formats/obo/syntax.ts +232 -37
  539. package/src/formats/pajek/exporter.ts +90 -42
  540. package/src/formats/pajek/importer.ts +407 -93
  541. package/src/formats/pajek/index.ts +2 -1
  542. package/src/formats/pajek/syntax.ts +216 -55
  543. package/src/formats/xgmml/columns.ts +10 -0
  544. package/src/formats/xgmml/constants.ts +183 -56
  545. package/src/formats/xgmml/document.ts +212 -34
  546. package/src/formats/xgmml/emit.ts +175 -34
  547. package/src/formats/xgmml/exporter.ts +36 -28
  548. package/src/formats/xgmml/importer.ts +64 -86
  549. package/src/formats/xgmml/index.ts +1 -0
  550. package/src/formats/xgmml/values.ts +12 -4
  551. package/src/index.ts +81 -11
  552. package/src/registry.ts +1121 -135
  553. package/src/sniff.ts +163 -32
  554. package/src/types.ts +385 -114
  555. package/dist/chunks/escape-B-tkk_Cl.js.map +0 -1
  556. package/dist/chunks/export-Bh60Dv-n.js +0 -582
  557. package/dist/chunks/export-Bh60Dv-n.js.map +0 -1
  558. package/dist/chunks/exporter-BGrsWamJ.js.map +0 -1
  559. package/dist/chunks/exporter-DIeGJXAZ.js.map +0 -1
  560. package/dist/chunks/importer-BWY2FFc5.js.map +0 -1
  561. package/dist/chunks/importer-DD-xv9_Y.js +0 -1471
  562. package/dist/chunks/importer-DD-xv9_Y.js.map +0 -1
  563. package/dist/chunks/importer-DET1tg6s.js.map +0 -1
  564. package/dist/chunks/importer-DEcKhsmQ.js +0 -1614
  565. package/dist/chunks/importer-DEcKhsmQ.js.map +0 -1
  566. package/dist/chunks/importer-DIrbbnAf.js.map +0 -1
  567. package/dist/chunks/importer-D_e7e7LX.js.map +0 -1
  568. package/dist/chunks/importer-eUREhBGj.js.map +0 -1
  569. package/dist/chunks/importer-vi3bSdtw.js.map +0 -1
  570. package/dist/chunks/importer-zGLo8Dg_.js.map +0 -1
  571. package/dist/chunks/json-elements-DDS8N2Dd.js +0 -779
  572. package/dist/chunks/json-elements-DDS8N2Dd.js.map +0 -1
  573. package/dist/chunks/ontology-BnrJ4I98.js +0 -113
  574. package/dist/chunks/ontology-BnrJ4I98.js.map +0 -1
  575. package/dist/chunks/records-dbRkxwaq.js.map +0 -1
  576. package/dist/chunks/report-B1z4WT9e.js +0 -2372
  577. package/dist/chunks/report-B1z4WT9e.js.map +0 -1
  578. package/dist/chunks/text-Dr0Ifpag.js.map +0 -1
  579. package/dist/chunks/weights-Dzba96G3.js +0 -176
  580. package/dist/chunks/weights-Dzba96G3.js.map +0 -1
  581. package/dist/chunks/writer-C7flM-Ih.js.map +0 -1
@@ -38,7 +38,7 @@ import {
38
38
  takenIn,
39
39
  uniqueColumnName,
40
40
  } from "../../common/attributes.js";
41
- import { type DeclaredTypeSpec } from "../../common/declared-types.js";
41
+ import { type DeclaredTypeSpec, overflowsToInfinity } from "../../common/declared-types.js";
42
42
  import { DirectionResolver, type EdgeKind } from "../../common/direction.js";
43
43
  import { IdCoercer } from "../../common/ids.js";
44
44
  import { textChunks, throwIfAborted } from "../../common/input.js";
@@ -50,13 +50,16 @@ import {
50
50
  type ResolvedImportOptions,
51
51
  resolveImportOptions,
52
52
  } from "../../common/options.js";
53
+ import { agree, plural } from "../../common/plural.js";
53
54
  import { ImportReportBuilder } from "../../common/report.js";
54
55
  import { isWeightField, parseWeightText } from "../../common/weights.js";
55
56
  import {
56
57
  isWhitespace,
57
58
  localName,
59
+ sniffXmlText,
58
60
  tokenizeXml,
59
61
  xmlDeclaredEncoding,
62
+ XmlEmptyInputError,
60
63
  type XmlHandler,
61
64
  XmlSyntaxError,
62
65
  } from "../../common/xml.js";
@@ -85,9 +88,29 @@ import {
85
88
  import { XmlTreeBuilder } from "./tree.js";
86
89
  import { graphicsDecl, graphicsValues } from "./yfiles.js";
87
90
 
88
- /** The XML attributes the importer reads on `<graph>`, `<node>` and `<edge>`; any other is reported. */
91
+ /** The XML attributes the importer reads on each element; any other is reported (once per element and name). */
89
92
  const GRAPH_ATTRIBUTES: ReadonlySet<string> = new Set(["id", "edgedefault", "parse.nodes", "parse.edges"]);
90
93
  const NODE_ATTRIBUTES: ReadonlySet<string> = new Set(["id"]);
94
+ const KEY_ATTRIBUTES: ReadonlySet<string> = new Set([
95
+ "id",
96
+ "for",
97
+ "attr.name",
98
+ "attr.type",
99
+ "name",
100
+ "type",
101
+ "yfiles.type",
102
+ ]);
103
+ const DATA_ATTRIBUTES: ReadonlySet<string> = new Set(["key"]);
104
+ const HYPEREDGE_ATTRIBUTES: ReadonlySet<string> = new Set(["id"]);
105
+ const ENDPOINT_ATTRIBUTES: ReadonlySet<string> = new Set(["node", "type"]);
106
+ const PORT_ATTRIBUTES: ReadonlySet<string> = new Set(["name"]);
107
+ const NO_ATTRIBUTES: ReadonlySet<string> = new Set();
108
+
109
+ /** Attributes the GraphML schema defines that the importer does not keep: reported as not kept, not as foreign. */
110
+ const SCHEMA_ATTRIBUTES: ReadonlySet<string> = new Set(["id", "port", "parse.order", "parse.nodeids", "parse.edgeids"]);
111
+
112
+ /** Distinct unknown attribute names reported one by one per element kind; the rest are counted in one issue. */
113
+ const MAX_UNKNOWN_ATTRIBUTE_NAMES = 16;
91
114
  const EDGE_ATTRIBUTES: ReadonlySet<string> = new Set([
92
115
  "id",
93
116
  "source",
@@ -97,12 +120,21 @@ const EDGE_ATTRIBUTES: ReadonlySet<string> = new Set([
97
120
  "targetport",
98
121
  ]);
99
122
 
100
- /** The format-specific options of the GraphML importer. */
101
- export interface GraphmlImportOptions {
123
+ /**
124
+ * The format-specific options of the GraphML importer.
125
+ * @category Built-in formats
126
+ */
127
+ export interface GraphmlImportOptions extends CommonImportOptions {
102
128
  /**
103
- * How keys with a `yfiles.type` (yEd / yFiles graphics) are read: "json" (default) keeps the
104
- * nested XML of each `<data>` as a json column with origin.namespace "yfiles"; "skip" reports
105
- * them once and declares nothing.
129
+ * How yEd graphics (keys with a `yfiles.type`) are read. "json" keeps each one as a JSON
130
+ * attribute named after the key (for example, `d0`) that holds its XML as a tree, which the GraphML
131
+ * exporter writes back, and also reads the shapes it knows into plain columns beside it:
132
+ * `yfiles.position`, `yfiles.width`, `yfiles.height`, `yfiles.color`, `yfiles.borderColor`,
133
+ * `yfiles.borderWidth`, `yfiles.label` and `yfiles.shape` for nodes, and the line color, width
134
+ * and arrows for edges. The report's `lossy` list then holds W_GRAPHML_YFILES_JSON, because the
135
+ * XML comes back with the same structure but not byte for byte. "skip" leaves the graphics out,
136
+ * with a W_GRAPHML_YFILES_SKIPPED warning per key.
137
+ * @defaultValue "json"
106
138
  */
107
139
  yfiles?: "json" | "skip" | undefined;
108
140
  }
@@ -149,6 +181,10 @@ const enum Ctx {
149
181
  Data,
150
182
  Capture,
151
183
  Skip,
184
+ /** A `<port>` (ports nest; their names are collected for the edge port check). */
185
+ Port,
186
+ /** Inside a `<graph>` held by an `<edge>`: dropped, its nodes and edges counted as skipped. */
187
+ Dropped,
152
188
  }
153
189
 
154
190
  /** The element domains a key can be declared for. */
@@ -216,6 +252,8 @@ interface NodeState {
216
252
  restoredId: string | null;
217
253
  failed: boolean;
218
254
  readonly line: number;
255
+ /** The keys of the `<data>` read so far, for the duplicate check; null before the first. */
256
+ keys: Set<string> | null;
219
257
  }
220
258
 
221
259
  /** The `<edge>` being read. */
@@ -230,6 +268,13 @@ interface EdgeState {
230
268
  readonly pending: unknown[];
231
269
  failed: boolean;
232
270
  line: number;
271
+ /** The keys of the `<data>` read so far, for the duplicate check; null before the first. */
272
+ keys: Set<string> | null;
273
+ /** The weight key whose value was read, or null. */
274
+ weightKey: string | null;
275
+ /** The endpoint ids as written, for the nested-graph-id check. */
276
+ readonly sourceText: string | null;
277
+ readonly targetText: string | null;
233
278
  }
234
279
 
235
280
  /** One `<endpoint>` of a hyperedge. */
@@ -268,6 +313,12 @@ interface Where {
268
313
  interface GraphState {
269
314
  readonly directed: boolean;
270
315
  readonly parent: number;
316
+ /** The `parse.nodes` / `parse.edges` hints of a top-level graph, checked at its end; null when absent. */
317
+ readonly hints: { readonly nodes: number | null; readonly edges: number | null } | null;
318
+ /** The `<node>` / `<edge>` elements read directly in this graph. */
319
+ nodes: number;
320
+ edges: number;
321
+ readonly line: number;
271
322
  }
272
323
 
273
324
  /**
@@ -334,6 +385,10 @@ class GraphmlReader implements XmlHandler {
334
385
  private readonly declared = new IndexFlags();
335
386
 
336
387
  private readonly edgeIds = new Set<string>();
388
+ /** The keys `<data>` referenced before any `<key>` declared them (E_GRAPHML_UNKNOWN_KEY). */
389
+ private readonly unknownKeyIds = new Set<string>();
390
+ /** The ids of the edges waiting in {@link deferred}. */
391
+ private readonly deferredEdgeIds = new Set<string>();
337
392
 
338
393
  private elementsSinceCheck = 0;
339
394
 
@@ -361,6 +416,11 @@ class GraphmlReader implements XmlHandler {
361
416
 
362
417
  private descText = "";
363
418
 
419
+ private descLine = 0;
420
+
421
+ /** The keys of the graph-level `<data>` read so far, for the duplicate check. */
422
+ private readonly graphKeys: { keys: Set<string> | null } = { keys: null };
423
+
364
424
  private description: string | null = null;
365
425
 
366
426
  private graphSeen = false;
@@ -390,6 +450,34 @@ class GraphmlReader implements XmlHandler {
390
450
 
391
451
  private hubHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
392
452
 
453
+ /** The hub nodes of star-expanded hyperedges, so a later `<node>` of the same id is reported as a clash. */
454
+ private readonly hubs = new IndexFlags();
455
+
456
+ /** Edges and hyperedges whose endpoints were not declared yet under addMissingNodes: false, added at the end. */
457
+ private readonly deferred: (EdgeState | HyperedgeState)[] = [];
458
+
459
+ /** The ids of the `<graph>` elements read so far, for the endpoint-names-a-graph check. */
460
+ private readonly graphIds = new Set<string>();
461
+
462
+ /** How many top-level `<graph>` elements were opened. */
463
+ private topGraphs = 0;
464
+
465
+ /** The namespace of the `<graphml>` root (children in it are GraphML even when it is not GraphML's). */
466
+ private rootNs: string | null = GRAPHML_NAMESPACE;
467
+
468
+ /** Whether a graph-level `<desc>` was kept, so a second one is reported. */
469
+ private graphDescSeen = false;
470
+
471
+ /** Unknown attribute names reported per element kind, and how many more were not listed. */
472
+ private readonly unknownNames = new Map<string, Set<string>>();
473
+
474
+ private readonly unknownOverflow = new Map<string, number>();
475
+
476
+ /** The port names each node declares, and the ports edges reference, checked at the end. */
477
+ private readonly ports = new Map<NodeId, Set<string>>();
478
+
479
+ private readonly portRefs: [NodeId, string][] = [];
480
+
393
481
  /**
394
482
  * Create a reader for one import call.
395
483
  * @param sink - the sink
@@ -418,8 +506,9 @@ class GraphmlReader implements XmlHandler {
418
506
  * @param name - the element name
419
507
  * @param attrs - its attributes
420
508
  * @param line - the line
509
+ * @param ns - the element's namespace (null: an undeclared prefix)
421
510
  */
422
- start(name: string, attrs: ReadonlyMap<string, string>, line: number): void {
511
+ start(name: string, attrs: ReadonlyMap<string, string>, line: number, ns: string | null = ""): void {
423
512
  const parent = this.ctx.length === 0 ? null : this.ctx[this.ctx.length - 1];
424
513
  switch (parent) {
425
514
  case Ctx.Capture:
@@ -433,10 +522,18 @@ class GraphmlReader implements XmlHandler {
433
522
  case Ctx.Default:
434
523
  this.captureStart(name, attrs);
435
524
  return;
525
+ case Ctx.Dropped:
526
+ this.dropStart(localName(name));
527
+ return;
436
528
  default:
437
529
  break;
438
530
  }
439
531
  const local = localName(name);
532
+ if (parent !== null && ns !== GRAPHML_NAMESPACE && ns !== "" && ns !== this.rootNs) {
533
+ // a vendor element whose local name happens to be node, edge or data is not GraphML
534
+ this.unknownElement(name, line);
535
+ return;
536
+ }
440
537
  switch (parent) {
441
538
  case null:
442
539
  if (local !== "graphml") {
@@ -444,6 +541,18 @@ class GraphmlReader implements XmlHandler {
444
541
  line,
445
542
  });
446
543
  }
544
+ if (ns !== GRAPHML_NAMESPACE && ns !== "") {
545
+ this.report.warning(
546
+ "validation-error",
547
+ GRAPHML_ISSUE.NAMESPACE,
548
+ ns === null
549
+ ? `the root <${name}> uses an undeclared prefix; read as GraphML`
550
+ : `the root <${name}> is in the namespace ${JSON.stringify(ns)}, not ${GRAPHML_NAMESPACE}; read as GraphML`,
551
+ { line, element: ns },
552
+ );
553
+ }
554
+ this.rootNs = ns;
555
+ this.reportUnreadAttributes("graphml", attrs, NO_ATTRIBUTES, line);
447
556
  this.readRoot(attrs);
448
557
  this.ctx.push(Ctx.Graphml);
449
558
  return;
@@ -451,7 +560,7 @@ class GraphmlReader implements XmlHandler {
451
560
  this.startInGraphml(name, local, attrs, line);
452
561
  return;
453
562
  case Ctx.Key:
454
- this.startInKey(name, local, line);
563
+ this.startInKey(name, local, attrs, line);
455
564
  return;
456
565
  case Ctx.Graph:
457
566
  this.startInGraph(name, local, attrs, line);
@@ -465,6 +574,9 @@ class GraphmlReader implements XmlHandler {
465
574
  case Ctx.Hyperedge:
466
575
  this.startInHyperedge(name, local, attrs, line);
467
576
  return;
577
+ case Ctx.Port:
578
+ this.startInPort(name, local, attrs, line);
579
+ return;
468
580
  default:
469
581
  this.unknownElement(name, line);
470
582
  }
@@ -483,6 +595,8 @@ class GraphmlReader implements XmlHandler {
483
595
  return;
484
596
  case Ctx.Skip:
485
597
  case Ctx.Graphml:
598
+ case Ctx.Port:
599
+ case Ctx.Dropped:
486
600
  case undefined:
487
601
  return;
488
602
  case Ctx.Desc:
@@ -507,7 +621,7 @@ class GraphmlReader implements XmlHandler {
507
621
  this.finishHyperedge();
508
622
  return;
509
623
  case Ctx.Graph:
510
- this.graphs.pop();
624
+ this.finishGraph();
511
625
  return;
512
626
  default: {
513
627
  const value: never = ctx;
@@ -539,6 +653,7 @@ class GraphmlReader implements XmlHandler {
539
653
  this.descText += text;
540
654
  return;
541
655
  case Ctx.Skip:
656
+ case Ctx.Dropped:
542
657
  return;
543
658
  default:
544
659
  if (!isWhitespace(text)) {
@@ -559,6 +674,24 @@ class GraphmlReader implements XmlHandler {
559
674
  if (!this.graphSeen) {
560
675
  this.report.fail(GRAPHML_ISSUE.NO_GRAPH, "the document has no <graph> element");
561
676
  }
677
+ // GraphML allows an edge before the nodes it joins: what waited for them is added now
678
+ for (const edge of this.deferred) {
679
+ if ("endpoints" in edge) {
680
+ this.expandHyperedge(edge);
681
+ } else {
682
+ this.addEdge(edge);
683
+ }
684
+ }
685
+ this.deferred.length = 0;
686
+ this.checkPorts();
687
+ for (const [kind, more] of this.unknownOverflow) {
688
+ this.report.warning(
689
+ "unsupported",
690
+ GRAPHML_ISSUE.UNKNOWN_XML_ATTRIBUTE,
691
+ `${more} more distinct attribute names on <${kind}> are not GraphML attributes and are not kept (not listed one by one)`,
692
+ { element: kind },
693
+ );
694
+ }
562
695
  const meta: GraphmlMeta = { graphId: this.graphId, edgedefault: this.edgedefault, namespaces: this.namespaces };
563
696
  this.sink.setMeta({
564
697
  sourceFormat: FORMAT,
@@ -601,7 +734,7 @@ class GraphmlReader implements XmlHandler {
601
734
  private startInGraphml(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
602
735
  switch (local) {
603
736
  case "desc":
604
- this.beginDesc("graph");
737
+ this.beginDesc("graph", line);
605
738
  return;
606
739
  case "key":
607
740
  this.beginKey(attrs, line);
@@ -621,14 +754,27 @@ class GraphmlReader implements XmlHandler {
621
754
  * A child of `<key>`.
622
755
  * @param name - the element name
623
756
  * @param local - its local name
757
+ * @param attrs - its attributes
624
758
  * @param line - the line
625
759
  */
626
- private startInKey(name: string, local: string, line: number): void {
760
+ private startInKey(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
627
761
  switch (local) {
628
762
  case "desc":
629
- this.beginDesc("key");
763
+ this.beginDesc("key", line);
630
764
  return;
631
765
  case "default":
766
+ this.reportUnreadAttributes("default", attrs, NO_ATTRIBUTES, line);
767
+ if (
768
+ this.pendingKey !== null &&
769
+ (this.pendingKey.defaultText !== null || this.pendingKey.defaultTree !== undefined)
770
+ ) {
771
+ this.report.warning(
772
+ "validation-error",
773
+ GRAPHML_ISSUE.DUPLICATE_ATTRIBUTE,
774
+ `key "${this.pendingKey.id ?? ""}" has a second <default>; the later one is kept`,
775
+ { line, element: this.pendingKey.id },
776
+ );
777
+ }
632
778
  this.content.key = null;
633
779
  this.content.pendingKey = this.pendingKey;
634
780
  this.content.text = "";
@@ -649,22 +795,32 @@ class GraphmlReader implements XmlHandler {
649
795
  * @param line - the line
650
796
  */
651
797
  private startInGraph(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
798
+ const first = this.graphs.length === 1 && this.topGraphs === 1;
652
799
  switch (local) {
653
800
  case "desc":
654
- if (this.graphs.length === 1) {
655
- this.beginDesc("graph");
801
+ if (first) {
802
+ this.beginDesc("graph", line);
656
803
  } else {
804
+ this.report.warnOnce(
805
+ "unsupported",
806
+ GRAPHML_ISSUE.DESC_DROPPED,
807
+ `the <desc> of a ${this.graphs.length === 1 ? "second top-level" : "nested"} <graph> is not kept`,
808
+ { line },
809
+ `${GRAPHML_ISSUE.DESC_DROPPED}:graph`,
810
+ );
657
811
  this.ctx.push(Ctx.Skip);
658
812
  }
659
813
  return;
660
814
  case "data":
661
- if (this.graphs.length === 1) {
815
+ if (first) {
662
816
  this.beginData(attrs, line, "graph");
663
817
  } else {
664
818
  this.report.warnOnce(
665
819
  "unsupported",
666
820
  GRAPHML_ISSUE.NESTED_GRAPH_DATA,
667
- "data of a nested <graph> cannot be kept; only the top-level graph has attributes",
821
+ this.graphs.length === 1
822
+ ? "data of a second top-level <graph> cannot be kept; the graphs are merged and only the first one's attributes are kept"
823
+ : "data of a nested <graph> cannot be kept; only the top-level graph has attributes",
668
824
  { line },
669
825
  );
670
826
  this.ctx.push(Ctx.Skip);
@@ -723,11 +879,19 @@ class GraphmlReader implements XmlHandler {
723
879
  "<port> declarations are not kept; sourceport / targetport edge attributes are",
724
880
  { line },
725
881
  );
726
- this.ctx.push(Ctx.Skip);
882
+ this.beginPort(attrs, line);
727
883
  return;
728
884
  case "graph": {
729
885
  const node = this.nodes[this.nodes.length - 1];
730
886
  this.materializeNode(node);
887
+ if (node.index === INVALID_INDEX) {
888
+ this.report.error(
889
+ "missing-value",
890
+ GRAPHML_ISSUE.UNKNOWN_PARENT,
891
+ "the nodes of this nested <graph> lose their parent: the node holding it was skipped; they are read as top-level nodes",
892
+ { line },
893
+ );
894
+ }
731
895
  this.beginGraph(attrs, line, node.index);
732
896
  return;
733
897
  }
@@ -768,11 +932,98 @@ class GraphmlReader implements XmlHandler {
768
932
  case "data":
769
933
  this.beginData(attrs, line, "edge");
770
934
  return;
935
+ case "graph":
936
+ this.report.warnOnce(
937
+ "unsupported",
938
+ GRAPHML_ISSUE.EDGE_GRAPH_DROPPED,
939
+ "a <graph> inside an <edge> is not supported; it is dropped with the nodes and edges it holds (counted as skipped)",
940
+ { line, element: attrs.get("id") ?? null },
941
+ );
942
+ this.ctx.push(Ctx.Dropped);
943
+ return;
771
944
  default:
772
945
  this.unknownElement(name, line);
773
946
  }
774
947
  }
775
948
 
949
+ /**
950
+ * An element inside a dropped `<graph>` (one held by an edge): counted as skipped.
951
+ * @param local - its local name
952
+ */
953
+ private dropStart(local: string): void {
954
+ if (local === "node") {
955
+ this.report.counts.skippedNodes++;
956
+ } else if (local === "edge" || local === "hyperedge") {
957
+ this.report.counts.skippedEdges++;
958
+ }
959
+ this.ctx.push(local === "data" || local === "desc" ? Ctx.Skip : Ctx.Dropped);
960
+ }
961
+
962
+ /**
963
+ * Open a `<port>`: its name is kept for the edge port check, its content is skipped.
964
+ * @param attrs - its attributes
965
+ * @param line - the line
966
+ */
967
+ private beginPort(attrs: ReadonlyMap<string, string>, line: number): void {
968
+ this.reportUnreadAttributes("port", attrs, PORT_ATTRIBUTES, line);
969
+ const node = this.nodes[this.nodes.length - 1];
970
+ const name = attrs.get("name");
971
+ if (node.id !== null && name !== undefined) {
972
+ const id = node.restoredId ?? node.id;
973
+ let names = this.ports.get(id);
974
+ if (names === undefined) {
975
+ names = new Set();
976
+ this.ports.set(id, names);
977
+ }
978
+ names.add(name);
979
+ }
980
+ this.ctx.push(Ctx.Port);
981
+ }
982
+
983
+ /**
984
+ * A child of `<port>`: a nested port declares a name too; data and desc are skipped.
985
+ * @param name - the element name
986
+ * @param local - its local name
987
+ * @param attrs - its attributes
988
+ * @param line - the line
989
+ */
990
+ private startInPort(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
991
+ switch (local) {
992
+ case "port":
993
+ this.beginPort(attrs, line);
994
+ return;
995
+ case "data":
996
+ case "desc":
997
+ this.ctx.push(Ctx.Skip);
998
+ return;
999
+ default:
1000
+ this.unknownElement(name, line);
1001
+ }
1002
+ }
1003
+
1004
+ /**
1005
+ * Report the `sourceport` / `targetport` values that name a port their node never declares
1006
+ * (once, with the count), now that every node has been read.
1007
+ */
1008
+ private checkPorts(): void {
1009
+ let dangling = 0;
1010
+ let first: string | null = null;
1011
+ for (const [node, port] of this.portRefs) {
1012
+ if (this.ports.get(node)?.has(port) !== true) {
1013
+ dangling++;
1014
+ first ??= `${String(node)}:${port}`;
1015
+ }
1016
+ }
1017
+ if (dangling > 0) {
1018
+ this.report.warning(
1019
+ "missing-value",
1020
+ GRAPHML_ISSUE.DANGLING_REFERENCE,
1021
+ `${dangling} edge port reference${plural(dangling)} ${agree(dangling, "names", "name")} a port their node does not declare (first: ${first ?? ""}); the values are kept`,
1022
+ { element: first },
1023
+ );
1024
+ }
1025
+ }
1026
+
776
1027
  /**
777
1028
  * A child of `<hyperedge>` (under the star / clique policies).
778
1029
  * @param name - the element name
@@ -783,6 +1034,7 @@ class GraphmlReader implements XmlHandler {
783
1034
  private startInHyperedge(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
784
1035
  switch (local) {
785
1036
  case "endpoint":
1037
+ this.reportUnreadAttributes("endpoint", attrs, ENDPOINT_ATTRIBUTES, line);
786
1038
  this.readEndpoint(attrs, line);
787
1039
  this.ctx.push(Ctx.Skip);
788
1040
  return;
@@ -796,6 +1048,13 @@ class GraphmlReader implements XmlHandler {
796
1048
  this.ctx.push(Ctx.Skip);
797
1049
  return;
798
1050
  case "desc":
1051
+ this.report.warnOnce(
1052
+ "unsupported",
1053
+ GRAPHML_ISSUE.DESC_DROPPED,
1054
+ "the <desc> of a hyperedge is not kept by the star / clique expansions",
1055
+ { line },
1056
+ `${GRAPHML_ISSUE.DESC_DROPPED}:hyperedge`,
1057
+ );
799
1058
  this.ctx.push(Ctx.Skip);
800
1059
  return;
801
1060
  default:
@@ -809,25 +1068,22 @@ class GraphmlReader implements XmlHandler {
809
1068
  * @param line - the line
810
1069
  */
811
1070
  private unknownElement(name: string, line: number): void {
812
- this.report.warnOnce(
813
- "unsupported",
814
- GRAPHML_ISSUE.UNKNOWN_ELEMENT,
815
- `element <${name}> is not GraphML and was skipped`,
816
- {
817
- line,
818
- element: name,
819
- },
820
- );
1071
+ this.report.warnOnce("unsupported", GRAPHML_ISSUE.UNKNOWN_ELEMENT, unknownElementMessage(name), {
1072
+ line,
1073
+ element: name,
1074
+ });
821
1075
  this.ctx.push(Ctx.Skip);
822
1076
  }
823
1077
 
824
1078
  /**
825
1079
  * Open a `<desc>`.
826
1080
  * @param target - what the text describes
1081
+ * @param line - the line
827
1082
  */
828
- private beginDesc(target: "graph" | "key"): void {
1083
+ private beginDesc(target: "graph" | "key", line: number): void {
829
1084
  this.descTarget = target;
830
1085
  this.descText = "";
1086
+ this.descLine = line;
831
1087
  this.ctx.push(Ctx.Desc);
832
1088
  }
833
1089
 
@@ -837,6 +1093,16 @@ class GraphmlReader implements XmlHandler {
837
1093
  if (this.descTarget === "key" && this.pendingKey !== null) {
838
1094
  this.pendingKey.desc = text;
839
1095
  } else if (this.descTarget === "graph" && text.length > 0) {
1096
+ if (this.graphDescSeen) {
1097
+ // a <graphml> and a <graph> description: the graph has one description field
1098
+ this.report.warning(
1099
+ "unsupported",
1100
+ GRAPHML_ISSUE.DESC_DROPPED,
1101
+ "a second graph-level <desc> replaces the first; the earlier text is not kept",
1102
+ { line: this.descLine },
1103
+ );
1104
+ }
1105
+ this.graphDescSeen = true;
840
1106
  this.description = text;
841
1107
  }
842
1108
  this.descTarget = null;
@@ -847,8 +1113,10 @@ class GraphmlReader implements XmlHandler {
847
1113
 
848
1114
  /**
849
1115
  * Report, once per element kind and attribute name, an XML attribute the importer does not read:
850
- * a GraphML `parse.*` hint as ignored, anything else as unknown. Namespace declarations and
851
- * `xml:` / `xsi:` attributes are not data and pass silently.
1116
+ * a GraphML `parse.*` hint as ignored, anything else as not kept. Namespace declarations and
1117
+ * `xml:` / `xsi:` attributes are not data and pass silently. Beyond MAX_UNKNOWN_ATTRIBUTE_NAMES
1118
+ * distinct names per element kind the rest are counted and reported once at the end, so a file
1119
+ * with thousands of vendor attributes does not grow the report without bound.
852
1120
  * @param kind - the element name
853
1121
  * @param attrs - the element's attributes
854
1122
  * @param known - the attributes the importer reads on that element
@@ -860,21 +1128,39 @@ class GraphmlReader implements XmlHandler {
860
1128
  known: ReadonlySet<string>,
861
1129
  line: number,
862
1130
  ): void {
1131
+ if (attrs.size === 0) {
1132
+ return;
1133
+ }
863
1134
  for (const name of attrs.keys()) {
864
1135
  if (known.has(name) || name === "xmlns" || /^(xmlns|xml|xsi):/.test(name)) {
865
1136
  continue;
866
1137
  }
867
1138
  const hint = name.startsWith("parse.");
868
1139
  const code = hint ? GRAPHML_ISSUE.PARSE_HINT_IGNORED : GRAPHML_ISSUE.UNKNOWN_XML_ATTRIBUTE;
869
- this.report.warnOnce(
870
- "unsupported",
871
- code,
872
- hint
873
- ? `the <${kind}> parse hint ${name} is ignored`
874
- : `the <${kind}> attribute ${name} is not a GraphML attribute; it is not kept`,
875
- { line, element: name },
876
- `${code}:${kind}:${name}`,
877
- );
1140
+ let names = this.unknownNames.get(kind);
1141
+ if (names === undefined) {
1142
+ names = new Set();
1143
+ this.unknownNames.set(kind, names);
1144
+ }
1145
+ if (names.has(name)) {
1146
+ continue;
1147
+ }
1148
+ if (names.size >= MAX_UNKNOWN_ATTRIBUTE_NAMES && !hint) {
1149
+ this.unknownOverflow.set(kind, (this.unknownOverflow.get(kind) ?? 0) + 1);
1150
+ }
1151
+ names.add(name);
1152
+ if (names.size > MAX_UNKNOWN_ATTRIBUTE_NAMES && !hint) {
1153
+ continue;
1154
+ }
1155
+ let message: string;
1156
+ if (hint) {
1157
+ message = `the <${kind}> parse hint ${name} is ignored`;
1158
+ } else if (SCHEMA_ATTRIBUTES.has(name)) {
1159
+ message = `the <${kind}> attribute ${name} is not kept`;
1160
+ } else {
1161
+ message = `the <${kind}> attribute ${name} is not a GraphML attribute; it is not kept`;
1162
+ }
1163
+ this.report.warning("unsupported", code, message, { line, element: name });
878
1164
  }
879
1165
  }
880
1166
 
@@ -888,17 +1174,29 @@ class GraphmlReader implements XmlHandler {
888
1174
  private beginGraph(attrs: ReadonlyMap<string, string>, line: number, parent: number): void {
889
1175
  this.reportUnreadAttributes("graph", attrs, GRAPH_ATTRIBUTES, line);
890
1176
  const top = this.graphs.length === 0;
1177
+ if (top) {
1178
+ this.topGraphs++;
1179
+ }
1180
+ const graphId = attrs.get("id");
1181
+ if (graphId !== undefined && graphId.length > 0) {
1182
+ this.graphIds.add(graphId);
1183
+ }
891
1184
  const edgedefault = attrs.get("edgedefault");
892
1185
  let directed: boolean;
893
1186
  if (edgedefault === "directed" || edgedefault === "undirected") {
894
1187
  directed = edgedefault === "directed";
895
1188
  } else if (edgedefault === undefined) {
896
1189
  directed = top ? this.options.defaultDirected : this.graphs[this.graphs.length - 1].directed;
897
- this.report.warning(
1190
+ // once per kind: a file of thousands of nested graphs without edgedefault is one finding
1191
+ const direction = directed ? "directed" : "undirected";
1192
+ this.report.warnOnce(
898
1193
  "validation-error",
899
1194
  GRAPHML_ISSUE.EDGEDEFAULT_MISSING,
900
- `<graph> has no edgedefault; read as ${directed ? "directed" : "undirected"}`,
1195
+ top
1196
+ ? `<graph> has no edgedefault; read as ${direction}`
1197
+ : `a nested <graph> has no edgedefault; it takes its container's (${direction})`,
901
1198
  { line },
1199
+ `${GRAPHML_ISSUE.EDGEDEFAULT_MISSING}:${top ? "top" : "nested"}`,
902
1200
  );
903
1201
  } else {
904
1202
  directed = top ? this.options.defaultDirected : this.graphs[this.graphs.length - 1].directed;
@@ -909,6 +1207,7 @@ class GraphmlReader implements XmlHandler {
909
1207
  { line },
910
1208
  );
911
1209
  }
1210
+ let hints: GraphState["hints"] = null;
912
1211
  if (top && this.graphSeen) {
913
1212
  this.report.warnOnce(
914
1213
  "unsupported",
@@ -916,6 +1215,17 @@ class GraphmlReader implements XmlHandler {
916
1215
  "the document holds more than one top-level <graph>; their nodes and edges are merged into one graph",
917
1216
  { line, element: attrs.get("id") ?? null },
918
1217
  );
1218
+ for (const name of ["parse.nodes", "parse.edges"]) {
1219
+ if (attrs.has(name)) {
1220
+ this.report.warnOnce(
1221
+ "unsupported",
1222
+ GRAPHML_ISSUE.PARSE_HINT_IGNORED,
1223
+ `the ${name} hint of a second top-level <graph> is ignored`,
1224
+ { line, element: name },
1225
+ `${GRAPHML_ISSUE.PARSE_HINT_IGNORED}:second:${name}`,
1226
+ );
1227
+ }
1228
+ }
919
1229
  } else if (top) {
920
1230
  this.graphSeen = true;
921
1231
  this.graphId = attrs.get("id") ?? null;
@@ -923,6 +1233,7 @@ class GraphmlReader implements XmlHandler {
923
1233
  this.direction.setHeader(directed, { line });
924
1234
  const nodes = this.hint(attrs.get("parse.nodes"), "parse.nodes", line);
925
1235
  const edges = this.hint(attrs.get("parse.edges"), "parse.edges", line);
1236
+ hints = nodes === null && edges === null ? null : { nodes, edges };
926
1237
  if (nodes !== null || edges !== null) {
927
1238
  try {
928
1239
  this.sink.reserve(nodes ?? undefined, edges ?? undefined);
@@ -941,10 +1252,31 @@ class GraphmlReader implements XmlHandler {
941
1252
  }
942
1253
  }
943
1254
  }
944
- this.graphs.push({ directed, parent });
1255
+ this.graphs.push({ directed, parent, hints, nodes: 0, edges: 0, line });
945
1256
  this.ctx.push(Ctx.Graph);
946
1257
  }
947
1258
 
1259
+ /** Close a `<graph>`: a top-level graph's count hints are checked against what it held. */
1260
+ private finishGraph(): void {
1261
+ const graph = this.graphs.pop();
1262
+ if (graph?.hints === null || graph === undefined) {
1263
+ return;
1264
+ }
1265
+ for (const [name, hint, found] of [
1266
+ ["parse.nodes", graph.hints.nodes, graph.nodes],
1267
+ ["parse.edges", graph.hints.edges, graph.edges],
1268
+ ] as const) {
1269
+ if (hint !== null && hint !== found) {
1270
+ this.report.warning(
1271
+ "validation-error",
1272
+ GRAPHML_ISSUE.COUNT_MISMATCH,
1273
+ `<graph ${name}="${hint}"> but the graph holds ${found} ${name === "parse.nodes" ? "<node>" : "<edge>"} elements`,
1274
+ { line: graph.line, element: name },
1275
+ );
1276
+ }
1277
+ }
1278
+ }
1279
+
948
1280
  /**
949
1281
  * A `parse.nodes` / `parse.edges` hint the sink can reserve; anything else is reported and ignored.
950
1282
  * @param text - the attribute text, or undefined
@@ -977,7 +1309,17 @@ class GraphmlReader implements XmlHandler {
977
1309
  * @param line - the line
978
1310
  */
979
1311
  private beginKey(attrs: ReadonlyMap<string, string>, line: number): void {
1312
+ // attr.list (a list extension of some writers) and yEd's yfiles.foldertype are reported here
1313
+ this.reportUnreadAttributes("key", attrs, KEY_ATTRIBUTES, line);
980
1314
  const id = attrs.get("id") ?? null;
1315
+ if (id !== null && this.unknownKeyIds.delete(id)) {
1316
+ this.report.warning(
1317
+ "missing-value",
1318
+ GRAPHML_ISSUE.KEY_DECLARED_LATE,
1319
+ `key "${id}" is declared after data that uses it; those values were dropped (declare <key> before <graph>)`,
1320
+ { line, element: id },
1321
+ );
1322
+ }
981
1323
  const forText = attrs.get("for") ?? "all";
982
1324
  let domains: readonly Domain[];
983
1325
  switch (forText) {
@@ -1067,6 +1409,8 @@ class GraphmlReader implements XmlHandler {
1067
1409
  continue;
1068
1410
  }
1069
1411
  if (domain === "edge" && !yfiles && isWeightField(name, this.options.weightFrom)) {
1412
+ // networkx writes one weight key per value type (long and double): every key of
1413
+ // that name is the weight; two of them on one edge are reported in finishData
1070
1414
  weight = true;
1071
1415
  this.weightOrigin ??= { id: key.id, title: key.attrName, type: key.attrType };
1072
1416
  continue;
@@ -1245,8 +1589,12 @@ class GraphmlReader implements XmlHandler {
1245
1589
  * @param domain - the domain of the element holding it
1246
1590
  */
1247
1591
  private beginData(attrs: ReadonlyMap<string, string>, line: number, domain: Domain): void {
1592
+ this.reportUnreadAttributes("data", attrs, DATA_ATTRIBUTES, line);
1248
1593
  const keyId = attrs.get("key");
1249
1594
  const { content } = this;
1595
+ if (keyId !== undefined) {
1596
+ this.checkDuplicateData(keyId, domain, line);
1597
+ }
1250
1598
  content.key = null;
1251
1599
  content.pendingKey = null;
1252
1600
  content.domain = domain;
@@ -1261,6 +1609,7 @@ class GraphmlReader implements XmlHandler {
1261
1609
  const entries = this.keys.get(keyId);
1262
1610
  const key = entries?.find((entry) => entry.domains.includes(domain)) ?? entries?.[0];
1263
1611
  if (key === undefined) {
1612
+ this.unknownKeyIds.add(keyId);
1264
1613
  this.report.error(
1265
1614
  "validation-error",
1266
1615
  GRAPHML_ISSUE.UNKNOWN_KEY,
@@ -1281,6 +1630,36 @@ class GraphmlReader implements XmlHandler {
1281
1630
  this.ctx.push(Ctx.Data);
1282
1631
  }
1283
1632
 
1633
+ /**
1634
+ * Report a second `<data>` of one key on one element; the later value is kept (it overwrites).
1635
+ * @param keyId - the key
1636
+ * @param domain - the domain of the element holding it
1637
+ * @param line - the line
1638
+ */
1639
+ private checkDuplicateData(keyId: string, domain: Domain, line: number): void {
1640
+ let holder: { keys: Set<string> | null } | null = null;
1641
+ if (domain === "node") {
1642
+ holder = this.nodes.at(-1) ?? null;
1643
+ } else if (domain === "edge") {
1644
+ holder = this.edge;
1645
+ } else {
1646
+ holder = this.graphKeys;
1647
+ }
1648
+ if (holder === null) {
1649
+ return;
1650
+ }
1651
+ holder.keys ??= new Set();
1652
+ if (holder.keys.has(keyId)) {
1653
+ this.report.warning(
1654
+ "validation-error",
1655
+ GRAPHML_ISSUE.DUPLICATE_ATTRIBUTE,
1656
+ `a second <data key="${keyId}"> on one ${domain}; the later value is kept`,
1657
+ { line, element: keyId },
1658
+ );
1659
+ }
1660
+ holder.keys.add(keyId);
1661
+ }
1662
+
1284
1663
  /**
1285
1664
  * A child element inside `<data>` / `<default>`: the content becomes a tree.
1286
1665
  * @param name - the element name
@@ -1337,9 +1716,18 @@ class GraphmlReader implements XmlHandler {
1337
1716
  case "node": {
1338
1717
  const node = this.nodes[this.nodes.length - 1];
1339
1718
  if (key.originalId) {
1340
- if (typeof value === "string") {
1341
- node.restoredId = value;
1719
+ if (typeof value !== "string" || node.index !== INVALID_INDEX) {
1720
+ this.report.warning(
1721
+ "validation-error",
1722
+ GRAPHML_ISSUE.ORIGINAL_ID_IGNORED,
1723
+ typeof value === "string"
1724
+ ? `the ${ORIGINAL_ID_ATTRIBUTE} value comes after the node's nested <graph>, which added the node under its written id; it is not restored`
1725
+ : `the ${ORIGINAL_ID_ATTRIBUTE} value holds elements, not text; it is not restored`,
1726
+ where,
1727
+ );
1728
+ return;
1342
1729
  }
1730
+ node.restoredId = value;
1343
1731
  return;
1344
1732
  }
1345
1733
  if (key.yfiles) {
@@ -1371,8 +1759,18 @@ class GraphmlReader implements XmlHandler {
1371
1759
  );
1372
1760
  return;
1373
1761
  }
1762
+ if (edge.weightKey !== null && edge.weightKey !== key.id) {
1763
+ this.report.warning(
1764
+ "validation-error",
1765
+ GRAPHML_ISSUE.DUPLICATE_ATTRIBUTE,
1766
+ `the edge has values of two weight keys ("${edge.weightKey}" and "${key.id}"); the later one is kept`,
1767
+ where,
1768
+ );
1769
+ }
1770
+ edge.weightKey = key.id;
1374
1771
  try {
1375
- edge.weight = parseWeightText(value);
1772
+ edge.weight = parseWeightText(value, this.report);
1773
+ this.checkOverflow(value, edge.weight, key.name, where);
1376
1774
  } catch (err) {
1377
1775
  this.skipEdge(edge);
1378
1776
  this.report.recordError(err, where);
@@ -1439,7 +1837,18 @@ class GraphmlReader implements XmlHandler {
1439
1837
  */
1440
1838
  private graphics(domain: "node" | "edge", key: KeyEntry, tree: unknown, where: Where): [ColumnHandle, unknown][] {
1441
1839
  const out: [ColumnHandle, unknown][] = [];
1442
- for (const [field, value] of graphicsValues(domain, tree)) {
1840
+ const unmapped: string[] = [];
1841
+ const values = graphicsValues(domain, tree, unmapped);
1842
+ for (const field of unmapped) {
1843
+ this.report.warnOnce(
1844
+ "validation-error",
1845
+ GRAPHML_ISSUE.YFILES_VALUE,
1846
+ `a yFiles ${domain} ${field} that is not a number is not mapped to its yfiles column (the graphics tree is kept)`,
1847
+ where,
1848
+ `${GRAPHML_ISSUE.YFILES_VALUE}:${domain}:${field}`,
1849
+ );
1850
+ }
1851
+ for (const [field, value] of values) {
1443
1852
  const id = `${domain}:${field}`;
1444
1853
  let handle = this.graphicsColumns.get(id);
1445
1854
  if (handle === undefined) {
@@ -1504,6 +1913,7 @@ class GraphmlReader implements XmlHandler {
1504
1913
  { line: where.line, element: target.name },
1505
1914
  );
1506
1915
  }
1916
+ this.checkOverflow(value, parsed, target.name, where);
1507
1917
  return parsed;
1508
1918
  } catch (err) {
1509
1919
  this.report.recordError(err, where);
@@ -1511,6 +1921,25 @@ class GraphmlReader implements XmlHandler {
1511
1921
  }
1512
1922
  }
1513
1923
 
1924
+ /**
1925
+ * Report (once per column) a finite number text too large for a double, stored as Infinity.
1926
+ * @param text - the value text
1927
+ * @param value - the parsed value
1928
+ * @param column - the column name
1929
+ * @param where - the location
1930
+ */
1931
+ private checkOverflow(text: string, value: unknown, column: string, where: Where): void {
1932
+ if (typeof value === "number" && overflowsToInfinity(text)) {
1933
+ this.report.warnOnce(
1934
+ "precision",
1935
+ PRECISION_CODE,
1936
+ `values of "${column}" beyond the double range (${text.trim()}) are stored as Infinity`,
1937
+ { line: where.line, element: column },
1938
+ `${PRECISION_CODE}:overflow:${column}`,
1939
+ );
1940
+ }
1941
+ }
1942
+
1514
1943
  // ------------------------------------------------------------------ nodes
1515
1944
 
1516
1945
  /**
@@ -1522,6 +1951,7 @@ class GraphmlReader implements XmlHandler {
1522
1951
  private beginNode(attrs: ReadonlyMap<string, string>, line: number): void {
1523
1952
  this.reportUnreadAttributes("node", attrs, NODE_ATTRIBUTES, line);
1524
1953
  const graph = this.graphs[this.graphs.length - 1];
1954
+ graph.nodes++;
1525
1955
  const state: NodeState = {
1526
1956
  id: null,
1527
1957
  idText: null,
@@ -1531,6 +1961,7 @@ class GraphmlReader implements XmlHandler {
1531
1961
  restoredId: null,
1532
1962
  failed: false,
1533
1963
  line,
1964
+ keys: null,
1534
1965
  };
1535
1966
  const idText = attrs.get("id");
1536
1967
  if (idText === undefined || idText.length === 0) {
@@ -1569,7 +2000,14 @@ class GraphmlReader implements XmlHandler {
1569
2000
  try {
1570
2001
  const existing = this.sink.indexOf(id);
1571
2002
  const index = this.sink.addNode(id);
1572
- if (existing !== INVALID_INDEX && this.declared.has(index)) {
2003
+ if (existing !== INVALID_INDEX && this.hubs.has(index)) {
2004
+ this.report.warning(
2005
+ "validation-error",
2006
+ GRAPHML_ISSUE.HUB_ID_CLASH,
2007
+ `node "${String(id)}" has the id of the hub a star-expanded hyperedge created earlier; the node is merged into the hub`,
2008
+ { line: node.line, element: String(id) },
2009
+ );
2010
+ } else if (existing !== INVALID_INDEX && this.declared.has(index)) {
1573
2011
  this.report.warning(
1574
2012
  "validation-error",
1575
2013
  GRAPHML_ISSUE.DUPLICATE_NODE,
@@ -1644,10 +2082,15 @@ class GraphmlReader implements XmlHandler {
1644
2082
  private beginEdge(attrs: ReadonlyMap<string, string>, line: number): void {
1645
2083
  this.reportUnreadAttributes("edge", attrs, EDGE_ATTRIBUTES, line);
1646
2084
  const graph = this.graphs[this.graphs.length - 1];
2085
+ graph.edges++;
2086
+ const idText = attrs.get("id");
2087
+ const sourceText = attrs.get("source");
2088
+ const targetText = attrs.get("target");
1647
2089
  const edge: EdgeState = {
1648
2090
  source: null,
1649
2091
  target: null,
1650
- id: attrs.get("id") ?? null,
2092
+ // an exporter that writes id="" for an edge without one: the same as no id
2093
+ id: idText === undefined || idText.length === 0 ? null : idText,
1651
2094
  kind: graph.directed ? "directed" : "undirected",
1652
2095
  sourcePort: attrs.get("sourceport") ?? null,
1653
2096
  targetPort: attrs.get("targetport") ?? null,
@@ -1655,10 +2098,12 @@ class GraphmlReader implements XmlHandler {
1655
2098
  pending: [],
1656
2099
  failed: false,
1657
2100
  line,
2101
+ keys: null,
2102
+ weightKey: null,
2103
+ sourceText: sourceText ?? null,
2104
+ targetText: targetText ?? null,
1658
2105
  };
1659
2106
  const where = { line, element: edge.id };
1660
- const sourceText = attrs.get("source");
1661
- const targetText = attrs.get("target");
1662
2107
  if (sourceText === undefined || sourceText.length === 0) {
1663
2108
  this.skipEdge(edge);
1664
2109
  this.report.error("missing-value", GRAPHML_ISSUE.MISSING_ENDPOINT, "<edge> without a source", where);
@@ -1678,16 +2123,17 @@ class GraphmlReader implements XmlHandler {
1678
2123
  }
1679
2124
  const directedText = attrs.get("directed");
1680
2125
  if (directedText !== undefined) {
1681
- if (directedText === "true") {
2126
+ // the schema types directed as xs:boolean: true, false, 1 or 0
2127
+ if (directedText === "true" || directedText === "1") {
1682
2128
  edge.kind = "directed";
1683
- } else if (directedText === "false") {
2129
+ } else if (directedText === "false" || directedText === "0") {
1684
2130
  edge.kind = "undirected";
1685
2131
  } else {
1686
2132
  this.skipEdge(edge);
1687
2133
  this.report.error(
1688
2134
  "validation-error",
1689
2135
  GRAPHML_ISSUE.INVALID_DIRECTED,
1690
- `directed="${directedText}" is neither true nor false`,
2136
+ `directed="${directedText}" is neither true nor false (nor 1 or 0)`,
1691
2137
  where,
1692
2138
  );
1693
2139
  }
@@ -1709,6 +2155,31 @@ class GraphmlReader implements XmlHandler {
1709
2155
  this.skipEdge(edge);
1710
2156
  return;
1711
2157
  }
2158
+ if (
2159
+ (!this.options.addMissingNodes &&
2160
+ (this.sink.indexOf(edge.source) === INVALID_INDEX ||
2161
+ this.sink.indexOf(edge.target) === INVALID_INDEX)) ||
2162
+ (edge.id !== null && this.deferredEdgeIds.has(edge.id))
2163
+ ) {
2164
+ // GraphML allows nodes and edges in any order: wait for the end of the document. An
2165
+ // edge reusing a waiting edge's id waits behind it, so the first one keeps the id.
2166
+ if (edge.id !== null) {
2167
+ this.deferredEdgeIds.add(edge.id);
2168
+ }
2169
+ this.deferred.push(edge);
2170
+ return;
2171
+ }
2172
+ this.addEdge(edge);
2173
+ }
2174
+
2175
+ /**
2176
+ * Push an edge through the direction resolver and write its values.
2177
+ * @param edge - the edge, its endpoints known
2178
+ */
2179
+ private addEdge(edge: EdgeState): void {
2180
+ if (edge.source === null || edge.target === null) {
2181
+ return;
2182
+ }
1712
2183
  const element = edge.id ?? `${String(edge.source)} -> ${String(edge.target)}`;
1713
2184
  const where = { line: edge.line, element };
1714
2185
  try {
@@ -1716,6 +2187,8 @@ class GraphmlReader implements XmlHandler {
1716
2187
  this.requireNode(edge.source);
1717
2188
  this.requireNode(edge.target);
1718
2189
  }
2190
+ this.checkGraphEndpoint(edge.source, edge.sourceText, where);
2191
+ this.checkGraphEndpoint(edge.target, edge.targetText, where);
1719
2192
  if (edge.id !== null && this.edgeIds.has(edge.id)) {
1720
2193
  throw new GraphFormatError("E_DUPLICATE_EDGE_ID", `edge id "${edge.id}" is used twice`, {
1721
2194
  id: edge.id,
@@ -1732,9 +2205,11 @@ class GraphmlReader implements XmlHandler {
1732
2205
  }
1733
2206
  if (edge.sourcePort !== null) {
1734
2207
  this.sink.setEdgeValue(this.portColumn("source"), e, edge.sourcePort);
2208
+ this.portRefs.push([edge.source, edge.sourcePort]);
1735
2209
  }
1736
2210
  if (edge.targetPort !== null) {
1737
2211
  this.sink.setEdgeValue(this.portColumn("target"), e, edge.targetPort);
2212
+ this.portRefs.push([edge.target, edge.targetPort]);
1738
2213
  }
1739
2214
  const { pending } = edge;
1740
2215
  for (let i = 0; i < pending.length; i += 2) {
@@ -1757,6 +2232,24 @@ class GraphmlReader implements XmlHandler {
1757
2232
  }
1758
2233
  }
1759
2234
 
2235
+ /**
2236
+ * Report an endpoint that names a nested `<graph>` rather than a node (a node of that id is
2237
+ * then created under addMissingNodes).
2238
+ * @param id - the endpoint id
2239
+ * @param text - the endpoint as written, or null
2240
+ * @param where - the edge's location
2241
+ */
2242
+ private checkGraphEndpoint(id: NodeId, text: string | null, where: Where): void {
2243
+ if (text !== null && this.graphIds.has(text) && this.sink.indexOf(id) === INVALID_INDEX) {
2244
+ this.report.warning(
2245
+ "validation-error",
2246
+ GRAPHML_ISSUE.GRAPH_ENDPOINT,
2247
+ `edge endpoint "${text}" names a <graph>, not a node; a node "${text}" is created`,
2248
+ where,
2249
+ );
2250
+ }
2251
+ }
2252
+
1760
2253
  /**
1761
2254
  * E_UNKNOWN_NODE when an endpoint was never declared (addMissingNodes false enforced by the
1762
2255
  * importer, whatever the sink allows).
@@ -1826,6 +2319,7 @@ class GraphmlReader implements XmlHandler {
1826
2319
  * @param line - the line
1827
2320
  */
1828
2321
  private beginHyperedge(attrs: ReadonlyMap<string, string>, line: number): void {
2322
+ this.reportUnreadAttributes("hyperedge", attrs, HYPEREDGE_ATTRIBUTES, line);
1829
2323
  const id = attrs.get("id") ?? null;
1830
2324
  const where = { line, element: id };
1831
2325
  switch (this.options.hyperedges) {
@@ -1908,12 +2402,52 @@ class GraphmlReader implements XmlHandler {
1908
2402
  if (hyperedge.failed) {
1909
2403
  return;
1910
2404
  }
2405
+ if (hyperedge.endpoints.length === 0) {
2406
+ this.skipEdge(hyperedge);
2407
+ this.report.error(
2408
+ "missing-value",
2409
+ GRAPHML_ISSUE.HYPEREDGE_ENDPOINT,
2410
+ "<hyperedge> without endpoints yields no edge",
2411
+ { line: hyperedge.line, element: hyperedge.id },
2412
+ );
2413
+ return;
2414
+ }
2415
+ if (
2416
+ !this.options.addMissingNodes &&
2417
+ hyperedge.endpoints.some((endpoint) => this.sink.indexOf(endpoint.node) === INVALID_INDEX)
2418
+ ) {
2419
+ this.deferred.push(hyperedge);
2420
+ return;
2421
+ }
2422
+ this.expandHyperedge(hyperedge);
2423
+ }
2424
+
2425
+ /**
2426
+ * Expand a hyperedge to a star or a clique; under addMissingNodes false every endpoint must be
2427
+ * a declared node.
2428
+ * @param hyperedge - the hyperedge, its endpoints read
2429
+ */
2430
+ private expandHyperedge(hyperedge: HyperedgeState): void {
1911
2431
  const where = { line: hyperedge.line, element: hyperedge.id };
1912
2432
  try {
2433
+ for (const endpoint of hyperedge.endpoints) {
2434
+ if (!this.options.addMissingNodes) {
2435
+ this.requireNode(endpoint.node);
2436
+ }
2437
+ }
2438
+ for (const node of new Set(hyperedge.endpoints.map((endpoint) => endpoint.node))) {
2439
+ this.countEndpoints(node, node);
2440
+ }
1913
2441
  if (this.options.hyperedges === "star") {
1914
2442
  this.expandStar(hyperedge, where);
1915
- } else {
1916
- this.expandClique(hyperedge, where);
2443
+ } else if (this.expandClique(hyperedge, where) === 0) {
2444
+ this.skipEdge(hyperedge);
2445
+ this.report.error(
2446
+ "validation-error",
2447
+ GRAPHML_ISSUE.HYPEREDGE_ENDPOINT,
2448
+ "no two endpoints of this hyperedge can be joined (all in, all out, or only one); it yields no edge",
2449
+ where,
2450
+ );
1917
2451
  }
1918
2452
  } catch (err) {
1919
2453
  this.skipEdge(hyperedge);
@@ -1936,6 +2470,7 @@ class GraphmlReader implements XmlHandler {
1936
2470
  }
1937
2471
  const hub = this.sink.addNode(hubId);
1938
2472
  this.declared.add(hub);
2473
+ this.hubs.add(hub);
1939
2474
  this.report.counts.nodes++;
1940
2475
  if (this.hubHandle === INVALID_INDEX) {
1941
2476
  this.hubHandle = this.sink.declareNodeColumn({
@@ -1972,9 +2507,11 @@ class GraphmlReader implements XmlHandler {
1972
2507
  * joined undirected to every other endpoint; two endpoints of the same direction are not joined.
1973
2508
  * @param hyperedge - the hyperedge
1974
2509
  * @param where - the location for issues
2510
+ * @returns the number of edge elements added
1975
2511
  */
1976
- private expandClique(hyperedge: HyperedgeState, where: Where): void {
2512
+ private expandClique(hyperedge: HyperedgeState, where: Where): number {
1977
2513
  const { endpoints } = hyperedge;
2514
+ let added = 0;
1978
2515
  for (let i = 0; i < endpoints.length; i++) {
1979
2516
  for (let j = i + 1; j < endpoints.length; j++) {
1980
2517
  const a = endpoints[i];
@@ -1989,9 +2526,11 @@ class GraphmlReader implements XmlHandler {
1989
2526
  } else {
1990
2527
  continue;
1991
2528
  }
2529
+ added++;
1992
2530
  this.report.counts.edges += this.sink.edgeCount - before;
1993
2531
  }
1994
2532
  }
2533
+ return added;
1995
2534
  }
1996
2535
 
1997
2536
  // ------------------------------------------------------------------ options and ids
@@ -2099,12 +2638,13 @@ function resolveYfiles(options: GraphmlImportOptions | undefined): "json" | "ski
2099
2638
  * or when the head does not start with markup
2100
2639
  */
2101
2640
  function sniffGraphml(head: Uint8Array): number {
2102
- const text = new TextDecoder("utf-8", { fatal: false }).decode(head.subarray(0, SNIFF_BYTES));
2641
+ const text = sniffXmlText(head.subarray(0, SNIFF_BYTES));
2103
2642
  // Only a document that starts as markup: a JSON or CSV value may mention a `<graphml>` tag.
2104
2643
  if (!/^\uFEFF?\s*</.test(text)) {
2105
2644
  return 0;
2106
2645
  }
2107
- const root = text.indexOf("<graphml");
2646
+ // the root may carry a namespace prefix (<g:graphml xmlns:g="...">)
2647
+ const root = text.search(/<([\w.-]+:)?graphml[\s>/]/);
2108
2648
  if (root >= 0) {
2109
2649
  return text.includes(GRAPHML_NAMESPACE, root) ? 1 : 0.9;
2110
2650
  }
@@ -2130,8 +2670,14 @@ async function importGraphml(
2130
2670
  reportUnusedOptions(options, report, USED_OPTIONS);
2131
2671
  const reader = new GraphmlReader(sink, report, common, yfilesMode);
2132
2672
  try {
2133
- await tokenizeXml(textChunks(input, report, { ...common, declaredEncoding: xmlDeclaredEncoding }), reader);
2673
+ await tokenizeXml(
2674
+ textChunks(input, report, { ...common, declaredEncoding: xmlDeclaredEncoding, xml: true }),
2675
+ reader,
2676
+ );
2134
2677
  } catch (err) {
2678
+ if (err instanceof XmlEmptyInputError) {
2679
+ report.fail(GRAPHML_ISSUE.EMPTY_INPUT, "the input is empty (no markup at all)", { line: err.line });
2680
+ }
2135
2681
  if (err instanceof XmlSyntaxError) {
2136
2682
  report.fail(GRAPHML_ISSUE.XML_SYNTAX, err.message, { line: err.line });
2137
2683
  }
@@ -2142,11 +2688,45 @@ async function importGraphml(
2142
2688
  return report.finish();
2143
2689
  }
2144
2690
 
2145
- /** The GraphML importer (design section 8.4). */
2691
+ /**
2692
+ * The GraphML importer.
2693
+ * @category Built-in formats
2694
+ */
2146
2695
  export const graphmlImporter: GraphImporter<GraphmlImportOptions> = Object.freeze({
2147
2696
  format: FORMAT,
2697
+ options: Object.freeze(["yfiles"]),
2148
2698
  extensions: EXTENSIONS,
2149
2699
  mimeTypes: MIME_TYPES,
2150
2700
  sniff: sniffGraphml,
2151
2701
  import: importGraphml,
2152
2702
  });
2703
+
2704
+ /** The elements GraphML defines, for telling a misplaced one from a foreign one. */
2705
+ const GRAPHML_ELEMENTS = new Set([
2706
+ "graphml",
2707
+ "key",
2708
+ "default",
2709
+ "graph",
2710
+ "node",
2711
+ "edge",
2712
+ "hyperedge",
2713
+ "endpoint",
2714
+ "port",
2715
+ "data",
2716
+ "desc",
2717
+ "locator",
2718
+ ]);
2719
+
2720
+ /**
2721
+ * The message of an element skipped where it stands.
2722
+ * @param name - the element name
2723
+ * @returns a message that says where a GraphML element belongs, or that a foreign one is not GraphML
2724
+ */
2725
+ function unknownElementMessage(name: string): string {
2726
+ if (name === "key") {
2727
+ return "element <key> is only allowed as a child of <graphml>, before the first <graph>; it was skipped";
2728
+ }
2729
+ return GRAPHML_ELEMENTS.has(name)
2730
+ ? `element <${name}> is not allowed at this place in a GraphML file and was skipped`
2731
+ : `element <${name}> is not GraphML and was skipped`;
2732
+ }