@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
@@ -8,11 +8,13 @@
8
8
  * instead of re-reading the token from its `<`, and line numbers are counted from a cached next
9
9
  * line-break position, so a document without line breaks costs the same as one with them.
10
10
  *
11
- * What it handles: the XML declaration and processing instructions (skipped), comments (skipped),
11
+ * What it handles: the XML declaration and processing instructions (skipped), comments (skipped;
12
+ * a `--` inside one, which XML 1.0 forbids, is accepted: a comment carries no data and banner
13
+ * comments such as `<!-- ----- -->` are common in hand-written files),
12
14
  * CDATA sections (text), a DOCTYPE with an internal subset (skipped; entity declarations are not
13
15
  * expanded, so an unknown entity reference is a syntax error), the five predefined entities and
14
16
  * numeric character references (decimal and hexadecimal) in text and attribute values, attribute
15
- * value whitespace normalisation, end-of-line normalisation (CR LF and lone CR become LF), and
17
+ * value whitespace normalization, end-of-line normalization (CR LF and lone CR become LF), and
16
18
  * well-formedness: matching end tags, one root element, no text outside it, no duplicate
17
19
  * attributes, no unterminated markup at the end of the input. Namespaces are not resolved; element
18
20
  * and attribute names are reported as written (with their prefix).
@@ -26,7 +28,11 @@
26
28
  */
27
29
  import { XML_ILLEGAL_CHAR_CODE } from "./codes.js";
28
30
  import { isNameChar } from "./export.js";
