@graphty/graph-io 0.3.24 → 0.3.26

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-BbJI55dx.js +56 -0
  4. package/dist/chunks/choose-BbJI55dx.js.map +1 -0
  5. package/dist/chunks/constants-BCsFsMj4.js +231 -0
  6. package/dist/chunks/constants-BCsFsMj4.js.map +1 -0
  7. package/dist/chunks/{escape-B-tkk_Cl.js → escape-D0g6NNUy.js} +382 -92
  8. package/dist/chunks/escape-D0g6NNUy.js.map +1 -0
  9. package/dist/chunks/export-BTI4yQgy.js +4333 -0
  10. package/dist/chunks/export-BTI4yQgy.js.map +1 -0
  11. package/dist/chunks/exporter-BMqk5oxW.js +3465 -0
  12. package/dist/chunks/exporter-BMqk5oxW.js.map +1 -0
  13. package/dist/chunks/{importer-eUREhBGj.js → exporter-Cra2GYcN.js} +4969 -3201
  14. package/dist/chunks/exporter-Cra2GYcN.js.map +1 -0
  15. package/dist/chunks/{exporter-BGrsWamJ.js → exporter-Dxh231ZF.js} +38 -229
  16. package/dist/chunks/exporter-Dxh231ZF.js.map +1 -0
  17. package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-tne475KK.js} +22 -20
  18. package/dist/chunks/exporter-tne475KK.js.map +1 -0
  19. package/dist/chunks/importer-90YWhJoF.js +2811 -0
  20. package/dist/chunks/importer-90YWhJoF.js.map +1 -0
  21. package/dist/chunks/{importer-D_e7e7LX.js → importer-Bcl1d4xH.js} +398 -145
  22. package/dist/chunks/importer-Bcl1d4xH.js.map +1 -0
  23. package/dist/chunks/{importer-BWY2FFc5.js → importer-BfIsp1f9.js} +255 -77
  24. package/dist/chunks/importer-BfIsp1f9.js.map +1 -0
  25. package/dist/chunks/{importer-zGLo8Dg_.js → importer-C_TEA1zJ.js} +484 -157
  26. package/dist/chunks/importer-C_TEA1zJ.js.map +1 -0
  27. package/dist/chunks/{importer-DET1tg6s.js → importer-DCjOvO7U.js} +1687 -310
  28. package/dist/chunks/importer-DCjOvO7U.js.map +1 -0
  29. package/dist/chunks/{importer-vi3bSdtw.js → importer-DfVu6j59.js} +509 -76
  30. package/dist/chunks/importer-DfVu6j59.js.map +1 -0
  31. package/dist/chunks/{importer-DIrbbnAf.js → importer-Dku_VAIY.js} +585 -226
  32. package/dist/chunks/importer-Dku_VAIY.js.map +1 -0
  33. package/dist/chunks/json-elements-3tnyt5uZ.js +1225 -0
  34. package/dist/chunks/json-elements-3tnyt5uZ.js.map +1 -0
  35. package/dist/chunks/ontology-export-B9Tcf37D.js +357 -0
  36. package/dist/chunks/ontology-export-B9Tcf37D.js.map +1 -0
  37. package/dist/chunks/{records-dbRkxwaq.js → records-CQXT__mY.js} +298 -114
  38. package/dist/chunks/records-CQXT__mY.js.map +1 -0
  39. package/dist/chunks/{text-Dr0Ifpag.js → text-DmcUs5_Q.js} +29 -11
  40. package/dist/chunks/text-DmcUs5_Q.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 +951 -261
  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 +342 -85
  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 +564 -157
  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 +19 -1
  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 +33 -17
  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 +868 -188
  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 +2 -0
  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 +400 -83
  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 +145 -32
  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 +680 -285
  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 +226 -79
  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 +98 -24
  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 +108 -26
  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 +238 -85
  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 +66 -34
  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 +183 -51
  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 +20 -21
  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 +7 -2
  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 +38 -4
  468. package/src/common/direction.ts +78 -51
  469. package/src/common/escape.ts +33 -17
  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 +1034 -198
  475. package/src/common/line-format.ts +249 -0
  476. package/src/common/lists.ts +6 -1
  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 +477 -88
  487. package/src/common/zip.ts +169 -7
  488. package/src/formats/csv/exporter.ts +201 -46
  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 +888 -300
  496. package/src/formats/cx/index.ts +4 -3
  497. package/src/formats/cx2/exporter.ts +61 -338
  498. package/src/formats/cx2/importer.ts +317 -86
  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 +139 -31
  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 +111 -26
  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 +320 -101
  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 +89 -41
  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 +213 -52
  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 +35 -27
  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 +11 -3
  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
@@ -1,10 +1,9 @@
1
1
  import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
2
- import { V as resolveExportOptions, X as pairFolding, ax as canonicalId, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, R as ROLE_TAKEN_CODE, at as RENAMED_CODE, I as ID_MERGED_CODE, d as DUPLICATE_NODE_CODE, t as SYNTAX_CODE, M as MULTIPLE_GRAPHS_CODE, q as UNKNOWN_ENCODING_CODE, E as ENCODING_FALLBACK_CODE, r as INVALID_ENCODING_CODE, s as INVALID_UTF8_CODE, v as resolveImportOptions, w as ImportReportBuilder, z as reportUnusedOptions, ap as LineReader, y as throwIfAborted, F as reportSinkOptions, J as DirectionResolver, K as IdCoercer, Q as uniqueColumnName, L as declareResolved, az as coerceIdText } from "./report-B1z4WT9e.js";
3
- import { i as isPajekLabel, q as quotePajekLabel } from "./escape-B-tkk_Cl.js";
4
- import { L as LOSS, s as sanitizeIds, a as checkCapabilities, c as capabilities } from "./export-Bh60Dv-n.js";
5
- import { e as explicitWeights, a as formatF64, b as formatDecimal, f as formatF32, d as formatInteger, i as isWeightField, p as parseWeightText } from "./weights-Dzba96G3.js";
6
- import { i as inferTextDtype, W as WIDENING_UNSUPPORTED_CODE, a as isNumericText, T as TextCellWriter } from "./text-Dr0Ifpag.js";
7
- import { j as joinText, e as encodeChunks } from "./writer-C7flM-Ih.js";
2
+ import { L as LOSS, r as resolveExportOptions, aE as sanitizeIds, a as checkCapabilities, p as pairFolding, b as plural, $ as agree, c as capabilities, a9 as idText, e as explicitWeights, aF as formatDecimal, aa as formatF32, aG as formatInteger, aH as canonicalId, P as PRECISION_CODE, o as DUPLICATE_ATTRIBUTE_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, R as ROLE_TAKEN_CODE, ay as RENAMED_CODE, I as ID_MERGED_CODE, n as DUPLICATE_NODE_CODE, ak as SYNTAX_CODE, ae as AMBIGUOUS_GRAPH_NAME_CODE, af as GRAPH_NOT_FOUND_CODE, l as MULTIPLE_GRAPHS_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE, al as resolveImportOptions, a8 as ImportReportBuilder, an as reportUnusedOptions, az as LineReader, am as throwIfAborted, ap as graphChosen, ao as reportSinkOptions, aq as DirectionResolver, ar as IdCoercer, a0 as uniqueColumnName, a1 as declareResolved, aI as coerceIdText, aJ as survivesF32, aK as isWeightField, aL as parseWeightText } from "./export-BTI4yQgy.js";
3
+ import { i as isPajekLabel, q as quotePajekLabel } from "./escape-D0g6NNUy.js";
4
+ import { i as inferTextDtype, W as WIDENING_UNSUPPORTED_CODE, a as isNumericText, T as TextCellWriter } from "./text-DmcUs5_Q.js";
5
+ import { j as joinText, e as encodeChunks } from "./writer-DxjgHwSe.js";
6
+ import { i as importChosenGraph } from "./choose-BbJI55dx.js";
8
7
  const LABEL_COLUMN = "label";
9
8
  const POSITION_COLUMN = "position";
10
9
  const SHAPE_COLUMN = "shape";
@@ -42,46 +41,103 @@ const SECTION_KINDS = /* @__PURE__ */ new Map([
42
41
  ]);
43
42
  const INTEGER_TEXT = /^[+-]?[0-9]+$/;
44
43
  const TIME_POINT = /^(\*|[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)(?:-(\*|[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?))?$/;
45
- function tokenize(line) {
46
- const tokens = [];
47
- let current = "";
48
- let started = false;
49
- let quoted = false;
50
- for (let i = 0; i < line.length; i++) {
51
- const c = line.charCodeAt(i);
44
+ function tokenize(line, notes) {
45
+ const tokens = new LineTokens(line, notes);
46
+ let i = 0;
47
+ while (i < line.length) {
48
+ i = tokens.step(i);
49
+ }
50
+ return tokens.finish();
51
+ }
52
+ class LineTokens {
53
+ /**
54
+ * Start a line.
55
+ * @param line - the line
56
+ * @param notes - set to what the line holds beyond its tokens, when given
57
+ */
58
+ constructor(line, notes) {
59
+ this.line = line;
60
+ this.notes = notes;
61
+ this.tokens = [];
62
+ this.current = "";
63
+ this.started = false;
64
+ this.quoted = false;
65
+ }
66
+ /**
67
+ * Read the character at `i`.
68
+ * @param i - its index
69
+ * @returns the index of the next character to read
70
+ */
71
+ step(i) {
72
+ const c = this.line.codePointAt(i);
52
73
  if (c === 34) {
53
- quoted = !quoted;
54
- started = true;
55
- continue;
74
+ this.quote(i);
75
+ return i + 1;
56
76
  }
57
- if (!quoted && !started && c === 91) {
58
- const close = line.indexOf("]", i);
59
- if (close > i && line.lastIndexOf("[", close) === i) {
60
- current += line.slice(i, close + 1);
61
- started = true;
62
- i = close;
63
- continue;
64
- }
77
+ const close = !this.quoted && !this.started && c === 91 ? this.intervalEnd(i) : -1;
78
+ if (close > 0) {
79
+ this.current += this.line.slice(i, close + 1);
80
+ this.started = true;
81
+ return close + 1;
65
82
  }
66
- if (!quoted && (c === 32 || c === 9 || c === 13 || c === 12 || c === 11)) {
67
- if (started) {
68
- tokens.push(current);
69
- current = "";
70
- started = false;
71
- }
72
- continue;
83
+ if (!this.quoted && isBlank(c)) {
84
+ this.endToken();
85
+ return i + 1;
73
86
  }
74
- current += line[i];
75
- started = true;
87
+ this.current += this.line[i];
88
+ this.started = true;
89
+ return i + 1;
76
90
  }
77
- if (quoted) {
78
- return null;
91
+ /**
92
+ * The tokens of the line.
93
+ * @returns them, or null when a quote is not closed
94
+ */
95
+ finish() {
96
+ if (this.quoted) {
97
+ return null;
98
+ }
99
+ this.endToken();
100
+ return this.tokens;
101
+ }
102
+ /**
103
+ * Open or close a quote. One that opens inside a token, or closes with more of the token after
104
+ * it, is noted.
105
+ * @param i - the quote's index
106
+ */
107
+ quote(i) {
108
+ const next = this.line.codePointAt(i + 1);
109
+ const joined = this.quoted ? i + 1 < this.line.length && !isBlank(next) : this.started;
110
+ if (joined && this.notes !== void 0) {
111
+ this.notes.oddQuote = true;
112
+ }
113
+ this.quoted = !this.quoted;
114
+ this.started = true;
79
115
  }
80
- if (started) {
81
- tokens.push(current);
116
+ /**
117
+ * Where a `[` that starts a token is closed, when no other `[` comes first.
118
+ * @param i - the index of the `[`
119
+ * @returns the index of its `]`, or -1
120
+ */
121
+ intervalEnd(i) {
122
+ const close = this.line.indexOf("]", i);
123
+ return close > i && this.line.lastIndexOf("[", close) === i ? close : -1;
82
124
  }
83
- return tokens;
125
+ /** End the token being read, if any. */
126
+ endToken() {
127
+ if (this.started) {
128
+ this.tokens.push(this.current);
129
+ this.current = "";
130
+ this.started = false;
131
+ }
132
+ }
133
+ }
134
+ function isBlank(c) {
135
+ return c === 32 || c === 9 || c === 13 || c === 12 || c === 11;
136
+ }
137
+ function isProjectObject(keyword) {
138
+ return PROJECT_OBJECTS.has(keyword.toLowerCase());
84
139
  }
140
+ const PROJECT_OBJECTS = /* @__PURE__ */ new Set(["permutation", "cluster", "hierarchy"]);
85
141
  function isCommentOrBlank(line) {
86
142
  for (let i = 0; i < line.length; i++) {
87
143
  const c = line.charCodeAt(i);
@@ -187,8 +243,11 @@ function parseIntervals(token) {
187
243
  if (match === null) {
188
244
  throw new Error(`malformed time interval ${token}: "${part}" is not a-b, a-* or a`);
189
245
  }
190
- const start = match[1] === "*" ? -Infinity : Number(match[1]);
191
- const endText = match[2];
246
+ const [, startText, endText] = match;
247
+ if (startText === "*" && endText === void 0) {
248
+ throw new Error(`malformed time interval ${token}: "*" alone is not a time point`);
249
+ }
250
+ const start = startText === "*" ? -Infinity : Number(startText);
192
251
  let end;
193
252
  if (endText === void 0) {
194
253
  end = start;
@@ -197,21 +256,28 @@ function parseIntervals(token) {
197
256
  } else {
198
257
  end = Number(endText);
199
258
  }
259
+ if (startText !== "*" && !Number.isFinite(start) || endText !== void 0 && endText !== "*" && !Number.isFinite(end)) {
260
+ throw new Error(`malformed time interval ${token}: "${part}" is beyond the number range`);
261
+ }
200
262
  if (start > end) {
201
- throw new Error(`malformed time interval ${token}: ${match[1]} is after ${endText ?? ""}`);
263
+ throw new Error(`malformed time interval ${token}: ${startText} is after ${endText ?? ""}`);
202
264
  }
203
265
  spells.push([start, end]);
204
266
  }
205
267
  return spells;
206
268
  }
207
- const CHARACTER_REFERENCE = /&#(?:[xX]([0-9a-fA-F]{1,6})|([0-9]{1,7}));/g;
208
- function decodeCharacterReferences(text) {
269
+ const CHARACTER_REFERENCE = /&#(?:[xX]([\da-fA-F]+)|(\d+));/g;
270
+ function decodeCharacterReferences(text, onUnknown) {
209
271
  if (!text.includes("&#")) {
210
272
  return text;
211
273
  }
212
274
  return text.replace(CHARACTER_REFERENCE, (whole, hex, dec) => {
213
275
  const code = hex === void 0 ? Number(dec) : Number.parseInt(hex, 16);
214
- return code <= 1114111 ? String.fromCodePoint(code) : whole;
276
+ if (code <= 1114111) {
277
+ return String.fromCodePoint(code);
278
+ }
279
+ onUnknown?.(whole);
280
+ return whole;
215
281
  });
216
282
  }
217
283
  function encodeCharacterReferences(text) {
@@ -233,42 +299,63 @@ function formatIntervals(spells) {
233
299
  return `[${parts.join(",")}]`;
234
300
  }
235
301
  function isParameterKey(text) {
236
- if (text.length === 0 || /[\s"]/.test(text) || text.startsWith("[") || text.startsWith("*")) {
302
+ if (text.length === 0 || /[\s"]/.test(text) || text.startsWith("[") || text.startsWith("*") || text.startsWith("%")) {
237
303
  return false;
238
304
  }
239
305
  if (isShapeKeyword(text)) {
240
306
  return false;
241
307
  }
242
- return !/^[+-]?(\.[0-9]|[0-9])/.test(text);
308
+ return !/^[+-]?(\.\d|\d)/.test(text) && !/^[+-]?(?:nan|inf|infinity)$/i.test(text);
243
309
  }
244
310
  const PAJEK_LOSS = Object.freeze({
245
- /** A label or text value holds a double quote or a line break; export() will throw E_UNSUPPORTED. */
311
+ /**
312
+ * A label or text value holds a double quote or a line break, which Pajek cannot write; the save fails with
313
+ * E_UNSUPPORTED.
314
+ */
246
315
  TEXT: "E_PAJEK_TEXT",
247
316
  /** A column whose name cannot be a parameter key (whitespace, a quote, numeric, a shape keyword); skipped. */
248
317
  KEY_DROPPED: "W_PAJEK_KEY_DROPPED",
249
- /** A label role column that is not text; its values are written as text and re-import as string. */
318
+ /**
319
+ * The node label attribute holds numbers or other values that are not text; they are written as text and read back
320
+ * as text.
321
+ */
250
322
  LABEL_AS_TEXT: "W_PAJEK_LABEL_AS_TEXT",
251
- /** A non-finite f64 value; written as Infinity / NaN text, which re-imports as string. */
323
+ /** NaN or an infinity is written as the text Infinity or NaN, which reads back as text. */
252
324
  NONFINITE_AS_TEXT: "W_PAJEK_NONFINITE_AS_TEXT",
253
325
  /** A mutual pair; written as one undirected edge, the mark lost. */
254
326
  MUTUAL_AS_UNDIRECTED: LOSS.MUTUAL_AS_UNDIRECTED,
255
327
  /** A start / end / timestamp role column; Pajek intervals are written from the spells role only. */
256
328
  TEMPORAL_DROPPED: LOSS.TEMPORAL,
257
- /** A position column with a stride other than 2 or 3. */
329
+ /** A position with other than 2 or 3 coordinates is not written. */
258
330
  POSITION_STRIDE: "W_PAJEK_POSITION_STRIDE",
259
- /** meta.extra.pajek.firstMode is not a count within 0..N; the two-mode header is not written. */
331
+ /**
332
+ * The graph's two-mode vertex count (`meta.extra.pajek.firstMode`) is not between 0 and the number of nodes, so
333
+ * the file is written as a one-mode network.
334
+ */
260
335
  FIRST_MODE_DROPPED: "W_PAJEK_FIRST_MODE_DROPPED",
261
- /** A role column Pajek has no slot for (kind, ...) written as a plain parameter; the role is lost. */
336
+ /** An attribute with a role the format has no place for is written as a plain attribute; the role is lost. */
262
337
  ROLE_DROPPED: LOSS.ROLE,
263
- /** A `shape` column with a value outside the shape keywords is written as a parameter (a string on re-import). */
338
+ /**
339
+ * A `shape` attribute whose value is not one of Pajek's shape names is written as a parameter, and reads back as a
340
+ * plain text attribute.
341
+ */
264
342
  SHAPE_AS_PARAMETER: "W_PAJEK_SHAPE_AS_PARAMETER",
265
343
  /** A vertex line with coordinates, a shape or parameters needs a label: the id text is written and reads back as a label. */
266
344
  LABEL_GAINED: "W_PAJEK_LABEL_GAINED",
267
- /** A role-less node column named `label` reads back with the label role (its values become the vertex labels). */
345
+ /**
346
+ * An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and
347
+ * reads back with that role.
348
+ */
268
349
  ROLE_ASSUMED: LOSS.ROLE_ASSUMED,
269
- /** Under sanitizeIds "mangle": an original id whose text reads back as the other type under ids "canonical". */
350
+ /**
351
+ * With `sanitizeIds: "mangle"`: an original id that reads back as a different type, such as the text "7" as the
352
+ * number 7.
353
+ */
270
354
  ID_TEXT_TYPE: LOSS.ID_TEXT_TYPE,
271
- /** Parameter text that looks like a number or a boolean reads back as one (parameters are untyped, design 5.1). */
355
+ /**
356
+ * A text value that reads back as a number or a boolean, because the format does not record that it was text (the
357
+ * text "42" reads back as the number 42).
358
+ */
272
359
  TEXT_INFERRED: LOSS.TEXT_INFERRED
273
360
  });
274
361
  const SLOT_ROLES = /* @__PURE__ */ new Set(["label"]);
@@ -453,7 +540,7 @@ function labelNotes(snapshot, nodePlan, notes) {
453
540
  notes.push(
454
541
  note(
455
542
  PAJEK_LOSS.LABEL_GAINED,
456
- `${gained} vertex line(s) carry coordinates, a shape or parameters and need a label; the id text is written there and reads back as a label`,
543
+ `${gained} vertex line${plural(gained)} ${agree(gained, "carries", "carry")} coordinates, a shape or parameters and need a label; the id text is written there and reads back as a label`,
457
544
  labels === null ? LABEL_COLUMN : labels.meta.name,
458
545
  gained
459
546
  )
@@ -554,7 +641,7 @@ function checkCells(planned, domain, notes) {
554
641
  notes.push(
555
642
  note(
556
643
  PAJEK_LOSS.TEXT,
557
- `${badText} value(s) of ${domain} column "${name}" hold a double quote or a line break; Pajek cannot write them`,
644
+ `${badText} value${plural(badText)} of ${domain} column "${name}" hold a double quote or a line break; Pajek cannot write them`,
558
645
  name,
559
646
  badText
560
647
  )
@@ -564,7 +651,7 @@ function checkCells(planned, domain, notes) {
564
651
  notes.push(
565
652
  note(
566
653
  PAJEK_LOSS.TEXT_INFERRED,
567
- `${typed} value(s) of ${domain} column "${name}" look like numbers or booleans; Pajek parameters are untyped and they read back as such`,
654
+ `${typed} value${plural(typed)} of ${domain} column "${name}" look like numbers or booleans; Pajek parameters are untyped and they read back as such`,
568
655
  name,
569
656
  typed
570
657
  )
@@ -574,7 +661,7 @@ function checkCells(planned, domain, notes) {
574
661
  notes.push(
575
662
  note(
576
663
  PAJEK_LOSS.NONFINITE_AS_TEXT,
577
- `${nonFinite} non-finite value(s) of ${domain} column "${name}" are written as text`,
664
+ `${nonFinite} non-finite value${plural(nonFinite)} of ${domain} column "${name}" ${agree(nonFinite, "is", "are")} written as text`,
578
665
  name,
579
666
  nonFinite
580
667
  )
@@ -605,9 +692,6 @@ function labelOf(labels, ids, i, needed) {
605
692
  }
606
693
  return null;
607
694
  }
608
- function idText(id) {
609
- return typeof id === "number" ? formatF64(id) : id;
610
- }
611
695
  function spellsText(column, row) {
612
696
  if (column.dtype !== "list" || !column.isSet(row)) {
613
697
  return null;
@@ -670,8 +754,8 @@ function* writeParts(snapshot, options) {
670
754
  });
671
755
  }
672
756
  }
673
- if (options.networkHeader && snapshot.meta.name !== null) {
674
- yield `*Network ${snapshot.meta.name}
757
+ if (options.networkHeader) {
758
+ yield options.name === null ? "*Network\n" : `*Network ${options.name}
675
759
  `;
676
760
  }
677
761
  const firstMode = firstModeOf(snapshot);
@@ -706,7 +790,7 @@ function checkIdTexts(nodeCount, ids, mangle, notes) {
706
790
  notes.push(
707
791
  note(
708
792
  PAJEK_LOSS.TEXT,
709
- `${badText} node id(s) hold a double quote or a line break; Pajek cannot write them as labels or parameters`,
793
+ `${badText} node id${plural(badText)} ${agree(badText, "holds", "hold")} a double quote or a line break; Pajek cannot write them as labels or parameters`,
710
794
  null,
711
795
  badText
712
796
  )
@@ -716,7 +800,7 @@ function checkIdTexts(nodeCount, ids, mangle, notes) {
716
800
  notes.push(
717
801
  note(
718
802
  PAJEK_LOSS.ID_TEXT_TYPE,
719
- `${typeChanged} original id(s) read back from ${ORIGINAL_ID_KEY} as the other type under ids: "canonical" (a string "1" becomes 1, a number 1.5 becomes "1.5")`,
803
+ `${typeChanged} original id${plural(typeChanged)} ${agree(typeChanged, "reads", "read")} back from ${ORIGINAL_ID_KEY} as the other type under ids: "canonical" (a string "1" becomes 1, a number 1.5 becomes "1.5")`,
720
804
  null,
721
805
  typeChanged
722
806
  )
@@ -879,7 +963,7 @@ function* writeLines(snapshot, edgePlan) {
879
963
  yield snapshot.directed ? "*Arcs\n" : "*Edges\n";
880
964
  }
881
965
  }
882
- function resolvePajekOptions(options) {
966
+ function resolvePajekOptions(snapshot, options) {
883
967
  const common = resolveExportOptions(options);
884
968
  const value = options?.networkHeader;
885
969
  if (value !== void 0 && typeof value !== "boolean") {
@@ -888,10 +972,22 @@ function resolvePajekOptions(options) {
888
972
  found: typeof value
889
973
  });
890
974
  }
891
- return { networkHeader: value ?? false, mangle: common.sanitizeIds === "mangle" };
975
+ const name = options?.name;
976
+ if (name !== void 0 && (typeof name !== "string" || /[\r\n]/.test(name))) {
977
+ throw new GraphFormatError("E_UNSUPPORTED", "option name: not a one-line string", {
978
+ option: "name",
979
+ found: typeof name
980
+ });
981
+ }
982
+ return {
983
+ networkHeader: value ?? name !== void 0,
984
+ name: name ?? snapshot.meta.name,
985
+ mangle: common.sanitizeIds === "mangle"
986
+ };
892
987
  }
893
988
  const pajekExporter = Object.freeze({
894
989
  format: "pajek",
990
+ options: Object.freeze(["name", "networkHeader"]),
895
991
  capabilities: CAPABILITIES,
896
992
  /**
897
993
  * Pre-flight: the generic capability gaps plus the Pajek-specific ones.
@@ -901,7 +997,7 @@ const pajekExporter = Object.freeze({
901
997
  */
902
998
  check(snapshot, options) {
903
999
  const resolved = resolveExportOptions(options);
904
- const { mangle } = resolvePajekOptions(options);
1000
+ const { mangle } = resolvePajekOptions(snapshot, options);
905
1001
  const notes = [];
906
1002
  const planned = plan(snapshot.nodes, "node", notes);
907
1003
  const nodePlan = withoutReservedKey(planned, mangle);
@@ -971,7 +1067,7 @@ const pajekExporter = Object.freeze({
971
1067
  notes.push(
972
1068
  note(
973
1069
  PAJEK_LOSS.MUTUAL_AS_UNDIRECTED,
974
- `${folding.mutualCount} mutual pair(s) are written as undirected edges; the mutual mark is lost`,
1070
+ `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as undirected edges; the mutual mark is lost`,
975
1071
  snapshot.edges.byRole("mutual")?.meta.name ?? null,
976
1072
  folding.mutualCount
977
1073
  )
@@ -994,7 +1090,7 @@ const pajekExporter = Object.freeze({
994
1090
  * @returns the chunks
995
1091
  */
996
1092
  export(snapshot, options) {
997
- const resolved = resolvePajekOptions(options);
1093
+ const resolved = resolvePajekOptions(snapshot, options);
998
1094
  return encodeChunks(writeParts(snapshot, resolved));
999
1095
  },
1000
1096
  /**
@@ -1004,32 +1100,47 @@ const pajekExporter = Object.freeze({
1004
1100
  * @returns the document
1005
1101
  */
1006
1102
  exportToString(snapshot, options) {
1007
- const resolved = resolvePajekOptions(options);
1103
+ const resolved = resolvePajekOptions(snapshot, options);
1008
1104
  return joinText(writeParts(snapshot, resolved));
1009
1105
  }
1010
1106
  });
1011
1107
  const PAJEK_ISSUE = Object.freeze({
1012
- /** The input holds invalid UTF-8 (fatal). */
1108
+ ...INPUT_ISSUE,
1109
+ ...ELEMENT_ISSUE,
1110
+ /** The input is not valid UTF-8. The import stops. */
1013
1111
  INVALID_UTF8: INVALID_UTF8_CODE,
1014
- /** Invalid bytes in the encoding a BOM, a declaration or the encoding option chose (fatal). */
1112
+ /**
1113
+ * Some bytes are not valid in the encoding that was chosen (by a byte order mark, the file's declaration or the
1114
+ * `encoding` option). The import stops.
1115
+ */
1015
1116
  INVALID_ENCODING: INVALID_ENCODING_CODE,
1016
1117
  /** Bytes that are not UTF-8 and declare no encoding were read as windows-1252. */
1017
1118
  ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
1018
1119
  /** A declared encoding the platform cannot decode was ignored. */
1019
1120
  UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
1020
- /** Fatal: no `*Vertices` section (an empty file, or not a Pajek network). */
1121
+ /** There is no `*Vertices` section: the file is empty or not a Pajek network. The import stops. */
1021
1122
  NO_VERTICES: "E_PAJEK_NO_VERTICES",
1022
- /** Fatal: `*Vertices` without a vertex count, one the sink cannot hold, or a first-mode count outside 0..N. */
1123
+ /**
1124
+ * `*Vertices` without a vertex count, with a count too large to hold, or with a first-mode count outside 0 to N;
1125
+ * the import stops.
1126
+ */
1023
1127
  VERTICES_COUNT: "E_PAJEK_VERTICES_COUNT",
1024
- /** A project file holds several networks; import() reads the first, importAll() reads every one. */
1128
+ /**
1129
+ * The file holds several graphs and only the first was read. It is not added when `graphIndex` or `graphName` chose the graph.
1130
+ * `importAllGraphs()` reads every one.
1131
+ */
1025
1132
  MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
1133
+ /** `graphIndex` or `graphName` names no graph of the file; the message lists the graphs it holds. The import stops. */
1134
+ GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
1135
+ /** `graphName` matches more than one graph; pass `graphIndex`. The import stops. */
1136
+ AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
1026
1137
  /** A data line before the first section header. */
1027
1138
  OUTSIDE_SECTION: "E_PAJEK_OUTSIDE_SECTION",
1028
1139
  /** A section header the importer cannot parse. */
1029
1140
  SYNTAX: SYNTAX_CODE,
1030
1141
  /** A double quote not closed before the end of the line. */
1031
1142
  UNTERMINATED_QUOTE: "E_PAJEK_UNTERMINATED_QUOTE",
1032
- /** A vertex line the grammar does not accept. */
1143
+ /** A vertex line that is not in Pajek's vertex syntax; the vertex is skipped. */
1033
1144
  VERTEX_LINE: "E_PAJEK_VERTEX_LINE",
1034
1145
  /** A vertex number outside the declared range. */
1035
1146
  VERTEX_RANGE: "E_PAJEK_VERTEX_RANGE",
@@ -1037,9 +1148,12 @@ const PAJEK_ISSUE = Object.freeze({
1037
1148
  VERTEX_COUNT: "W_PAJEK_VERTEX_COUNT",
1038
1149
  /** A second line for the same vertex; the later values overwrite. */
1039
1150
  DUPLICATE_NODE: DUPLICATE_NODE_CODE,
1040
- /** A line (arc, edge, list, matrix row, partition or vector value) the grammar does not accept. */
1151
+ /**
1152
+ * A line that is not in Pajek's syntax for its section (an arc, edge, list, matrix row, partition or vector
1153
+ * value); it is skipped.
1154
+ */
1041
1155
  LINE: "E_PAJEK_LINE",
1042
- /** A line endpoint outside the declared vertex range (the core's code, forwarded). */
1156
+ /** An edge names a vertex number outside the range `*Vertices` declared; the edge is skipped. */
1043
1157
  UNKNOWN_NODE: "E_UNKNOWN_NODE",
1044
1158
  /** A malformed time interval token. */
1045
1159
  INTERVAL: "E_PAJEK_INTERVAL",
@@ -1051,32 +1165,57 @@ const PAJEK_ISSUE = Object.freeze({
1051
1165
  OBJECT_COUNT: "E_PAJEK_OBJECT_COUNT",
1052
1166
  /** A project-file section (`*Events`, `*Permutation`, ...) the importer does not read; its lines are skipped. */
1053
1167
  UNSUPPORTED_SECTION: "W_PAJEK_UNSUPPORTED_SECTION",
1054
- /** Tokens after a section header the grammar does not account for. */
1168
+ /** A section header has extra words Pajek does not define; they are ignored. */
1055
1169
  HEADER_EXTRA: "W_PAJEK_HEADER_EXTRA",
1056
1170
  /** The file declares vertices but no line section. */
1057
1171
  NO_LINES: "W_PAJEK_NO_LINES",
1058
- /** Vertex numbering starts at 0 rather than 1. */
1172
+ /** Vertex numbering starts at 0 rather than 1, found by `firstVertex: "auto"` (passing `firstVertex: 0` says so and gives no warning). */
1059
1173
  ZERO_BASED: "W_PAJEK_ZERO_BASED",
1060
1174
  /** Vertex lines mix two and three coordinates. */
1061
1175
  COORD_DIMS: "W_PAJEK_COORD_DIMS",
1062
1176
  /** Two vertices share a label under nodeIdFrom "label" and became one node. */
1063
1177
  LABEL_MERGED: "W_PAJEK_LABEL_MERGED",
1064
- /** Two distinct label texts became one numeric id under ids "number". */
1178
+ /** Two different labels became the same number because `ids` is "number", so their vertices were merged. */
1065
1179
  ID_MERGED: ID_MERGED_CODE,
1066
- /** A parameter column of `2.0`-style text kept at the value-inferred dtype (the sink cannot widen). */
1180
+ /**
1181
+ * Your graph builder cannot change an attribute's type after its first value, so a text column keeps the type of
1182
+ * its first values.
1183
+ */
1067
1184
  WIDENING_UNSUPPORTED: WIDENING_UNSUPPORTED_CODE,
1068
- /** A structural column (label, position, shape, spells, relation) renamed `<name>#<id>` because the name was taken (design section 5.6). */
1185
+ /** A structural column (label, position, shape, spells, relation) renamed `<name>#<id>` because the name was taken. */
1069
1186
  COLUMN_RENAMED: RENAMED_CODE,
1070
- /** A structural column declared without its role because the sink already holds it. */
1187
+ /**
1188
+ * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
1189
+ * a plain attribute.
1190
+ */
1071
1191
  ROLE_TAKEN: ROLE_TAKEN_CODE,
1072
1192
  /** Two vertices carry the same `graphty_originalId` under restoreMangledIds and became one node. */
1073
1193
  ORIGINAL_ID_MERGED: "W_PAJEK_ORIGINAL_ID_MERGED",
1074
1194
  /** A vertex line's `graphty_originalId` came after a later vertex's line had created it under its number. */
1075
1195
  ORIGINAL_ID_UNRESTORED: "W_PAJEK_ORIGINAL_ID_UNRESTORED",
1076
- /** A common option the importer has no use for (weightFrom naming a parameter and restoreMangledIds are honoured; long, hyperedges are not). */
1196
+ /** You set an option this format does not use; it had no effect. The message names the option. */
1077
1197
  OPTION_IGNORED: OPTION_IGNORED_CODE,
1078
- /** A builder-policy option the caller passed that the caller's sink does not use (the shared W_SINK_OPTION). */
1079
- SINK_OPTION: SINK_OPTION_CODE
1198
+ /**
1199
+ * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
1200
+ * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
1201
+ */
1202
+ SINK_OPTION: SINK_OPTION_CODE,
1203
+ /** A double quote inside a token (a CSV-style doubled quote, a quote mid-word): removed and the parts joined. */
1204
+ QUOTE_IN_TOKEN: "W_PAJEK_QUOTE_IN_TOKEN",
1205
+ /** The same parameter twice on one line; the later value stands. */
1206
+ DUPLICATE_ATTRIBUTE: DUPLICATE_ATTRIBUTE_CODE,
1207
+ /** A character reference beyond U+10FFFF in a label; kept as written. */
1208
+ REFERENCE_RANGE: "W_PAJEK_REFERENCE_RANGE",
1209
+ /** A line of a two-mode network whose endpoints are both in one mode. */
1210
+ TWO_MODE_LINE: "W_PAJEK_TWO_MODE_LINE",
1211
+ /** A bare non-integer number read as a vertex label before two coordinates (`1 0.1 0.2 0.3`); it may be an x y z line without a label. */
1212
+ NUMERIC_LABEL: "W_PAJEK_NUMERIC_LABEL",
1213
+ /** A relation number given a second name by a later `*Arcs :k "name"` header. */
1214
+ RELATION_RENAMED: "W_PAJEK_RELATION_RENAMED",
1215
+ /** A negative vertex number in an adjacency list, read as its absolute value. */
1216
+ NEGATIVE_LIST_ENTRY: "W_PAJEK_NEGATIVE_LIST_ENTRY",
1217
+ /** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
1218
+ PRECISION: PRECISION_CODE
1080
1219
  });
1081
1220
  const USED_OPTIONS = /* @__PURE__ */ new Set([
1082
1221
  "ids",
@@ -1096,6 +1235,8 @@ const USED_OPTIONS = /* @__PURE__ */ new Set([
1096
1235
  const DEFAULTS = { ids: "canonical", defaultDirected: true, weightFrom: VALUE_FIELD };
1097
1236
  const ABORT_CHECK_INTERVAL = 64;
1098
1237
  const FIRST_VERTEX_VALUES = /* @__PURE__ */ new Set([0, 1, "auto"]);
1238
+ const NUMERAL = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
1239
+ const NON_FINITE_WORD = /^[+-]?(?:nan|inf|infinity)$/i;
1099
1240
  const LABEL_DECL = {
1100
1241
  name: LABEL_COLUMN,
1101
1242
  dtype: "string",
@@ -1169,6 +1310,7 @@ class PajekParser {
1169
1310
  this.kind = "directed";
1170
1311
  this.relation = null;
1171
1312
  this.vertexCount = -1;
1313
+ this.verticesLine = 0;
1172
1314
  this.firstMode = null;
1173
1315
  this.networkName = null;
1174
1316
  this.base = 1;
@@ -1181,6 +1323,8 @@ class PajekParser {
1181
1323
  this.relationNames = /* @__PURE__ */ new Map();
1182
1324
  this.objects = [];
1183
1325
  this.objectHeader = false;
1326
+ this.firstUnsupported = null;
1327
+ this.outOfRange = 0;
1184
1328
  this.indexOfPos = new Uint32Array(0);
1185
1329
  this.idOfPos = null;
1186
1330
  this.seen = new Uint8Array(0);
@@ -1241,7 +1385,7 @@ class PajekParser {
1241
1385
  this.matrixLine(text, line);
1242
1386
  return false;
1243
1387
  case "object":
1244
- this.objectLine(text);
1388
+ this.objectLine(text, line);
1245
1389
  return false;
1246
1390
  case "skip":
1247
1391
  return false;
@@ -1265,7 +1409,8 @@ class PajekParser {
1265
1409
  */
1266
1410
  finish() {
1267
1411
  if (this.vertexCount < 0) {
1268
- this.report.fail(PAJEK_ISSUE.NO_VERTICES, "not a Pajek network: no *Vertices section found");
1412
+ const seen = this.firstUnsupported === null ? "" : ` (the file has the unrecognised section *${this.firstUnsupported})`;
1413
+ this.report.fail(PAJEK_ISSUE.NO_VERTICES, `not a Pajek network: no *Vertices section found${seen}`);
1269
1414
  }
1270
1415
  this.endSection();
1271
1416
  this.finishVertices();
@@ -1284,7 +1429,7 @@ class PajekParser {
1284
1429
  this.report.warning(
1285
1430
  "coercion",
1286
1431
  PAJEK_ISSUE.ID_MERGED,
1287
- `${this.coercer.mergeCount} label(s) merged into an id another label already produced under ids "number"`
1432
+ `${this.coercer.mergeCount} label${plural(this.coercer.mergeCount)} merged into an id another label already produced under ids "number"`
1288
1433
  );
1289
1434
  }
1290
1435
  const pajek = {
@@ -1336,7 +1481,7 @@ class PajekParser {
1336
1481
  this.report.error(
1337
1482
  "validation-error",
1338
1483
  PAJEK_ISSUE.OBJECT_COUNT,
1339
- `*${partition ? "Partition" : "Vector"} "${object.name}" declares ${declared} vertices and holds ${object.values.length} value(s); the network has ${this.vertexCount}`,
1484
+ `*${partition ? "Partition" : "Vector"} "${object.name}" declares ${declared} vertices and holds ${object.values.length} value${plural(object.values.length)}; the network has ${this.vertexCount}`,
1340
1485
  where
1341
1486
  );
1342
1487
  }
@@ -1363,7 +1508,7 @@ class PajekParser {
1363
1508
  this.report.warning(
1364
1509
  "validation-error",
1365
1510
  PAJEK_ISSUE.HEADER_EXTRA,
1366
- `ignored ${h.extra.length} unexpected token(s) after *${h.keyword}: ${h.extra.join(" ")}`,
1511
+ `ignored ${h.extra.length} unexpected token${plural(h.extra.length)} after *${h.keyword}: ${h.extra.join(" ")}`,
1367
1512
  { line }
1368
1513
  );
1369
1514
  }
@@ -1410,7 +1555,8 @@ class PajekParser {
1410
1555
  return;
1411
1556
  case "unsupported":
1412
1557
  this.section = "skip";
1413
- this.objectHeader = true;
1558
+ this.objectHeader = isProjectObject(h.keyword);
1559
+ this.firstUnsupported ??= h.keyword;
1414
1560
  this.report.warning(
1415
1561
  "unsupported",
1416
1562
  PAJEK_ISSUE.UNSUPPORTED_SECTION,
@@ -1456,6 +1602,7 @@ class PajekParser {
1456
1602
  );
1457
1603
  }
1458
1604
  this.vertexCount = h.count;
1605
+ this.verticesLine = line;
1459
1606
  if (h.secondCount !== null) {
1460
1607
  this.firstMode = h.secondCount;
1461
1608
  }
@@ -1496,7 +1643,17 @@ class PajekParser {
1496
1643
  this.kind = kind;
1497
1644
  this.relation = h.relation === null ? null : h.name ?? String(h.relation);
1498
1645
  if (h.relation !== null) {
1499
- this.relationNames.set(h.relation, this.relation ?? String(h.relation));
1646
+ const name = this.relation ?? String(h.relation);
1647
+ const before = this.relationNames.get(h.relation);
1648
+ if (before !== void 0 && before !== name) {
1649
+ this.report.warning(
1650
+ "validation-error",
1651
+ PAJEK_ISSUE.RELATION_RENAMED,
1652
+ `relation ${h.relation} was named ${JSON.stringify(before)} and is now named ${JSON.stringify(name)}; its lines carry the name of their own section`,
1653
+ { line, element: String(h.relation) }
1654
+ );
1655
+ }
1656
+ this.relationNames.set(h.relation, name);
1500
1657
  }
1501
1658
  }
1502
1659
  /**
@@ -1508,7 +1665,7 @@ class PajekParser {
1508
1665
  this.report.error(
1509
1666
  "validation-error",
1510
1667
  PAJEK_ISSUE.MATRIX_ROWS,
1511
- `*Matrix has ${this.matrixRows} row(s); *Vertices declares ${rows}`
1668
+ `*Matrix has ${this.matrixRows} row${plural(this.matrixRows)}; *Vertices declares ${rows}`
1512
1669
  );
1513
1670
  }
1514
1671
  this.section = "none";
@@ -1523,7 +1680,7 @@ class PajekParser {
1523
1680
  setBase(base, line) {
1524
1681
  this.base = base;
1525
1682
  this.baseKnown = true;
1526
- if (base === 0) {
1683
+ if (base === 0 && this.firstVertex === "auto") {
1527
1684
  this.report.warning(
1528
1685
  "coercion",
1529
1686
  PAJEK_ISSUE.ZERO_BASED,
@@ -1543,12 +1700,32 @@ class PajekParser {
1543
1700
  }
1544
1701
  this.nodesCreated = true;
1545
1702
  const n = this.vertexCount;
1546
- const { sink } = this;
1547
1703
  for (let pos = 0; pos < n; pos++) {
1548
- this.indexOfPos[pos] = sink.addNode(this.idOfNumber(pos + this.base));
1704
+ this.indexOfPos[pos] = this.addNode(this.idOfNumber(pos + this.base));
1549
1705
  }
1550
1706
  this.report.counts.nodes += n;
1551
1707
  }
1708
+ /**
1709
+ * Add a vertex to the sink. A sink that cannot hold the declared vertex set (the builder's
1710
+ * E_TOO_LARGE id map limit) makes the file unreadable, so its refusal is fatal; any other
1711
+ * error passes to the caller.
1712
+ * @param id - the vertex id
1713
+ * @returns the node index
1714
+ */
1715
+ addNode(id) {
1716
+ try {
1717
+ return this.sink.addNode(id);
1718
+ } catch (err) {
1719
+ if (!(err instanceof GraphFormatError) || err.code !== "E_TOO_LARGE") {
1720
+ throw err;
1721
+ }
1722
+ return this.report.fail(
1723
+ PAJEK_ISSUE.VERTICES_COUNT,
1724
+ `*Vertices ${this.vertexCount}: the sink cannot hold that many (${err.message})`,
1725
+ { line: this.verticesLine }
1726
+ );
1727
+ }
1728
+ }
1552
1729
  /**
1553
1730
  * The id of a vertex number under nodeIdFrom "id" (the number coerced by the ids rule) or
1554
1731
  * "index" (the position).
@@ -1603,12 +1780,12 @@ class PajekParser {
1603
1780
  }
1604
1781
  }
1605
1782
  }
1606
- const lines = this.seenCount + this.report.counts.skippedNodes;
1783
+ const lines = this.seenCount + this.report.counts.skippedNodes - this.outOfRange;
1607
1784
  if (this.seenCount > 0 && lines < n) {
1608
1785
  this.report.warning(
1609
1786
  "validation-error",
1610
1787
  PAJEK_ISSUE.VERTEX_COUNT,
1611
- `*Vertices declares ${n} vertices but ${lines} vertex line(s) were read; the others have no label`
1788
+ `*Vertices declares ${n} vertices but ${lines} vertex line${plural(lines)} ${agree(lines, "was", "were")} read; the others have no label`
1612
1789
  );
1613
1790
  }
1614
1791
  }
@@ -1618,7 +1795,7 @@ class PajekParser {
1618
1795
  * @param line - the line number
1619
1796
  */
1620
1797
  vertexLine(text, line) {
1621
- const tokens = this.tokens(text, "node");
1798
+ const tokens = this.tokens(text, "node", line);
1622
1799
  if (!isVertexNumber(tokens[0])) {
1623
1800
  this.report.counts.skippedNodes++;
1624
1801
  throw new LineError(
@@ -1634,6 +1811,7 @@ class PajekParser {
1634
1811
  const pos = k - this.base;
1635
1812
  if (pos < 0 || pos >= this.vertexCount) {
1636
1813
  this.report.counts.skippedNodes++;
1814
+ this.outOfRange++;
1637
1815
  throw new LineError(
1638
1816
  "validation-error",
1639
1817
  PAJEK_ISSUE.VERTEX_RANGE,
@@ -1643,7 +1821,7 @@ class PajekParser {
1643
1821
  let i = 1;
1644
1822
  let label = null;
1645
1823
  if (tokens.length > 1 && !startsWithCoordinates(text, tokens)) {
1646
- label = decodeCharacterReferences(tokens[1]);
1824
+ label = this.labelOf(tokens[1], k, line);
1647
1825
  i = 2;
1648
1826
  }
1649
1827
  let x = 0;
@@ -1668,12 +1846,13 @@ class PajekParser {
1668
1846
  `vertex ${k}: a single coordinate "${tokens[i]}"; coordinates are x y [z]`
1669
1847
  );
1670
1848
  }
1849
+ this.checkCoordinates(text, tokens, i, dims === 2 && label !== null, k, line);
1671
1850
  let shape = null;
1672
1851
  if (i < tokens.length && isShapeKeyword(tokens[i])) {
1673
1852
  shape = tokens[i].toLowerCase();
1674
1853
  i++;
1675
1854
  }
1676
- const extras = this.extras(tokens, i, "node");
1855
+ const extras = this.extras(tokens, i, "node", line);
1677
1856
  const restored = this.restoredId(extras);
1678
1857
  this.createNodes();
1679
1858
  const repeated = this.seen[pos] !== 0;
@@ -1720,18 +1899,7 @@ class PajekParser {
1720
1899
  sink.setNodeValue(this.labelHandle, index, label);
1721
1900
  }
1722
1901
  if (dims > 0) {
1723
- if (this.positionHandle === INVALID_INDEX) {
1724
- this.coordDims = dims;
1725
- this.positionHandle = declareResolved(sink, "node", positionDecl(dims), this.report, { line }).handle;
1726
- } else if (dims !== this.coordDims) {
1727
- this.report.warnOnce(
1728
- "validation-error",
1729
- PAJEK_ISSUE.COORD_DIMS,
1730
- `vertex lines mix ${this.coordDims} and ${dims} coordinates; the position column records sourceDims ${this.coordDims}`,
1731
- { line, element: String(k) }
1732
- );
1733
- }
1734
- sink.setNodeValue(this.positionHandle, index, [x, y, z]);
1902
+ this.writePosition(index, dims, [x, y, z], k, line);
1735
1903
  }
1736
1904
  if (shape !== null) {
1737
1905
  if (this.shapeHandle === INVALID_INDEX && this.nodeParams.has(SHAPE_COLUMN)) {
@@ -1753,6 +1921,85 @@ class PajekParser {
1753
1921
  sink.setNodeValue(this.nodeSpellsHandle, index, extras.spells);
1754
1922
  }
1755
1923
  }
1924
+ /**
1925
+ * A vertex label as written, its character references decoded.
1926
+ * @param token - the label token
1927
+ * @param k - the vertex number
1928
+ * @param line - the line number
1929
+ * @returns the label
1930
+ */
1931
+ labelOf(token, k, line) {
1932
+ return decodeCharacterReferences(token, (reference) => {
1933
+ this.report.warnOnce(
1934
+ "validation-error",
1935
+ PAJEK_ISSUE.REFERENCE_RANGE,
1936
+ `character reference ${reference} is beyond U+10FFFF; it is kept as written`,
1937
+ { line, element: String(k) },
1938
+ `${PAJEK_ISSUE.REFERENCE_RANGE}:${reference}`
1939
+ );
1940
+ });
1941
+ }
1942
+ /**
1943
+ * Check the token after a vertex line's coordinates (or where they would stand): a numeral the
1944
+ * number range cannot hold is a broken coordinate, never a parameter named "1e999" or "NaN";
1945
+ * and a bare non-integer label before two coordinates may be the x of an x y z line.
1946
+ * @param text - the line
1947
+ * @param tokens - its tokens
1948
+ * @param i - the index after the coordinates
1949
+ * @param labelAndXy - whether the line was read as a label and two coordinates
1950
+ * @param k - the vertex number
1951
+ * @param line - the line number
1952
+ */
1953
+ checkCoordinates(text, tokens, i, labelAndXy, k, line) {
1954
+ if (i < tokens.length && (NUMERAL.test(tokens[i]) || NON_FINITE_WORD.test(tokens[i])) && !isNumericText(tokens[i])) {
1955
+ this.report.counts.skippedNodes++;
1956
+ throw new LineError(
1957
+ "parse-error",
1958
+ PAJEK_ISSUE.VERTEX_LINE,
1959
+ `vertex ${k}: coordinate "${tokens[i]}" is not a finite number`
1960
+ );
1961
+ }
1962
+ const label = tokens[1];
1963
+ if (labelAndXy && isNumericText(label) && !Number.isInteger(Number(label)) && !quotedAfterNumber(text, tokens)) {
1964
+ this.report.warnOnce(
1965
+ "validation-error",
1966
+ PAJEK_ISSUE.NUMERIC_LABEL,
1967
+ `vertex ${k}: "${label} ${tokens[2]} ${tokens[3]}" is read as the label ${label} and x y; quote the label, or it may be meant as x y z`,
1968
+ { line, element: String(k) }
1969
+ );
1970
+ }
1971
+ }
1972
+ /**
1973
+ * Write a vertex's coordinates into the position column (f32 x 3), declared on first use.
1974
+ * @param index - the node index
1975
+ * @param dims - 2 or 3, the coordinates the line gave
1976
+ * @param point - x, y, z (0 when absent)
1977
+ * @param k - the vertex number
1978
+ * @param line - the line number
1979
+ */
1980
+ writePosition(index, dims, point, k, line) {
1981
+ if (this.positionHandle === INVALID_INDEX) {
1982
+ this.coordDims = dims;
1983
+ this.positionHandle = declareResolved(this.sink, "node", positionDecl(dims), this.report, { line }).handle;
1984
+ } else if (dims !== this.coordDims) {
1985
+ this.report.warnOnce(
1986
+ "validation-error",
1987
+ PAJEK_ISSUE.COORD_DIMS,
1988
+ `vertex lines mix ${this.coordDims} and ${dims} coordinates; the position column records sourceDims ${this.coordDims}`,
1989
+ { line, element: String(k) }
1990
+ );
1991
+ }
1992
+ if (!point.every(survivesF32)) {
1993
+ this.report.warnOnce(
1994
+ "precision",
1995
+ PAJEK_ISSUE.PRECISION,
1996
+ `vertex ${k} has a coordinate the f32 position column rounds (warned once)`,
1997
+ { line, element: String(k) },
1998
+ `${PAJEK_ISSUE.PRECISION}:position`
1999
+ );
2000
+ }
2001
+ this.sink.setNodeValue(this.positionHandle, index, point);
2002
+ }
1756
2003
  /**
1757
2004
  * Whether a `graphty_originalId` parameter is restored as the vertex id: restoreMangledIds
1758
2005
  * under nodeIdFrom "id" (under "label" / "index" the parameter stays an ordinary column).
@@ -1770,7 +2017,7 @@ class PajekParser {
1770
2017
  numberedNode(ids, pos) {
1771
2018
  const id = this.idOfNumber(pos + this.base);
1772
2019
  ids[pos] = id;
1773
- this.indexOfPos[pos] = this.sink.addNode(id);
2020
+ this.indexOfPos[pos] = this.addNode(id);
1774
2021
  this.report.counts.nodes++;
1775
2022
  }
1776
2023
  /**
@@ -1807,7 +2054,7 @@ class PajekParser {
1807
2054
  deferredNode(ids, pos, k, text, line, from) {
1808
2055
  const id = text === null ? this.idOfNumber(k) : this.coercer.text(text);
1809
2056
  const before = this.sink.indexOf(id);
1810
- const index = this.sink.addNode(id);
2057
+ const index = this.addNode(id);
1811
2058
  ids[pos] = id;
1812
2059
  this.indexOfPos[pos] = index;
1813
2060
  this.report.counts.nodes++;
@@ -1828,7 +2075,7 @@ class PajekParser {
1828
2075
  * @param line - the line number
1829
2076
  */
1830
2077
  edgeLine(text, line) {
1831
- let tokens = this.tokens(text, "edge");
2078
+ let tokens = this.tokens(text, "edge", line);
1832
2079
  let { relation } = this;
1833
2080
  const prefix = RELATION_PREFIX.exec(tokens[0]);
1834
2081
  if (prefix !== null) {
@@ -1852,7 +2099,7 @@ class PajekParser {
1852
2099
  valueText = tokens[i];
1853
2100
  i++;
1854
2101
  }
1855
- const extras = this.extras(tokens, i, "edge");
2102
+ const extras = this.extras(tokens, i, "edge", line);
1856
2103
  const { weightFrom } = this.options;
1857
2104
  let weight;
1858
2105
  let valueIsWeight = false;
@@ -1882,7 +2129,7 @@ class PajekParser {
1882
2129
  * @param line - the line number
1883
2130
  */
1884
2131
  listLine(text, line) {
1885
- const tokens = this.tokens(text, "edge");
2132
+ const tokens = this.tokens(text, "edge", line);
1886
2133
  for (const token of tokens) {
1887
2134
  if (!LIST_VERTEX.test(token)) {
1888
2135
  this.report.counts.skippedEdges++;
@@ -1893,6 +2140,14 @@ class PajekParser {
1893
2140
  );
1894
2141
  }
1895
2142
  }
2143
+ if (tokens.some((token) => token.startsWith("-"))) {
2144
+ this.report.warnOnce(
2145
+ "coercion",
2146
+ PAJEK_ISSUE.NEGATIVE_LIST_ENTRY,
2147
+ "an adjacency list holds a negative vertex number; it is read as its absolute value and the sign is dropped",
2148
+ { line }
2149
+ );
2150
+ }
1896
2151
  const u = this.endpoint(Math.abs(Number(tokens[0])));
1897
2152
  for (let i = 1; i < tokens.length; i++) {
1898
2153
  const v = this.endpoint(Math.abs(Number(tokens[i])));
@@ -1908,7 +2163,7 @@ class PajekParser {
1908
2163
  * @param line - the line number
1909
2164
  */
1910
2165
  matrixLine(text, line) {
1911
- const tokens = this.tokens(text, "edge");
2166
+ const tokens = this.tokens(text, "edge", line);
1912
2167
  const row = this.matrixRows;
1913
2168
  this.matrixRows++;
1914
2169
  const offset = this.firstMode ?? 0;
@@ -1918,14 +2173,14 @@ class PajekParser {
1918
2173
  throw new LineError(
1919
2174
  "validation-error",
1920
2175
  PAJEK_ISSUE.MATRIX_ROWS,
1921
- `*Matrix row ${row + 1} is beyond the ${rows} declared row(s)`
2176
+ `*Matrix row ${row + 1} is beyond the ${rows} declared row${plural(rows)}`
1922
2177
  );
1923
2178
  }
1924
2179
  if (tokens.length < columns) {
1925
2180
  throw new LineError(
1926
2181
  "parse-error",
1927
2182
  PAJEK_ISSUE.LINE,
1928
- `*Matrix row ${row + 1} has ${tokens.length} value(s); the matrix has ${columns} column(s)`
2183
+ `*Matrix row ${row + 1} has ${tokens.length} value${plural(tokens.length)}; the matrix has ${columns} column${plural(columns)}`
1929
2184
  );
1930
2185
  }
1931
2186
  for (let j = 0; j < columns; j++) {
@@ -1941,7 +2196,7 @@ class PajekParser {
1941
2196
  this.report.warnOnce(
1942
2197
  "validation-error",
1943
2198
  PAJEK_ISSUE.MATRIX_EXTRA,
1944
- `*Matrix row ${row + 1} has ${tokens.length} value(s) for ${columns} column(s); the extra values are ignored`,
2199
+ `*Matrix row ${row + 1} has ${tokens.length} value${plural(tokens.length)} for ${columns} column${plural(columns)}; the extra values are ignored`,
1945
2200
  { line }
1946
2201
  );
1947
2202
  }
@@ -1958,10 +2213,11 @@ class PajekParser {
1958
2213
  * A value line of a `*Partition` (integers) or `*Vector` (numbers): the values of the next
1959
2214
  * vertices, usually one per line.
1960
2215
  * @param text - the line
2216
+ * @param line - the line number
1961
2217
  */
1962
- objectLine(text) {
2218
+ objectLine(text, line) {
1963
2219
  const object = this.objects[this.objects.length - 1];
1964
- const tokens = this.tokens(text, "node");
2220
+ const tokens = this.tokens(text, "node", line);
1965
2221
  for (const token of tokens) {
1966
2222
  if (!isNumericText(token) || object.kind === "partition" && !isI32(Number(token))) {
1967
2223
  object.values.push(null);
@@ -1990,6 +2246,14 @@ class PajekParser {
1990
2246
  this.directionSet = true;
1991
2247
  this.resolver.setHeader(this.kind === "directed", { line });
1992
2248
  }
2249
+ if (this.firstMode !== null && u < this.firstMode === v < this.firstMode) {
2250
+ this.report.warnOnce(
2251
+ "validation-error",
2252
+ PAJEK_ISSUE.TWO_MODE_LINE,
2253
+ `a line of the two-mode network joins two vertices of the ${u < this.firstMode ? "first" : "second"} mode`,
2254
+ { line }
2255
+ );
2256
+ }
1993
2257
  const before = sink.edgeCount;
1994
2258
  const e = this.resolver.addEdge(this.idAt(u), this.idAt(v), this.kind, weight, { line });
1995
2259
  this.report.counts.edges += sink.edgeCount - before;
@@ -2054,7 +2318,7 @@ class PajekParser {
2054
2318
  */
2055
2319
  weightOf(text) {
2056
2320
  try {
2057
- return parseWeightText(text);
2321
+ return parseWeightText(text, this.report);
2058
2322
  } catch (err) {
2059
2323
  this.report.counts.skippedEdges++;
2060
2324
  throw err;
@@ -2065,10 +2329,20 @@ class PajekParser {
2065
2329
  * Tokenize a data line.
2066
2330
  * @param text - the line
2067
2331
  * @param domain - what is skipped on an unterminated quote
2332
+ * @param line - the line number
2068
2333
  * @returns the tokens (at least one: blank lines never reach here)
2069
2334
  */
2070
- tokens(text, domain) {
2071
- const tokens = tokenize(text);
2335
+ tokens(text, domain, line) {
2336
+ const notes = { oddQuote: false };
2337
+ const tokens = tokenize(text, notes);
2338
+ if (notes.oddQuote) {
2339
+ this.report.warnOnce(
2340
+ "coercion",
2341
+ PAJEK_ISSUE.QUOTE_IN_TOKEN,
2342
+ "a double quote inside a token is removed and the parts joined (Pajek has no escape for a quote)",
2343
+ { line }
2344
+ );
2345
+ }
2072
2346
  if (tokens === null) {
2073
2347
  if (domain === "node") {
2074
2348
  this.report.counts.skippedNodes++;
@@ -2085,24 +2359,16 @@ class PajekParser {
2085
2359
  * @param tokens - the row's tokens
2086
2360
  * @param start - the first token to read
2087
2361
  * @param domain - what is skipped when the row is malformed
2362
+ * @param line - the line number
2088
2363
  * @returns the parsed extras
2089
2364
  */
2090
- extras(tokens, start, domain) {
2365
+ extras(tokens, start, domain, line) {
2091
2366
  const extras = { keys: [], values: [], spells: null };
2367
+ const seen = /* @__PURE__ */ new Set();
2092
2368
  for (let i = start; i < tokens.length; ) {
2093
2369
  const token = tokens[i];
2094
- if (isIntervalToken(token)) {
2095
- try {
2096
- const spells = parseIntervals(token);
2097
- extras.spells = spells.length === 0 ? null : spells;
2098
- } catch (err) {
2099
- this.skip(domain);
2100
- throw new LineError(
2101
- "validation-error",
2102
- PAJEK_ISSUE.INTERVAL,
2103
- err instanceof Error ? err.message : String(err)
2104
- );
2105
- }
2370
+ if (token.startsWith("[")) {
2371
+ extras.spells = this.intervals(token, domain);
2106
2372
  i++;
2107
2373
  continue;
2108
2374
  }
@@ -2114,12 +2380,48 @@ class PajekParser {
2114
2380
  `parameter "${token}" has no value`
2115
2381
  );
2116
2382
  }
2383
+ if (seen.has(token)) {
2384
+ this.report.warning(
2385
+ "merged",
2386
+ PAJEK_ISSUE.DUPLICATE_ATTRIBUTE,
2387
+ `parameter ${JSON.stringify(token)} is given twice on one line; the later value ${JSON.stringify(tokens[i + 1])} stands`,
2388
+ { line, element: token }
2389
+ );
2390
+ }
2391
+ seen.add(token);
2117
2392
  extras.keys.push(token);
2118
2393
  extras.values.push(tokens[i + 1]);
2119
2394
  i += 2;
2120
2395
  }
2121
2396
  return extras;
2122
2397
  }
2398
+ /**
2399
+ * The spells of a row's interval token; a malformed one skips the row.
2400
+ * @param token - the token, starting with "["
2401
+ * @param domain - what is skipped when the token is malformed
2402
+ * @returns the spells, or null for an empty `[]`, which is no spell at all
2403
+ */
2404
+ intervals(token, domain) {
2405
+ if (!isIntervalToken(token)) {
2406
+ this.skip(domain);
2407
+ throw new LineError(
2408
+ "validation-error",
2409
+ PAJEK_ISSUE.INTERVAL,
2410
+ `time interval ${token} is not closed by "]"`
2411
+ );
2412
+ }
2413
+ try {
2414
+ const spells = parseIntervals(token);
2415
+ return spells.length === 0 ? null : spells;
2416
+ } catch (err) {
2417
+ this.skip(domain);
2418
+ throw new LineError(
2419
+ "validation-error",
2420
+ PAJEK_ISSUE.INTERVAL,
2421
+ err instanceof Error ? err.message : String(err)
2422
+ );
2423
+ }
2424
+ }
2123
2425
  /**
2124
2426
  * Count a skipped element.
2125
2427
  * @param domain - node or edge
@@ -2156,7 +2458,7 @@ class PajekParser {
2156
2458
  }
2157
2459
  }
2158
2460
  function startsWithCoordinates(text, tokens) {
2159
- if (text.trimStart().slice(tokens[0].length).trimStart().startsWith('"')) {
2461
+ if (quotedAfterNumber(text, tokens)) {
2160
2462
  return false;
2161
2463
  }
2162
2464
  let run = 0;
@@ -2165,6 +2467,9 @@ function startsWithCoordinates(text, tokens) {
2165
2467
  }
2166
2468
  return run === 2;
2167
2469
  }
2470
+ function quotedAfterNumber(text, tokens) {
2471
+ return text.trimStart().slice(tokens[0].length).trimStart().startsWith('"');
2472
+ }
2168
2473
  function firstVertexOption(value) {
2169
2474
  if (value === void 0) {
2170
2475
  return "auto";
@@ -2181,23 +2486,32 @@ function firstVertexOption(value) {
2181
2486
  }
2182
2487
  );
2183
2488
  }
2184
- const HEAD_PATTERN = /^\s*\*(vertices|network)\b/i;
2489
+ function headSection(text) {
2490
+ let rest = text.trimStart();
2491
+ while (rest.startsWith("%")) {
2492
+ const end = rest.search(/[\r\n]/);
2493
+ rest = end < 0 ? "" : rest.slice(end).trimStart();
2494
+ }
2495
+ return /^\*(vertices|network)\b/i.exec(rest)?.[1].toLowerCase() ?? null;
2496
+ }
2185
2497
  const pajekImporter = Object.freeze({
2186
2498
  format: "pajek",
2499
+ options: Object.freeze(["firstVertex"]),
2187
2500
  extensions: Object.freeze([".net", ".paj"]),
2188
2501
  mimeTypes: Object.freeze(["text/x-pajek", "text/plain"]),
2189
2502
  /**
2190
- * Confidence that the input is a Pajek network: it starts with `*Vertices` (or `*Network`).
2503
+ * Confidence that the input is a Pajek network: it starts with `*Vertices` (or `*Network`),
2504
+ * after any `%` comment lines.
2191
2505
  * @param head - the first bytes
2192
2506
  * @returns 0.9 for a `*Vertices` start, 0.8 for `*Network`, 0 otherwise
2193
2507
  */
2194
2508
  sniff(head) {
2195
- const text = new TextDecoder("utf-8").decode(head.subarray(0, Math.min(head.byteLength, 512)));
2196
- const match = HEAD_PATTERN.exec(text.startsWith(String.fromCharCode(65279)) ? text.slice(1) : text);
2197
- if (match === null) {
2509
+ const text = new TextDecoder("utf-8").decode(head);
2510
+ const section = headSection(text.startsWith("\uFEFF") ? text.slice(1) : text);
2511
+ if (section === null) {
2198
2512
  return 0;
2199
2513
  }
2200
- return match[1].toLowerCase() === "vertices" ? 0.9 : 0.8;
2514
+ return section === "vertices" ? 0.9 : 0.8;
2201
2515
  },
2202
2516
  /**
2203
2517
  * Read a Pajek network into the sink.
@@ -2207,6 +2521,9 @@ const pajekImporter = Object.freeze({
2207
2521
  * @returns the report
2208
2522
  */
2209
2523
  async import(input, sink, options) {
2524
+ if (graphChosen(options)) {
2525
+ return importChosenGraph(pajekImporter, input, sink, options);
2526
+ }
2210
2527
  const resolved = resolveImportOptions(options, DEFAULTS);
2211
2528
  const firstVertex = firstVertexOption(options?.firstVertex);
2212
2529
  const report = new ImportReportBuilder("pajek", resolved.errorLimit);
@@ -2217,7 +2534,8 @@ const pajekImporter = Object.freeze({
2217
2534
  let rest = null;
2218
2535
  let restLine = 0;
2219
2536
  let sinceCheck = 0;
2220
- for await (const text of reader) {
2537
+ for await (const raw of reader) {
2538
+ const text = withoutStrayBom(raw);
2221
2539
  if (rest !== null) {
2222
2540
  rest.line(text);
2223
2541
  } else if (parser.line(text, reader.line)) {
@@ -2232,10 +2550,11 @@ const pajekImporter = Object.freeze({
2232
2550
  }
2233
2551
  parser.finish();
2234
2552
  if (rest !== null) {
2553
+ const objects = rest.objects > 0 ? `; ${rest.objects} *Partition / *Vector object(s) after them are not read` : "";
2235
2554
  report.warning(
2236
2555
  "unsupported",
2237
2556
  PAJEK_ISSUE.MULTIPLE_GRAPHS,
2238
- `the project file holds ${rest.count} more network(s) after the first; import() reads the first, importAll() reads every one`,
2557
+ `the project file holds ${rest.count} more network${plural(rest.count)} after the first; the first is read (graphIndex or graphName chooses another; importAllGraphs() reads every one)${objects}`,
2239
2558
  { line: restLine }
2240
2559
  );
2241
2560
  }
@@ -2265,7 +2584,8 @@ const pajekImporter = Object.freeze({
2265
2584
  let parser = open();
2266
2585
  const reader = new LineReader(input, reports[0], resolved);
2267
2586
  let sinceCheck = 0;
2268
- for await (const text of reader) {
2587
+ for await (const raw of reader) {
2588
+ const text = withoutStrayBom(raw);
2269
2589
  if (parser.line(text, reader.line)) {
2270
2590
  parser.finish();
2271
2591
  reports.push(new ImportReportBuilder("pajek", resolved.errorLimit));
@@ -2282,9 +2602,13 @@ const pajekImporter = Object.freeze({
2282
2602
  return reports.map((r) => r.finish());
2283
2603
  }
2284
2604
  });
2605
+ function withoutStrayBom(text) {
2606
+ return text.codePointAt(0) === 65279 ? text.slice(1) : text;
2607
+ }
2285
2608
  class NetworkCounter {
2286
2609
  constructor() {
2287
2610
  this.count = 0;
2611
+ this.objects = 0;
2288
2612
  this.named = false;
2289
2613
  this.populated = false;
2290
2614
  this.afterObjectHeader = false;
@@ -2307,8 +2631,11 @@ class NetworkCounter {
2307
2631
  return;
2308
2632
  }
2309
2633
  const { kind } = h;
2310
- if (kind === "partition" || kind === "vector" || kind === "unsupported") {
2634
+ if (kind === "partition" || kind === "vector") {
2311
2635
  this.afterObjectHeader = true;
2636
+ this.objects++;
2637
+ } else if (kind === "unsupported") {
2638
+ this.afterObjectHeader = isProjectObject(h.keyword);
2312
2639
  } else if (kind === "network") {
2313
2640
  if (this.count === 0 || this.named || this.populated) {
2314
2641
  this.count++;
@@ -2330,4 +2657,4 @@ export {
2330
2657
  pajekImporter as b,
2331
2658
  pajekExporter as p
2332
2659
  };
2333
- //# sourceMappingURL=importer-zGLo8Dg_.js.map
2660
+ //# sourceMappingURL=importer-C_TEA1zJ.js.map