29
- /** A well-formedness or syntax error, with the line it was found on. */
31
+ import { agree, plural } from "./plural.js";
32
+ /**
33
+ * A well-formedness or syntax error, with the line it was found on.
34
+ * @category Plugin helpers
35
+ */
30
36
  export class XmlSyntaxError extends Error {
31
37
  /**
32
38
  * Create the error.
@@ -39,6 +45,21 @@ export class XmlSyntaxError extends Error {
39
45
  this.line = line;
40
46
  }
41
47
  }
48
+ /**
49
+ * The input holds no markup at all (empty, whitespace only, a byte order mark only): an importer
50
+ * reports it as E_EMPTY_INPUT rather than as a syntax error.
51
+ * @category Plugin helpers
52
+ */
53
+ export class XmlEmptyInputError extends XmlSyntaxError {
54
+ /**
55
+ * Create the error.
56
+ * @param line - the 1-based line where the input ended
57
+ */
58
+ constructor(line) {
59
+ super("the input is empty: no root element", line);
60
+ this.name = "XmlEmptyInputError";
61
+ }
62
+ }
42
63
  /** How far Cytoscape's bare-ampersand repair looks for the `;` that ends an entity reference. */
43
64
  const BARE_AMPERSAND_LOOKAHEAD = 7;
44
65
  /** A numeric character reference at the start of a text: `&#123;` or `&#x1F;`. */
@@ -62,6 +83,7 @@ const OPEN_BRACKET = 91;
62
83
  const CLOSE_BRACKET = 93;
63
84
  const BANG = 33;
64
85
  const DASH = 45;
86
+ const QUESTION = 63;
65
87
  /** The longest entity reference held back at a chunk boundary (`&#x10FFFF;` is 10 characters). */
66
88
  const MAX_ENTITY_LENGTH = 16;
67
89
  /** Markup shorter than this (`<![CDATA[` is 9 characters) cannot be classified yet. */
@@ -108,10 +130,20 @@ const ILLEGAL_CHAR = new RegExp(`[${String.fromCharCode(0)}-${String.fromCharCod
108
130
  * written even as a character reference, so a conforming parser rejects the whole document.
109
131
  * @param text - the text
110
132
  * @returns true when the text cannot appear in an XML 1.0 document
133
+ * @category Plugin helpers
111
134
  */
112
135
  export function hasIllegalXmlChar(text) {
113
136
  return ILLEGAL_CHAR.test(text);
114
137
  }
138
+ /**
139
+ * Where the first character XML 1.0 forbids is in a text.
140
+ * @param text - the text
141
+ * @returns its index, or -1
142
+ */
143
+ function illegalCharIndex(text) {
144
+ const match = ILLEGAL_CHAR.exec(text);
145
+ return match === null ? -1 : match.index;
146
+ }
115
147
  /**
116
148
  * The loss notes of the texts a snapshot holds that no XML 1.0 document can carry (see
117
149
  * hasIllegalXmlChar): one E_XML_ILLEGAL_CHAR note per column (node, edge and graph string / dict /
@@ -119,6 +151,7 @@ export function hasIllegalXmlChar(text) {
119
151
  * the first such text. Shared by the GEXF and GraphML exporters' check().
120
152
  * @param snapshot - the snapshot
121
153
  * @returns the notes, empty when every text is writable
154
+ * @category Plugin helpers
122
155
  */
123
156
  export function xmlIllegalTextNotes(snapshot) {
124
157
  const notes = [];
@@ -132,7 +165,7 @@ export function xmlIllegalTextNotes(snapshot) {
132
165
  if (ids > 0) {
133
166
  notes.push(Object.freeze({
134
167
  code: XML_ILLEGAL_CHAR_CODE,
135
- message: `${ids} node id(s) hold a character XML 1.0 cannot carry; export() will throw`,
168
+ message: `${ids} node id${plural(ids)} ${agree(ids, "holds", "hold")} a character XML 1.0 cannot carry; the save fails`,
136
169
  column: null,
137
170
  count: ids,
138
171
  }));
@@ -147,7 +180,7 @@ export function xmlIllegalTextNotes(snapshot) {
147
180
  if (bad > 0) {
148
181
  notes.push(Object.freeze({
149
182
  code: XML_ILLEGAL_CHAR_CODE,
150
- message: `${domain} column "${column.meta.name}": ${bad} value(s) hold a character XML 1.0 cannot carry; export() will throw`,
183
+ message: `${domain} column "${column.meta.name}": ${bad} value${plural(bad)} ${agree(bad, "holds", "hold")} a character XML 1.0 cannot carry; the save fails`,
151
184
  column: column.meta.name,
152
185
  count: bad,
153
186
  }));
@@ -180,22 +213,44 @@ function countIllegalRows(column) {
180
213
  }
181
214
  return bad;
182
215
  }
183
- /** The encoding pseudo-attribute of an XML declaration at the very start of a document. */
216
+ /**
217
+ * The encoding pseudo-attribute of an XML declaration at the very start of a document (one
218
+ * anywhere else is a syntax error of the tokenizer).
219
+ */
184
220
  const XML_DECLARED_ENCODING = /^<\?xml\s[^>]*?\bencoding\s*=\s*["']([A-Za-z][A-Za-z0-9._-]*)["']/;
185
221
  /**
186
222
  * The encoding an XML document declares in its prolog (`<?xml version="1.0" encoding="..."?>`),
187
223
  * for the shared byte decoder (common/input.ts).
188
224
  * @param head - the start of the document, decoded as windows-1252
189
225
  * @returns the declared label, or null when there is no declaration or it names no encoding
226
+ * @category Plugin helpers
190
227
  */
191
228
  export function xmlDeclaredEncoding(head) {
192
229
  const match = XML_DECLARED_ENCODING.exec(head);
193
230
  return match === null ? null : match[1];
194
231
  }
232
+ /**
233
+ * The head of a document as text for an XML format's sniff(): UTF-16 when a byte order mark (or
234
+ * BOM-less UTF-16 `<?`, XML 1.0 Appendix F) says so, else UTF-8, never failing.
235
+ * @param head - the first bytes
236
+ * @returns the text, a leading byte order mark removed
237
+ * @category Plugin helpers
238
+ */
239
+ export function sniffXmlText(head) {
240
+ let label = "utf-8";
241
+ if ((head[0] === 0xff && head[1] === 0xfe) || (head[0] === 0x3c && head[1] === 0x00 && head[2] === 0x3f)) {
242
+ label = "utf-16le";
243
+ }
244
+ else if ((head[0] === 0xfe && head[1] === 0xff) || (head[0] === 0x00 && head[1] === 0x3c && head[3] === 0x3f)) {
245
+ label = "utf-16be";
246
+ }
247
+ return new TextDecoder(label, { fatal: false }).decode(head);
248
+ }
195
249
  /**
196
250
  * Whether a text is an XML Name.
197
251
  * @param text - the text
198
252
  * @returns true for a well-formed name
253
+ * @category Plugin helpers
199
254
  */
200
255
  export function isXmlName(text) {
201
256
  if (text.length === 0) {
@@ -230,6 +285,7 @@ function isXmlChar(cp) {
230
285
  * @param line - the line, for errors
231
286
  * @param repairs - the opt-in repairs (XGMML only); none by default
232
287
  * @returns the decoded text
288
+ * @category Plugin helpers
233
289
  */
234
290
  export function decodeEntities(raw, line, repairs) {
235
291
  let amp = raw.indexOf("&");
@@ -252,13 +308,14 @@ export function decodeEntities(raw, line, repairs) {
252
308
  continue;
253
309
  }
254
310
  if (semi < 0) {
255
- throw new XmlSyntaxError("unterminated entity reference", line);
311
+ throw new XmlSyntaxError("unterminated entity reference", lineAt(raw, amp, line));
256
312
  }
257
313
  const name = raw.slice(amp + 1, semi);
258
314
  if (name.startsWith("#")) {
259
315
  const hex = name.startsWith("#x") || name.startsWith("#X");
260
316
  const digits = name.slice(hex ? 2 : 1);
261
- const ok = hex ? /^[0-9a-fA-F]{1,6}$/.test(digits) : /^[0-9]{1,7}$/.test(digits);
317
+ // XML 1.0 puts no limit on leading zeros (&#x0000000041; is "A")
318
+ const ok = hex ? /^0*[\da-fA-F]{1,6}$/.test(digits) : /^0*\d{1,7}$/.test(digits);
262
319
  const cp = ok ? Number.parseInt(digits, hex ? 16 : 10) : -1;
263
320
  const low = cp >= 0xd800 && cp <= 0xdbff ? lowSurrogateAfter(raw, semi + 1, repairs) : null;
264
321
  if (low !== null) {
@@ -269,14 +326,14 @@ export function decodeEntities(raw, line, repairs) {
269
326
  continue;
270
327
  }
271
328
  if (cp < 0 || !isXmlChar(cp)) {
272
- throw new XmlSyntaxError(`invalid character reference &${name};`, line);
329
+ throw new XmlSyntaxError(`invalid character reference &${name};`, lineAt(raw, amp, line));
273
330
  }
274
331
  out += String.fromCodePoint(cp);
275
332
  }
276
333
  else {
277
334
  const value = NAMED_ENTITIES[name];
278
335
  if (value === undefined) {
279
- throw new XmlSyntaxError(`unknown entity &${name};`, line);
336
+ throw new XmlSyntaxError(`unknown entity &${name};`, lineAt(raw, amp, line));
280
337
  }
281
338
  out += value;
282
339
  }
@@ -322,6 +379,7 @@ function lowSurrogateAfter(raw, at, repairs) {
322
379
  * Whether a text is whitespace only.
323
380
  * @param text - the text
324
381
  * @returns true when every character is XML whitespace (also for an empty text)
382
+ * @category Plugin helpers
325
383
  */
326
384
  export function isWhitespace(text) {
327
385
  for (let i = 0; i < text.length; i++) {
@@ -335,6 +393,7 @@ export function isWhitespace(text) {
335
393
  * The local part of a possibly prefixed XML name.
336
394
  * @param name - the name as written
337
395
  * @returns the text after the last colon, or the name itself
396
+ * @category Plugin helpers
338
397
  */
339
398
  export function localName(name) {
340
399
  const colon = name.lastIndexOf(":");
@@ -346,13 +405,39 @@ export function localName(name) {
346
405
  * @param chunks - the text (already UTF-8 decoded, BOM removed)
347
406
  * @param handler - the event sink
348
407
  * @param repairs - the opt-in repairs (XGMML only); none by default
408
+ * @category Plugin helpers
349
409
  */
350
410
  export async function tokenizeXml(chunks, handler, repairs) {
351
411
  const tokenizer = new XmlTokenizer(handler, repairs);
352
- for await (const chunk of chunks) {
353
- tokenizer.push(chunk);
412
+ // XML 1.0 section 2.8: nothing may precede the XML declaration, not even whitespace (template
413
+ // output often adds a line break, and the declared encoding would then go unread)
414
+ let skipped = 0;
415
+ let lines = 1;
416
+ let leading = true;
417
+ try {
418
+ for await (const chunk of chunks) {
419
+ if (leading) {
420
+ const start = chunk.search(/\S/);
421
+ const blank = start < 0 ? chunk : chunk.slice(0, start);
422
+ skipped += blank.length;
423
+ lines += blank.split("\n").length - 1;
424
+ if (start >= 0) {
425
+ leading = false;
426
+ if (skipped > 0 && /^<\?xml\s/.test(chunk.slice(start, start + 6))) {
427
+ throw new XmlSyntaxError("the XML declaration must be at the very start of the document; whitespace precedes it", lines);
428
+ }
429
+ }
430
+ }
431
+ tokenizer.push(chunk);
432
+ }
433
+ tokenizer.finish();
434
+ }
435
+ catch (err) {
436
+ if (err instanceof XmlSyntaxError && tokenizer.declaresXml11 && !err.message.includes("XML 1.1")) {
437
+ throw new XmlSyntaxError(`${err.message}; the document declares XML 1.1, which graph-io does not read (it reads XML 1.0)`, err.line);
438
+ }
439
+ throw err;
354
440
  }
355
- tokenizer.finish();
356
441
  }
357
442
  /** The terminator of each fixed-terminator markup kind. */
358
443
  const TERMINATORS = {
@@ -361,9 +446,14 @@ const TERMINATORS = {
361
446
  pi: "?>",
362
447
  endtag: ">",
363
448
  };
449
+ /** The namespace the `xml` prefix is bound to by definition. */
450
+ const XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
451
+ /** The markup declarations a truncated `<!...` at the end of the input may have been cut from. */
452
+ const DECLARATION_OPENERS = ["<!--", "<![CDATA[", "<!DOCTYPE"];
364
453
  /**
365
454
  * The tokenizer state: the unconsumed tail of the input, the current line, the open element
366
455
  * stack and the text run being accumulated. `push()` chunks, then `finish()`.
456
+ * @category Plugin helpers
367
457
  */
368
458
  export class XmlTokenizer {
369
459
  /**
@@ -393,15 +483,47 @@ export class XmlTokenizer {
393
483
  this.stack = [];
394
484
  this.rootSeen = false;
395
485
  this.rootClosed = false;
486
+ /** Whether any markup was consumed (an input with none is empty, not malformed). */
487
+ this.markupSeen = false;
488
+ /** Whether nothing has been consumed yet: the XML declaration may only appear here. */
489
+ this.atStart = true;
490
+ /** Whether a DOCTYPE declaration was read (one is allowed, in the prolog). */
491
+ this.doctypeSeen = false;
492
+ /** The version of the XML declaration, or null. */
493
+ this.version = null;
494
+ /** The last two characters of the character data since the last markup, for the `]]>` check. */
495
+ this.textTail = "";
496
+ /** A high surrogate held back from the end of the previous chunk (its low half may start the next). */
497
+ this.pendingHigh = "";
498
+ /** The namespace declarations in scope, innermost last. */
499
+ this.bindings = [];
396
500
  this.handler = handler;
397
501
  this.repairs = repairs;
398
502
  }
503
+ /**
504
+ * Whether the document's XML declaration says version 1.1 (read by the XML 1.0 rules all the
505
+ * same; the syntax error of a 1.1-only construct then says why).
506
+ * @returns true after a `<?xml version="1.1"?>` declaration
507
+ */
508
+ get declaresXml11() {
509
+ return this.version === "1.1";
510
+ }
399
511
  /**
400
512
  * Feed one chunk and emit every complete token in it.
401
513
  * @param chunk - the text
402
514
  */
403
515
  push(chunk) {
404
516
  let text = chunk;
517
+ if (this.pendingHigh.length > 0) {
518
+ text = this.pendingHigh + text;
519
+ this.pendingHigh = "";
520
+ }
521
+ const last = text.charCodeAt(text.length - 1); // NOSONAR(S7758): reads UTF-16 code units on purpose
522
+ if (last >= 0xd800 && last <= 0xdbff) {
523
+ // a text chunk may end between the two halves of an astral character
524
+ this.pendingHigh = text.slice(-1);
525
+ text = text.slice(0, -1);
526
+ }
405
527
  if (this.pendingCr) {
406
528
  text = `\r${text}`;
407
529
  this.pendingCr = false;
@@ -411,19 +533,21 @@ export class XmlTokenizer {
411
533
  text = text.slice(0, -1);
412
534
  }
413
535
  if (text.includes("\r")) {
414
- text = text.replace(/\r\n?/g, "\n");
536
+ text = text.replaceAll(/\r\n?/g, "\n");
415
537
  }
416
538
  this.feed(text);
417
539
  }
418
540
  /** Signal the end of the input: flush the last text run and check well-formedness. */
419
541
  finish() {
420
542
  this.final = true;
543
+ const held = this.pendingHigh;
544
+ this.pendingHigh = "";
421
545
  if (this.pendingCr) {
422
546
  this.pendingCr = false;
423
- this.feed("\n");
547
+ this.feed(`\n${held}`);
424
548
  }
425
549
  else {
426
- this.feed("");
550
+ this.feed(held);
427
551
  }
428
552
  if (this.pending !== null || this.buffer.length > 0) {
429
553
  throw new XmlSyntaxError("unexpected end of input inside markup", this.line);
@@ -433,13 +557,16 @@ export class XmlTokenizer {
433
557
  throw new XmlSyntaxError(`unclosed element <${this.stack[this.stack.length - 1]}>`, this.line);
434
558
  }
435
559
  if (!this.rootSeen) {
560
+ if (!this.markupSeen) {
561
+ throw new XmlEmptyInputError(this.line);
562
+ }
436
563
  throw new XmlSyntaxError("no root element", this.line);
437
564
  }
438
565
  }
439
566
  /**
440
- * Append normalised text: continue a pending token's terminator scan over the new text alone,
567
+ * Append normalized text: continue a pending token's terminator scan over the new text alone,
441
568
  * and once the token is complete (or when none is pending) scan the buffer for tokens.
442
- * @param text - the text, line breaks normalised
569
+ * @param text - the text, line breaks normalized
443
570
  */
444
571
  feed(text) {
445
572
  const { pending } = this;
@@ -546,9 +673,20 @@ export class XmlTokenizer {
546
673
  * @returns the index one past the `>`, or -1
547
674
  */
548
675
  scanDoctype(pending, text, from) {
549
- let { quote, depth, comment, run } = pending;
676
+ let { quote, depth, comment, pi, run } = pending;
550
677
  for (let i = from; i < text.length; i++) {
551
678
  const c = text.charCodeAt(i);
679
+ if (pi) {
680
+ // a processing instruction in the subset ends at `?>`; quotes inside it are text
681
+ if (c === GT && run === 1) {
682
+ pi = false;
683
+ run = 0;
684
+ }
685
+ else {
686
+ run = c === QUESTION ? 1 : 0;
687
+ }
688
+ continue;
689
+ }
552
690
  if (comment) {
553
691
  if (c === DASH) {
554
692
  run++;
@@ -582,6 +720,11 @@ export class XmlTokenizer {
582
720
  run = 0;
583
721
  continue;
584
722
  }
723
+ if (run === 1 && c === QUESTION) {
724
+ pi = true;
725
+ run = 0;
726
+ continue;
727
+ }
585
728
  run = 0;
586
729
  if (c === LT) {
587
730
  run = 1;
@@ -601,6 +744,7 @@ export class XmlTokenizer {
601
744
  pending.quote = 0;
602
745
  pending.depth = 0;
603
746
  pending.comment = false;
747
+ pending.pi = false;
604
748
  pending.run = 0;
605
749
  return i + 1;
606
750
  }
@@ -608,6 +752,7 @@ export class XmlTokenizer {
608
752
  pending.quote = quote;
609
753
  pending.depth = depth;
610
754
  pending.comment = comment;
755
+ pending.pi = pi;
611
756
  pending.run = run;
612
757
  return -1;
613
758
  }
@@ -628,7 +773,11 @@ export class XmlTokenizer {
628
773
  if (!this.final) {
629
774
  // an entity reference may be split across chunks: hold back from its "&"
630
775
  const amp = buffer.lastIndexOf("&");
631
- if (amp >= pos && amp >= length - MAX_ENTITY_LENGTH && buffer.indexOf(";", amp) < 0) {
776
+ if (amp >= pos &&
777
+ !buffer.includes(";", amp) &&
778
+ // ponytail: a zero-padded reference longer than MAX_ENTITY_LENGTH is held back
779
+ // whole, so an absurdly long run of zeros is re-scanned once per chunk
780
+ (amp >= length - MAX_ENTITY_LENGTH || /^&#[xX]?0[\da-fA-F]*$/.test(buffer.slice(amp)))) {
632
781
  end = amp;
633
782
  }
634
783
  if (this.repairs?.surrogatePair !== undefined) {
@@ -675,6 +824,7 @@ export class XmlTokenizer {
675
824
  quote: 0,
676
825
  depth: 0,
677
826
  comment: false,
827
+ pi: false,
678
828
  run: 0,
679
829
  };
680
830
  if (piece.length < MIN_DECIDABLE_MARKUP) {
@@ -723,60 +873,31 @@ export class XmlTokenizer {
723
873
  * @returns the index after the markup, or -1 when the buffer ends before the markup does
724
874
  */
725
875
  consumeMarkup(pos) {
876
+ const end = this.markupEnd(pos);
877
+ if (end >= 0) {
878
+ this.markupSeen = true;
879
+ this.atStart = false;
880
+ // markup ends a run of character data: a `]]>` cannot straddle it
881
+ this.textTail = "";
882
+ }
883
+ return end;
884
+ }
885
+ /**
886
+ * Read the markup starting at `pos` (a `<`) and deliver what it means.
887
+ * @param pos - the index of the `<`
888
+ * @returns the index after the markup, or -1 when the buffer ends before the markup does
889
+ */
890
+ markupEnd(pos) {
726
891
  const { buffer } = this;
727
892
  if (buffer.startsWith("<!", pos)) {
728
- if (buffer.length - pos < MIN_DECIDABLE_MARKUP && !this.final) {
729
- // "<![CDATA[" is 9 characters; wait until the kind of declaration is decidable
730
- return -1;
731
- }
732
- if (buffer.startsWith("<!--", pos)) {
733
- const end = buffer.indexOf("-->", pos + 4);
734
- if (end < 0) {
735
- return -1;
736
- }
737
- this.advanceLine(pos, end + 3);
738
- return end + 3;
739
- }
740
- if (buffer.startsWith("<![CDATA[", pos)) {
741
- const end = buffer.indexOf("]]>", pos + 9);
742
- if (end < 0) {
743
- return -1;
744
- }
745
- if (this.text.length === 0) {
746
- this.textLine = this.line;
747
- }
748
- const cdata = buffer.slice(pos + 9, end);
749
- if (hasIllegalXmlChar(cdata)) {
750
- throw new XmlSyntaxError("a character XML 1.0 forbids appears in a CDATA section", this.line);
751
- }
752
- this.text += cdata;
753
- this.advanceLine(pos, end + 3);
754
- return end + 3;
755
- }
756
- if (!buffer.startsWith("<!DOCTYPE", pos)) {
757
- throw new XmlSyntaxError("unexpected markup declaration", this.line);
758
- }
759
- const state = {
760
- kind: "doctype",
761
- pieces: [],
762
- tail: "",
763
- quote: 0,
764
- depth: 0,
765
- comment: false,
766
- run: 0,
767
- };
768
- const end = this.scanDoctype(state, buffer, pos + 9);
769
- if (end < 0) {
770
- return -1;
771
- }
772
- this.advanceLine(pos, end);
773
- return end;
893
+ return this.declarationEnd(pos);
774
894
  }
775
895
  if (buffer.startsWith("<?", pos)) {
776
896
  const end = buffer.indexOf("?>", pos + 2);
777
897
  if (end < 0) {
778
898
  return -1;
779
899
  }
900
+ this.processingInstruction(pos, end);
780
901
  this.advanceLine(pos, end + 2);
781
902
  return end + 2;
782
903
  }
@@ -785,7 +906,8 @@ export class XmlTokenizer {
785
906
  if (end < 0) {
786
907
  return -1;
787
908
  }
788
- const name = buffer.slice(pos + 2, end).trim();
909
+ // whitespace may follow the name, never precede it
910
+ const name = buffer.slice(pos + 2, end).trimEnd();
789
911
  if (!isXmlName(name)) {
790
912
  throw new XmlSyntaxError(`malformed end tag </${name}>`, this.line);
791
913
  }
@@ -799,13 +921,149 @@ export class XmlTokenizer {
799
921
  return -1;
800
922
  }
801
923
  this.flushText();
802
- this.startElement(tag.name, tag.attrs);
924
+ this.startElement(tag.name, tag.attrs, tag.declaresNamespace);
803
925
  if (tag.selfClosing) {
804
926
  this.endElement(tag.name);
805
927
  }
806
928
  this.advanceLine(pos, tag.end);
807
929
  return tag.end;
808
930
  }
931
+ /**
932
+ * Read the CDATA section at `pos` into the text.
933
+ * @param pos - the index of the `<`
934
+ * @returns the index after the section, or -1 when the buffer ends before the section does
935
+ */
936
+ cdataEnd(pos) {
937
+ const end = this.buffer.indexOf("]]>", pos + 9);
938
+ if (end < 0) {
939
+ return -1;
940
+ }
941
+ if (this.text.length === 0) {
942
+ this.textLine = this.line;
943
+ }
944
+ const cdata = this.buffer.slice(pos + 9, end);
945
+ const bad = illegalCharIndex(cdata);
946
+ if (bad >= 0) {
947
+ throw new XmlSyntaxError("a character XML 1.0 forbids appears in a CDATA section", this.lineIn(pos, pos + 9 + bad));
948
+ }
949
+ this.text += cdata;
950
+ this.advanceLine(pos, end + 3);
951
+ return end + 3;
952
+ }
953
+ /**
954
+ * Read the `<!...>` markup at `pos`: a comment, a CDATA section or the DOCTYPE.
955
+ * @param pos - the index of the `<`
956
+ * @returns the index after the markup, or -1 when the buffer ends before the markup does
957
+ */
958
+ declarationEnd(pos) {
959
+ const { buffer } = this;
960
+ if (buffer.length - pos < MIN_DECIDABLE_MARKUP) {
961
+ // "<![CDATA[" is 9 characters; wait until the kind of declaration is decidable, and at
962
+ // the end of the input read the start of one cut short as a truncation
963
+ const head = buffer.slice(pos);
964
+ if (!this.final || DECLARATION_OPENERS.some((opener) => opener.startsWith(head))) {
965
+ return -1;
966
+ }
967
+ }
968
+ if (buffer.startsWith("<!--", pos)) {
969
+ const end = buffer.indexOf("-->", pos + 4);
970
+ if (end < 0) {
971
+ return -1;
972
+ }
973
+ // a "--" inside, which XML 1.0 forbids, is accepted: the comment carries no data
974
+ this.advanceLine(pos, end + 3);
975
+ return end + 3;
976
+ }
977
+ if (buffer.startsWith("<![CDATA[", pos)) {
978
+ return this.cdataEnd(pos);
979
+ }
980
+ if (!buffer.startsWith("<!DOCTYPE", pos)) {
981
+ if (buffer.slice(pos + 2, pos + 9).toUpperCase() === "DOCTYPE") {
982
+ throw new XmlSyntaxError(`<!${buffer.slice(pos + 2, pos + 9)}> is not XML (it must be written <!DOCTYPE>); the input looks like an HTML document`, this.line);
983
+ }
984
+ throw new XmlSyntaxError("unexpected markup declaration", this.line);
985
+ }
986
+ if (this.rootSeen || this.doctypeSeen) {
987
+ throw new XmlSyntaxError("a DOCTYPE declaration is allowed once, before the root element", this.line);
988
+ }
989
+ const state = {
990
+ kind: "doctype",
991
+ pieces: [],
992
+ tail: "",
993
+ quote: 0,
994
+ depth: 0,
995
+ comment: false,
996
+ pi: false,
997
+ run: 0,
998
+ };
999
+ const end = this.scanDoctype(state, buffer, pos + 9);
1000
+ if (end < 0) {
1001
+ return -1;
1002
+ }
1003
+ this.doctypeSeen = true;
1004
+ this.handler.doctype?.(buffer.slice(pos, end), this.line);
1005
+ this.advanceLine(pos, end);
1006
+ return end;
1007
+ }
1008
+ /**
1009
+ * Check a processing instruction `<?target ...?>` (skipped otherwise): its target must be a
1010
+ * name, and the reserved target `xml` is the XML declaration, allowed only at the very start
1011
+ * of the document with a 1.x version.
1012
+ * @param pos - the index of the `<`
1013
+ * @param end - the index of the `?>`
1014
+ */
1015
+ processingInstruction(pos, end) {
1016
+ const body = this.buffer.slice(pos + 2, end);
1017
+ const target = /^[^\s]*/.exec(body)?.[0] ?? "";
1018
+ if (!isXmlName(target)) {
1019
+ throw new XmlSyntaxError(`a processing instruction needs a target name, not "${target}"`, this.line);
1020
+ }
1021
+ if (target.toLowerCase() !== "xml") {
1022
+ return;
1023
+ }
1024
+ if (target !== "xml") {
1025
+ throw new XmlSyntaxError(`<?${target} is not an XML declaration (which is written <?xml in lower case); the target is reserved`, this.line);
1026
+ }
1027
+ if (!this.atStart || pos !== 0) {
1028
+ throw new XmlSyntaxError("an XML declaration (<?xml ...?>) may appear only at the very start of the document; the target xml is reserved", this.line);
1029
+ }
1030
+ const version = /\bversion\s*=\s*(["'])([^"']*)\1/.exec(body)?.[2];
1031
+ if (version === undefined) {
1032
+ throw new XmlSyntaxError("the XML declaration has no version", this.line);
1033
+ }
1034
+ if (!/^1\.\d+$/.test(version)) {
1035
+ throw new XmlSyntaxError(`XML version ${version} is not supported (only 1.x)`, this.line);
1036
+ }
1037
+ this.version = version;
1038
+ }
1039
+ /**
1040
+ * The line of a buffer position after `start`, the position the current line count refers to.
1041
+ * Used on the error path only.
1042
+ * @param start - a position on line `this.line`
1043
+ * @param at - the position
1044
+ * @returns its line
1045
+ */
1046
+ lineIn(start, at) {
1047
+ return lineAt(this.buffer.slice(start, at), at - start, this.line);
1048
+ }
1049
+ /**
1050
+ * Decode the entities of a text, naming XML 1.1 when a character reference XML 1.0 forbids
1051
+ * appears in a document that declares version 1.1.
1052
+ * @param raw - the text as written
1053
+ * @param line - the line the text starts on
1054
+ * @returns the decoded text
1055
+ */
1056
+ decode(raw, line) {
1057
+ try {
1058
+ return decodeEntities(raw, line, this.repairs);
1059
+ }
1060
+ catch (err) {
1061
+ if (err instanceof XmlSyntaxError && this.version !== null && this.version !== "1.0") {
1062
+ throw new XmlSyntaxError(`${err.message} (the document declares XML ${this.version}, whose character rules are not supported; it is read as XML 1.0)`, err.line);
1063
+ }
1064
+ throw err;
1065
+ }
1066
+ }
809
1067
  /**
810
1068
  * Parse a start tag at `pos`.
811
1069
  * @param pos - the index of the `<`
@@ -825,7 +1083,9 @@ export class XmlTokenizer {
825
1083
  const name = buffer.slice(i, nameEnd);
826
1084
  i = nameEnd;
827
1085
  const attrs = new Map();
1086
+ let declaresNamespace = false;
828
1087
  for (;;) {
1088
+ const before = i;
829
1089
  while (i < length && isSpace(buffer.charCodeAt(i))) {
830
1090
  i++;
831
1091
  }
@@ -834,23 +1094,27 @@ export class XmlTokenizer {
834
1094
  }
835
1095
  const c = buffer.charCodeAt(i);
836
1096
  if (c === GT) {
837
- return { name, attrs, selfClosing: false, end: i + 1 };
1097
+ return { name, attrs, selfClosing: false, declaresNamespace, end: i + 1 };
838
1098
  }
839
1099
  if (c === SLASH) {
840
1100
  if (i + 1 >= length) {
841
1101
  return null;
842
1102
  }
843
1103
  if (buffer.charCodeAt(i + 1) !== GT) {
844
- throw new XmlSyntaxError(`unexpected "/" in <${name}>`, this.line);
1104
+ throw new XmlSyntaxError(`unexpected "/" in <${name}>`, this.lineIn(pos, i));
845
1105
  }
846
- return { name, attrs, selfClosing: true, end: i + 2 };
1106
+ return { name, attrs, selfClosing: true, declaresNamespace, end: i + 2 };
1107
+ }
1108
+ if (i === before) {
1109
+ // the name, or the previous value's closing quote, runs into the next attribute
1110
+ throw new XmlSyntaxError(`the attributes of <${name}> must be separated by whitespace`, this.lineIn(pos, i));
847
1111
  }
848
1112
  const attrEnd = this.readName(i);
849
1113
  if (attrEnd < 0) {
850
1114
  return null;
851
1115
  }
852
1116
  if (attrEnd === i) {
853
- throw new XmlSyntaxError(`malformed attribute in <${name}>`, this.line);
1117
+ throw new XmlSyntaxError(`malformed attribute in <${name}>`, this.lineIn(pos, i));
854
1118
  }
855
1119
  const attrName = buffer.slice(i, attrEnd);
856
1120
  i = attrEnd;
@@ -861,7 +1125,7 @@ export class XmlTokenizer {
861
1125
  return null;
862
1126
  }
863
1127
  if (buffer.charCodeAt(i) !== EQUALS) {
864
- throw new XmlSyntaxError(`attribute ${attrName} of <${name}> has no value`, this.line);
1128
+ throw new XmlSyntaxError(`attribute ${attrName} of <${name}> has no value`, this.lineIn(pos, i));
865
1129
  }
866
1130
  i++;
867
1131
  while (i < length && isSpace(buffer.charCodeAt(i))) {
@@ -872,20 +1136,27 @@ export class XmlTokenizer {
872
1136
  }
873
1137
  const quote = buffer.charCodeAt(i);
874
1138
  if (quote !== QUOTE && quote !== APOS) {
875
- throw new XmlSyntaxError(`attribute ${attrName} of <${name}> is not quoted`, this.line);
1139
+ throw new XmlSyntaxError(`attribute ${attrName} of <${name}> is not quoted`, this.lineIn(pos, i));
876
1140
  }
877
1141
  const close = buffer.indexOf(quote === QUOTE ? '"' : "'", i + 1);
878
1142
  if (close < 0) {
879
1143
  return null;
880
1144
  }
1145
+ const line = this.lineIn(pos, i);
881
1146
  if (attrs.has(attrName)) {
882
- throw new XmlSyntaxError(`duplicate attribute ${attrName} in <${name}>`, this.line);
1147
+ throw new XmlSyntaxError(`duplicate attribute ${attrName} in <${name}>`, line);
883
1148
  }
884
1149
  const raw = buffer.slice(i + 1, close);
885
1150
  if (hasIllegalXmlChar(raw)) {
886
- throw new XmlSyntaxError(`a character XML 1.0 forbids appears in attribute ${attrName} of <${name}>`, this.line);
1151
+ throw new XmlSyntaxError(`a character XML 1.0 forbids appears in attribute ${attrName} of <${name}>`, line);
887
1152
  }
888
- attrs.set(attrName, decodeEntities(normalizeAttributeValue(raw), this.line, this.repairs));
1153
+ if (raw.includes("<")) {
1154
+ throw new XmlSyntaxError(`a "<" in the value of attribute ${attrName} of <${name}> (write &lt;)`, line);
1155
+ }
1156
+ if (attrName.startsWith("xmlns")) {
1157
+ declaresNamespace = true;
1158
+ }
1159
+ attrs.set(attrName, this.decode(normalizeAttributeValue(raw), line));
889
1160
  i = close + 1;
890
1161
  }
891
1162
  }
@@ -946,10 +1217,20 @@ export class XmlTokenizer {
946
1217
  this.textLine = this.line;
947
1218
  }
948
1219
  const raw = this.buffer.slice(start, end);
949
- if (hasIllegalXmlChar(raw)) {
950
- throw new XmlSyntaxError("a character XML 1.0 forbids appears in character data", this.line);
1220
+ const bad = illegalCharIndex(raw);
1221
+ if (bad >= 0) {
1222
+ throw new XmlSyntaxError("a character XML 1.0 forbids appears in character data", lineAt(raw, bad, this.line));
951
1223
  }
952
- this.text += decodeEntities(raw, this.line, this.repairs);
1224
+ if (raw.includes(">")) {
1225
+ const probe = this.textTail + raw;
1226
+ const close = probe.indexOf("]]>");
1227
+ if (close >= 0) {
1228
+ throw new XmlSyntaxError('"]]>" is not allowed in character data (write ]]&gt;)', lineAt(raw, Math.max(0, close - this.textTail.length), this.line));
1229
+ }
1230
+ }
1231
+ this.textTail = raw.length >= 2 ? raw.slice(-2) : (this.textTail + raw).slice(-2);
1232
+ this.atStart = false;
1233
+ this.text += this.decode(raw, this.line);
953
1234
  this.advanceLine(start, end);
954
1235
  }
955
1236
  /**
@@ -992,16 +1273,49 @@ export class XmlTokenizer {
992
1273
  * Open an element.
993
1274
  * @param name - the element name
994
1275
  * @param attrs - its attributes
1276
+ * @param declaresNamespace - whether an attribute name starts with `xmlns`
995
1277
  */
996
- startElement(name, attrs) {
1278
+ startElement(name, attrs, declaresNamespace) {
997
1279
  if (this.stack.length === 0) {
998
1280
  if (this.rootClosed) {
999
1281
  throw new XmlSyntaxError(`a second root element <${name}> follows the document element`, this.line);
1000
1282
  }
1001
1283
  this.rootSeen = true;
1002
1284
  }
1285
+ if (declaresNamespace) {
1286
+ for (const [key, uri] of attrs) {
1287
+ if (key === "xmlns") {
1288
+ this.bindings.push({ prefix: "", uri, depth: this.stack.length });
1289
+ }
1290
+ else if (key.startsWith("xmlns:")) {
1291
+ this.bindings.push({ prefix: key.slice(6), uri, depth: this.stack.length });
1292
+ }
1293
+ }
1294
+ }
1003
1295
  this.stack.push(name);
1004
- this.handler.start(name, attrs, this.line);
1296
+ this.handler.start(name, attrs, this.line, this.namespaceOf(name));
1297
+ }
1298
+ /**
1299
+ * The namespace an element name is in (Namespaces in XML 1.0 section 6).
1300
+ * @param name - the element name as written
1301
+ * @returns the URI; "" for no namespace; null for an undeclared prefix or a name that is not
1302
+ * namespace-well-formed
1303
+ */
1304
+ namespaceOf(name) {
1305
+ const colon = name.indexOf(":");
1306
+ if (colon >= 0 && (colon === 0 || colon === name.length - 1 || name.includes(":", colon + 1))) {
1307
+ return null;
1308
+ }
1309
+ const prefix = colon < 0 ? "" : name.slice(0, colon);
1310
+ for (let i = this.bindings.length - 1; i >= 0; i--) {
1311
+ if (this.bindings[i].prefix === prefix) {
1312
+ return this.bindings[i].uri;
1313
+ }
1314
+ }
1315
+ if (prefix === "xml") {
1316
+ return XML_NAMESPACE;
1317
+ }
1318
+ return prefix === "" ? "" : null;
1005
1319
  }
1006
1320
  /**
1007
1321
  * Close an element, checking that it matches the innermost open one.
@@ -1016,6 +1330,9 @@ export class XmlTokenizer {
1016
1330
  throw new XmlSyntaxError(`end tag </${name}> does not match <${open}>`, this.line);
1017
1331
  }
1018
1332
  this.stack.pop();
1333
+ while ((this.bindings.at(-1)?.depth ?? -1) >= this.stack.length) {
1334
+ this.bindings.pop();
1335
+ }
1019
1336
  if (this.stack.length === 0) {
1020
1337
  this.rootClosed = true;
1021
1338
  }
@@ -1023,12 +1340,12 @@ export class XmlTokenizer {
1023
1340
  }
1024
1341
  }
1025
1342
  /**
1026
- * Attribute value normalisation (XML 1.0 section 3.3.3): a literal tab or line break becomes a
1343
+ * Attribute value normalization (XML 1.0 section 3.3.3): a literal tab or line break becomes a
1027
1344
  * space; a character reference to one is kept, which is why this runs before entity decoding.
1028
1345
  * @param raw - the value between the quotes
1029
- * @returns the normalised value
1346
+ * @returns the normalized value
1030
1347
  */
1031
1348
  function normalizeAttributeValue(raw) {
1032
- return raw.includes("\n") || raw.includes("\t") ? raw.replace(/[\n\t]/g, " ") : raw;
1349
+ return raw.includes("\n") || raw.includes("\t") ? raw.replaceAll(/[\n\t]/g, " ") : raw;
1033
1350
  }
1034
1351
  //# sourceMappingURL=xml.js.map