@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
@@ -17,38 +17,59 @@
17
17
  */
18
18
  import { GraphFormatError, INVALID_INDEX, } from "@graphty/graph-format";
19
19
  import { declareResolved, RENAMED_CODE, ROLE_TAKEN_CODE, uniqueColumnName } from "../../common/attributes.js";
20
- import { DUPLICATE_NODE_CODE, ENCODING_FALLBACK_CODE, INVALID_ENCODING_CODE, INVALID_UTF8_CODE, MULTIPLE_GRAPHS_CODE, OPTION_IGNORED_CODE, SYNTAX_CODE, UNKNOWN_ENCODING_CODE, } from "../../common/codes.js";
20
+ import { importChosenGraph } from "../../common/choose.js";
21
+ import { AMBIGUOUS_GRAPH_NAME_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_NODE_CODE, ELEMENT_ISSUE, ENCODING_FALLBACK_CODE, GRAPH_NOT_FOUND_CODE, INPUT_ISSUE, INVALID_ENCODING_CODE, INVALID_UTF8_CODE, MULTIPLE_GRAPHS_CODE, OPTION_IGNORED_CODE, PRECISION_CODE, SYNTAX_CODE, UNKNOWN_ENCODING_CODE, } from "../../common/codes.js";
21
22
  import { DirectionResolver } from "../../common/direction.js";
23
+ import { survivesF32 } from "../../common/format.js";
22
24
  import { coerceIdText, ID_MERGED_CODE, IdCoercer } from "../../common/ids.js";
23
25
  import { LineReader, throwIfAborted } from "../../common/input.js";
24
- import { reportSinkOptions, reportUnusedOptions, resolveImportOptions, SINK_OPTION_CODE, } from "../../common/options.js";
26
+ import { graphChosen, reportSinkOptions, reportUnusedOptions, resolveImportOptions, SINK_OPTION_CODE, } from "../../common/options.js";
27
+ import { agree, plural } from "../../common/plural.js";
25
28
  import { ImportReportBuilder } from "../../common/report.js";
26
29
  import { isNumericText, TextCellWriter, WIDENING_UNSUPPORTED_CODE } from "../../common/text.js";
27
30
  import { isWeightField, parseWeightText } from "../../common/weights.js";
28
- import { decodeCharacterReferences, isCommentOrBlank, isIntervalToken, isSectionLine, isShapeKeyword, isVertexNumber, LABEL_COLUMN, ORIGINAL_ID_KEY, parseIntervals, parseSectionHeader, PARTITION_COLUMN, POSITION_COLUMN, RELATION_COLUMN, SHAPE_COLUMN, SPELLS_COLUMN, tokenize, VALUE_COLUMN, VALUE_FIELD, VECTOR_COLUMN, } from "./syntax.js";
29
- /** Issue codes of the Pajek importer. */
31
+ import { decodeCharacterReferences, isCommentOrBlank, isIntervalToken, isProjectObject, isSectionLine, isShapeKeyword, isVertexNumber, LABEL_COLUMN, ORIGINAL_ID_KEY, parseIntervals, parseSectionHeader, PARTITION_COLUMN, POSITION_COLUMN, RELATION_COLUMN, SHAPE_COLUMN, SPELLS_COLUMN, tokenize, VALUE_COLUMN, VALUE_FIELD, VECTOR_COLUMN, } from "./syntax.js";
32
+ /**
33
+ * Issue codes of the Pajek importer.
34
+ * @category Built-in formats
35
+ */
30
36
  export const PAJEK_ISSUE = Object.freeze({
31
- /** The input holds invalid UTF-8 (fatal). */
37
+ ...INPUT_ISSUE,
38
+ ...ELEMENT_ISSUE,
39
+ /** The input is not valid UTF-8. The import stops. */
32
40
  INVALID_UTF8: INVALID_UTF8_CODE,
33
- /** Invalid bytes in the encoding a BOM, a declaration or the encoding option chose (fatal). */
41
+ /**
42
+ * Some bytes are not valid in the encoding that was chosen (by a byte order mark, the file's declaration or the
43
+ * `encoding` option). The import stops.
44
+ */
34
45
  INVALID_ENCODING: INVALID_ENCODING_CODE,
35
46
  /** Bytes that are not UTF-8 and declare no encoding were read as windows-1252. */
36
47
  ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
37
48
  /** A declared encoding the platform cannot decode was ignored. */
38
49
  UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
39
- /** Fatal: no `*Vertices` section (an empty file, or not a Pajek network). */
50
+ /** There is no `*Vertices` section: the file is empty or not a Pajek network. The import stops. */
40
51
  NO_VERTICES: "E_PAJEK_NO_VERTICES",
41
- /** Fatal: `*Vertices` without a vertex count, one the sink cannot hold, or a first-mode count outside 0..N. */
52
+ /**
53
+ * `*Vertices` without a vertex count, with a count too large to hold, or with a first-mode count outside 0 to N;
54
+ * the import stops.
55
+ */
42
56
  VERTICES_COUNT: "E_PAJEK_VERTICES_COUNT",
43
- /** A project file holds several networks; import() reads the first, importAll() reads every one. */
57
+ /**
58
+ * The file holds several graphs and only the first was read. It is not added when `graphIndex` or `graphName` chose the graph.
59
+ * `importAllGraphs()` reads every one.
60
+ */
44
61
  MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
62
+ /** `graphIndex` or `graphName` names no graph of the file; the message lists the graphs it holds. The import stops. */
63
+ GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
64
+ /** `graphName` matches more than one graph; pass `graphIndex`. The import stops. */
65
+ AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
45
66
  /** A data line before the first section header. */
46
67
  OUTSIDE_SECTION: "E_PAJEK_OUTSIDE_SECTION",
47
68
  /** A section header the importer cannot parse. */
48
69
  SYNTAX: SYNTAX_CODE,
49
70
  /** A double quote not closed before the end of the line. */
50
71
  UNTERMINATED_QUOTE: "E_PAJEK_UNTERMINATED_QUOTE",
51
- /** A vertex line the grammar does not accept. */
72
+ /** A vertex line that is not in Pajek's vertex syntax; the vertex is skipped. */
52
73
  VERTEX_LINE: "E_PAJEK_VERTEX_LINE",
53
74
  /** A vertex number outside the declared range. */
54
75
  VERTEX_RANGE: "E_PAJEK_VERTEX_RANGE",
@@ -56,9 +77,12 @@ export const PAJEK_ISSUE = Object.freeze({
56
77
  VERTEX_COUNT: "W_PAJEK_VERTEX_COUNT",
57
78
  /** A second line for the same vertex; the later values overwrite. */
58
79
  DUPLICATE_NODE: DUPLICATE_NODE_CODE,
59
- /** A line (arc, edge, list, matrix row, partition or vector value) the grammar does not accept. */
80
+ /**
81
+ * A line that is not in Pajek's syntax for its section (an arc, edge, list, matrix row, partition or vector
82
+ * value); it is skipped.
83
+ */
60
84
  LINE: "E_PAJEK_LINE",
61
- /** A line endpoint outside the declared vertex range (the core's code, forwarded). */
85
+ /** An edge names a vertex number outside the range `*Vertices` declared; the edge is skipped. */
62
86
  UNKNOWN_NODE: "E_UNKNOWN_NODE",
63
87
  /** A malformed time interval token. */
64
88
  INTERVAL: "E_PAJEK_INTERVAL",
@@ -70,32 +94,57 @@ export const PAJEK_ISSUE = Object.freeze({
70
94
  OBJECT_COUNT: "E_PAJEK_OBJECT_COUNT",
71
95
  /** A project-file section (`*Events`, `*Permutation`, ...) the importer does not read; its lines are skipped. */
72
96
  UNSUPPORTED_SECTION: "W_PAJEK_UNSUPPORTED_SECTION",
73
- /** Tokens after a section header the grammar does not account for. */
97
+ /** A section header has extra words Pajek does not define; they are ignored. */
74
98
  HEADER_EXTRA: "W_PAJEK_HEADER_EXTRA",
75
99
  /** The file declares vertices but no line section. */
76
100
  NO_LINES: "W_PAJEK_NO_LINES",
77
- /** Vertex numbering starts at 0 rather than 1. */
101
+ /** Vertex numbering starts at 0 rather than 1, found by `firstVertex: "auto"` (passing `firstVertex: 0` says so and gives no warning). */
78
102
  ZERO_BASED: "W_PAJEK_ZERO_BASED",
79
103
  /** Vertex lines mix two and three coordinates. */
80
104
  COORD_DIMS: "W_PAJEK_COORD_DIMS",
81
105
  /** Two vertices share a label under nodeIdFrom "label" and became one node. */
82
106
  LABEL_MERGED: "W_PAJEK_LABEL_MERGED",
83
- /** Two distinct label texts became one numeric id under ids "number". */
107
+ /** Two different labels became the same number because `ids` is "number", so their vertices were merged. */
84
108
  ID_MERGED: ID_MERGED_CODE,
85
- /** A parameter column of `2.0`-style text kept at the value-inferred dtype (the sink cannot widen). */
109
+ /**
110
+ * Your graph builder cannot change an attribute's type after its first value, so a text column keeps the type of
111
+ * its first values.
112
+ */
86
113
  WIDENING_UNSUPPORTED: WIDENING_UNSUPPORTED_CODE,
87
- /** A structural column (label, position, shape, spells, relation) renamed `<name>#<id>` because the name was taken (design section 5.6). */
114
+ /** A structural column (label, position, shape, spells, relation) renamed `<name>#<id>` because the name was taken. */
88
115
  COLUMN_RENAMED: RENAMED_CODE,
89
- /** A structural column declared without its role because the sink already holds it. */
116
+ /**
117
+ * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
118
+ * a plain attribute.
119
+ */
90
120
  ROLE_TAKEN: ROLE_TAKEN_CODE,
91
121
  /** Two vertices carry the same `graphty_originalId` under restoreMangledIds and became one node. */
92
122
  ORIGINAL_ID_MERGED: "W_PAJEK_ORIGINAL_ID_MERGED",
93
123
  /** A vertex line's `graphty_originalId` came after a later vertex's line had created it under its number. */
94
124
  ORIGINAL_ID_UNRESTORED: "W_PAJEK_ORIGINAL_ID_UNRESTORED",
95
- /** A common option the importer has no use for (weightFrom naming a parameter and restoreMangledIds are honoured; long, hyperedges are not). */
125
+ /** You set an option this format does not use; it had no effect. The message names the option. */
96
126
  OPTION_IGNORED: OPTION_IGNORED_CODE,
97
- /** A builder-policy option the caller passed that the caller's sink does not use (the shared W_SINK_OPTION). */
127
+ /**
128
+ * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
129
+ * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
130
+ */
98
131
  SINK_OPTION: SINK_OPTION_CODE,
132
+ /** A double quote inside a token (a CSV-style doubled quote, a quote mid-word): removed and the parts joined. */
133
+ QUOTE_IN_TOKEN: "W_PAJEK_QUOTE_IN_TOKEN",
134
+ /** The same parameter twice on one line; the later value stands. */
135
+ DUPLICATE_ATTRIBUTE: DUPLICATE_ATTRIBUTE_CODE,
136
+ /** A character reference beyond U+10FFFF in a label; kept as written. */
137
+ REFERENCE_RANGE: "W_PAJEK_REFERENCE_RANGE",
138
+ /** A line of a two-mode network whose endpoints are both in one mode. */
139
+ TWO_MODE_LINE: "W_PAJEK_TWO_MODE_LINE",
140
+ /** 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. */
141
+ NUMERIC_LABEL: "W_PAJEK_NUMERIC_LABEL",
142
+ /** A relation number given a second name by a later `*Arcs :k "name"` header. */
143
+ RELATION_RENAMED: "W_PAJEK_RELATION_RENAMED",
144
+ /** A negative vertex number in an adjacency list, read as its absolute value. */
145
+ NEGATIVE_LIST_ENTRY: "W_PAJEK_NEGATIVE_LIST_ENTRY",
146
+ /** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
147
+ PRECISION: PRECISION_CODE,
99
148
  });
100
149
  /** The common options the Pajek importer reads (the rest is reported by reportUnusedOptions). */
101
150
  const USED_OPTIONS = new Set([
@@ -117,6 +166,9 @@ const DEFAULTS = { ids: "canonical", defaultDirected: true, weightFrom: VALUE_FI
117
166
  /** Lines between two checks of the cancellation signal (a whole string input is one chunk). */
118
167
  const ABORT_CHECK_INTERVAL = 64;
119
168
  const FIRST_VERTEX_VALUES = new Set([0, 1, "auto"]);
169
+ /** A numeral that is not a finite number (`1e999`, `NaN`, `Infinity`): never a parameter key. */
170
+ const NUMERAL = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
171
+ const NON_FINITE_WORD = /^[+-]?(?:nan|inf|infinity)$/i;
120
172
  const LABEL_DECL = {
121
173
  name: LABEL_COLUMN,
122
174
  dtype: "string",
@@ -214,6 +266,8 @@ class PajekParser {
214
266
  this.relation = null;
215
267
  /** `*Vertices N`; -1 before the header. */
216
268
  this.vertexCount = -1;
269
+ /** The line of the `*Vertices` header. */
270
+ this.verticesLine = 0;
217
271
  /** The first-mode count of a two-mode network, or null. */
218
272
  this.firstMode = null;
219
273
  this.networkName = null;
@@ -232,6 +286,10 @@ class PajekParser {
232
286
  this.objects = [];
233
287
  /** Set when the previous line was a project-object header, whose `*Vertices N` line comes next. */
234
288
  this.objectHeader = false;
289
+ /** The first unsupported section keyword, for the message of a file without `*Vertices`. */
290
+ this.firstUnsupported = null;
291
+ /** Vertex lines skipped because their number is outside the declared range (not lines of declared vertices). */
292
+ this.outOfRange = 0;
235
293
  /** Node index per vertex position (vertex number minus base). */
236
294
  this.indexOfPos = new Uint32Array(0);
237
295
  /**
@@ -302,7 +360,7 @@ class PajekParser {
302
360
  this.matrixLine(text, line);
303
361
  return false;
304
362
  case "object":
305
- this.objectLine(text);
363
+ this.objectLine(text, line);
306
364
  return false;
307
365
  case "skip":
308
366
  return false;
@@ -327,7 +385,10 @@ class PajekParser {
327
385
  */
328
386
  finish() {
329
387
  if (this.vertexCount < 0) {
330
- this.report.fail(PAJEK_ISSUE.NO_VERTICES, "not a Pajek network: no *Vertices section found");
388
+ const seen = this.firstUnsupported === null
389
+ ? ""
390
+ : ` (the file has the unrecognised section *${this.firstUnsupported})`;
391
+ this.report.fail(PAJEK_ISSUE.NO_VERTICES, `not a Pajek network: no *Vertices section found${seen}`);
331
392
  }
332
393
  this.endSection();
333
394
  this.finishVertices();
@@ -341,7 +402,7 @@ class PajekParser {
341
402
  }
342
403
  const objects = this.writeObjects();
343
404
  if (this.coercer.mergeCount > 0) {
344
- this.report.warning("coercion", PAJEK_ISSUE.ID_MERGED, `${this.coercer.mergeCount} label(s) merged into an id another label already produced under ids "number"`);
405
+ this.report.warning("coercion", PAJEK_ISSUE.ID_MERGED, `${this.coercer.mergeCount} label${plural(this.coercer.mergeCount)} merged into an id another label already produced under ids "number"`);
345
406
  }
346
407
  const pajek = {
347
408
  ...(this.firstMode === null ? {} : { firstMode: this.firstMode }),
@@ -383,7 +444,7 @@ class PajekParser {
383
444
  }
384
445
  const declared = object.count ?? object.values.length;
385
446
  if (declared !== this.vertexCount || object.values.length !== declared) {
386
- this.report.error("validation-error", PAJEK_ISSUE.OBJECT_COUNT, `*${partition ? "Partition" : "Vector"} "${object.name}" declares ${declared} vertices and holds ${object.values.length} value(s); the network has ${this.vertexCount}`, where);
447
+ this.report.error("validation-error", PAJEK_ISSUE.OBJECT_COUNT, `*${partition ? "Partition" : "Vector"} "${object.name}" declares ${declared} vertices and holds ${object.values.length} value${plural(object.values.length)}; the network has ${this.vertexCount}`, where);
387
448
  }
388
449
  written.push({ kind: object.kind, name: object.name, column: decl.name });
389
450
  }
@@ -405,7 +466,7 @@ class PajekParser {
405
466
  throw new LineError("parse-error", PAJEK_ISSUE.SYNTAX, `cannot read the section header "${text}"`);
406
467
  }
407
468
  if (h.extra.length > 0) {
408
- this.report.warning("validation-error", PAJEK_ISSUE.HEADER_EXTRA, `ignored ${h.extra.length} unexpected token(s) after *${h.keyword}: ${h.extra.join(" ")}`, { line });
469
+ this.report.warning("validation-error", PAJEK_ISSUE.HEADER_EXTRA, `ignored ${h.extra.length} unexpected token${plural(h.extra.length)} after *${h.keyword}: ${h.extra.join(" ")}`, { line });
409
470
  }
410
471
  switch (h.kind) {
411
472
  case "network":
@@ -453,7 +514,9 @@ class PajekParser {
453
514
  return;
454
515
  case "unsupported":
455
516
  this.section = "skip";
456
- this.objectHeader = true;
517
+ // only a project object's next line is its own `*Vertices N`
518
+ this.objectHeader = isProjectObject(h.keyword);
519
+ this.firstUnsupported ?? (this.firstUnsupported = h.keyword);
457
520
  this.report.warning("unsupported", PAJEK_ISSUE.UNSUPPORTED_SECTION, `the *${h.keyword} section is not supported; its lines are skipped`, { line });
458
521
  return;
459
522
  default: {
@@ -478,8 +541,9 @@ class PajekParser {
478
541
  this.report.fail(PAJEK_ISSUE.VERTICES_COUNT, `*Vertices ${h.count} ${h.secondCount}: the first-mode count is not within 0..${h.count}`, { line });
479
542
  }
480
543
  try {
481
- // the declared count must be reservable (the sink is the authority on what it can hold,
482
- // E_TOO_LARGE beyond MAX_COUNT) before any vertex is materialised
544
+ // the declared count must be reservable (the sink is the authority on what it can hold;
545
+ // the builder refuses more than its id map holds with E_TOO_LARGE) before any vertex
546
+ // or per-vertex table is allocated
483
547
  this.sink.reserve(h.count);
484
548
  }
485
549
  catch (err) {
@@ -489,6 +553,7 @@ class PajekParser {
489
553
  });
490
554
  }
491
555
  this.vertexCount = h.count;
556
+ this.verticesLine = line;
492
557
  if (h.secondCount !== null) {
493
558
  this.firstMode = h.secondCount;
494
559
  }
@@ -525,7 +590,12 @@ class PajekParser {
525
590
  this.kind = kind;
526
591
  this.relation = h.relation === null ? null : (h.name ?? String(h.relation));
527
592
  if (h.relation !== null) {
528
- this.relationNames.set(h.relation, this.relation ?? String(h.relation));
593
+ const name = this.relation ?? String(h.relation);
594
+ const before = this.relationNames.get(h.relation);
595
+ if (before !== undefined && before !== name) {
596
+ this.report.warning("validation-error", PAJEK_ISSUE.RELATION_RENAMED, `relation ${h.relation} was named ${JSON.stringify(before)} and is now named ${JSON.stringify(name)}; its lines carry the name of their own section`, { line, element: String(h.relation) });
597
+ }
598
+ this.relationNames.set(h.relation, name);
529
599
  }
530
600
  }
531
601
  /**
@@ -534,7 +604,7 @@ class PajekParser {
534
604
  endSection() {
535
605
  const rows = this.firstMode ?? this.vertexCount;
536
606
  if (this.section === "matrix" && this.matrixRows !== rows) {
537
- this.report.error("validation-error", PAJEK_ISSUE.MATRIX_ROWS, `*Matrix has ${this.matrixRows} row(s); *Vertices declares ${rows}`);
607
+ this.report.error("validation-error", PAJEK_ISSUE.MATRIX_ROWS, `*Matrix has ${this.matrixRows} row${plural(this.matrixRows)}; *Vertices declares ${rows}`);
538
608
  }
539
609
  this.section = "none";
540
610
  }
@@ -548,7 +618,8 @@ class PajekParser {
548
618
  setBase(base, line) {
549
619
  this.base = base;
550
620
  this.baseKnown = true;
551
- if (base === 0) {
621
+ // the caller who passed firstVertex: 0 knows the numbering; the warning is for a guess
622
+ if (base === 0 && this.firstVertex === "auto") {
552
623
  this.report.warning("coercion", PAJEK_ISSUE.ZERO_BASED, "vertex numbering starts at 0; Pajek numbers vertices from 1", { line });
553
624
  }
554
625
  }
@@ -563,12 +634,29 @@ class PajekParser {
563
634
  }
564
635
  this.nodesCreated = true;
565
636
  const n = this.vertexCount;
566
- const { sink } = this;
567
637
  for (let pos = 0; pos < n; pos++) {
568
- this.indexOfPos[pos] = sink.addNode(this.idOfNumber(pos + this.base));
638
+ this.indexOfPos[pos] = this.addNode(this.idOfNumber(pos + this.base));
569
639
  }
570
640
  this.report.counts.nodes += n;
571
641
  }
642
+ /**
643
+ * Add a vertex to the sink. A sink that cannot hold the declared vertex set (the builder's
644
+ * E_TOO_LARGE id map limit) makes the file unreadable, so its refusal is fatal; any other
645
+ * error passes to the caller.
646
+ * @param id - the vertex id
647
+ * @returns the node index
648
+ */
649
+ addNode(id) {
650
+ try {
651
+ return this.sink.addNode(id);
652
+ }
653
+ catch (err) {
654
+ if (!(err instanceof GraphFormatError) || err.code !== "E_TOO_LARGE") {
655
+ throw err;
656
+ }
657
+ return this.report.fail(PAJEK_ISSUE.VERTICES_COUNT, `*Vertices ${this.vertexCount}: the sink cannot hold that many (${err.message})`, { line: this.verticesLine });
658
+ }
659
+ }
572
660
  /**
573
661
  * The id of a vertex number under nodeIdFrom "id" (the number coerced by the ids rule) or
574
662
  * "index" (the position).
@@ -624,9 +712,9 @@ class PajekParser {
624
712
  }
625
713
  }
626
714
  // a partial list: fewer lines than declared, counting the lines skipped after an error
627
- const lines = this.seenCount + this.report.counts.skippedNodes;
715
+ const lines = this.seenCount + this.report.counts.skippedNodes - this.outOfRange;
628
716
  if (this.seenCount > 0 && lines < n) {
629
- this.report.warning("validation-error", PAJEK_ISSUE.VERTEX_COUNT, `*Vertices declares ${n} vertices but ${lines} vertex line(s) were read; the others have no label`);
717
+ this.report.warning("validation-error", PAJEK_ISSUE.VERTEX_COUNT, `*Vertices declares ${n} vertices but ${lines} vertex line${plural(lines)} ${agree(lines, "was", "were")} read; the others have no label`);
630
718
  }
631
719
  }
632
720
  /**
@@ -635,7 +723,7 @@ class PajekParser {
635
723
  * @param line - the line number
636
724
  */
637
725
  vertexLine(text, line) {
638
- const tokens = this.tokens(text, "node");
726
+ const tokens = this.tokens(text, "node", line);
639
727
  if (!isVertexNumber(tokens[0])) {
640
728
  this.report.counts.skippedNodes++;
641
729
  throw new LineError("parse-error", PAJEK_ISSUE.VERTEX_LINE, `a vertex line starts with a vertex number, not "${tokens[0]}"`);
@@ -647,13 +735,14 @@ class PajekParser {
647
735
  const pos = k - this.base;
648
736
  if (pos < 0 || pos >= this.vertexCount) {
649
737
  this.report.counts.skippedNodes++;
738
+ this.outOfRange++;
650
739
  throw new LineError("validation-error", PAJEK_ISSUE.VERTEX_RANGE, `vertex ${k} is outside ${this.base}..${this.base + this.vertexCount - 1}`);
651
740
  }
652
741
  // parse everything before writing anything
653
742
  let i = 1;
654
743
  let label = null;
655
744
  if (tokens.length > 1 && !startsWithCoordinates(text, tokens)) {
656
- label = decodeCharacterReferences(tokens[1]);
745
+ label = this.labelOf(tokens[1], k, line);
657
746
  i = 2;
658
747
  }
659
748
  let x = 0;
@@ -675,12 +764,13 @@ class PajekParser {
675
764
  this.report.counts.skippedNodes++;
676
765
  throw new LineError("parse-error", PAJEK_ISSUE.VERTEX_LINE, `vertex ${k}: a single coordinate "${tokens[i]}"; coordinates are x y [z]`);
677
766
  }
767
+ this.checkCoordinates(text, tokens, i, dims === 2 && label !== null, k, line);
678
768
  let shape = null;
679
769
  if (i < tokens.length && isShapeKeyword(tokens[i])) {
680
770
  shape = tokens[i].toLowerCase();
681
771
  i++;
682
772
  }
683
- const extras = this.extras(tokens, i, "node");
773
+ const extras = this.extras(tokens, i, "node", line);
684
774
  const restored = this.restoredId(extras);
685
775
  this.createNodes();
686
776
  // write
@@ -727,14 +817,7 @@ class PajekParser {
727
817
  sink.setNodeValue(this.labelHandle, index, label);
728
818
  }
729
819
  if (dims > 0) {
730
- if (this.positionHandle === INVALID_INDEX) {
731
- this.coordDims = dims;
732
- this.positionHandle = declareResolved(sink, "node", positionDecl(dims), this.report, { line }).handle;
733
- }
734
- else if (dims !== this.coordDims) {
735
- this.report.warnOnce("validation-error", PAJEK_ISSUE.COORD_DIMS, `vertex lines mix ${this.coordDims} and ${dims} coordinates; the position column records sourceDims ${this.coordDims}`, { line, element: String(k) });
736
- }
737
- sink.setNodeValue(this.positionHandle, index, [x, y, z]);
820
+ this.writePosition(index, dims, [x, y, z], k, line);
738
821
  }
739
822
  if (shape !== null) {
740
823
  if (this.shapeHandle === INVALID_INDEX && this.nodeParams.has(SHAPE_COLUMN)) {
@@ -758,6 +841,65 @@ class PajekParser {
758
841
  sink.setNodeValue(this.nodeSpellsHandle, index, extras.spells);
759
842
  }
760
843
  }
844
+ /**
845
+ * A vertex label as written, its character references decoded.
846
+ * @param token - the label token
847
+ * @param k - the vertex number
848
+ * @param line - the line number
849
+ * @returns the label
850
+ */
851
+ labelOf(token, k, line) {
852
+ return decodeCharacterReferences(token, (reference) => {
853
+ this.report.warnOnce("validation-error", PAJEK_ISSUE.REFERENCE_RANGE, `character reference ${reference} is beyond U+10FFFF; it is kept as written`, { line, element: String(k) }, `${PAJEK_ISSUE.REFERENCE_RANGE}:${reference}`);
854
+ });
855
+ }
856
+ /**
857
+ * Check the token after a vertex line's coordinates (or where they would stand): a numeral the
858
+ * number range cannot hold is a broken coordinate, never a parameter named "1e999" or "NaN";
859
+ * and a bare non-integer label before two coordinates may be the x of an x y z line.
860
+ * @param text - the line
861
+ * @param tokens - its tokens
862
+ * @param i - the index after the coordinates
863
+ * @param labelAndXy - whether the line was read as a label and two coordinates
864
+ * @param k - the vertex number
865
+ * @param line - the line number
866
+ */
867
+ checkCoordinates(text, tokens, i, labelAndXy, k, line) {
868
+ if (i < tokens.length &&
869
+ (NUMERAL.test(tokens[i]) || NON_FINITE_WORD.test(tokens[i])) &&
870
+ !isNumericText(tokens[i])) {
871
+ this.report.counts.skippedNodes++;
872
+ throw new LineError("parse-error", PAJEK_ISSUE.VERTEX_LINE, `vertex ${k}: coordinate "${tokens[i]}" is not a finite number`);
873
+ }
874
+ const label = tokens[1];
875
+ if (labelAndXy &&
876
+ isNumericText(label) &&
877
+ !Number.isInteger(Number(label)) &&
878
+ !quotedAfterNumber(text, tokens)) {
879
+ this.report.warnOnce("validation-error", PAJEK_ISSUE.NUMERIC_LABEL, `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`, { line, element: String(k) });
880
+ }
881
+ }
882
+ /**
883
+ * Write a vertex's coordinates into the position column (f32 x 3), declared on first use.
884
+ * @param index - the node index
885
+ * @param dims - 2 or 3, the coordinates the line gave
886
+ * @param point - x, y, z (0 when absent)
887
+ * @param k - the vertex number
888
+ * @param line - the line number
889
+ */
890
+ writePosition(index, dims, point, k, line) {
891
+ if (this.positionHandle === INVALID_INDEX) {
892
+ this.coordDims = dims;
893
+ this.positionHandle = declareResolved(this.sink, "node", positionDecl(dims), this.report, { line }).handle;
894
+ }
895
+ else if (dims !== this.coordDims) {
896
+ this.report.warnOnce("validation-error", PAJEK_ISSUE.COORD_DIMS, `vertex lines mix ${this.coordDims} and ${dims} coordinates; the position column records sourceDims ${this.coordDims}`, { line, element: String(k) });
897
+ }
898
+ if (!point.every(survivesF32)) {
899
+ this.report.warnOnce("precision", PAJEK_ISSUE.PRECISION, `vertex ${k} has a coordinate the f32 position column rounds (warned once)`, { line, element: String(k) }, `${PAJEK_ISSUE.PRECISION}:position`);
900
+ }
901
+ this.sink.setNodeValue(this.positionHandle, index, point);
902
+ }
761
903
  /**
762
904
  * Whether a `graphty_originalId` parameter is restored as the vertex id: restoreMangledIds
763
905
  * under nodeIdFrom "id" (under "label" / "index" the parameter stays an ordinary column).
@@ -775,7 +917,7 @@ class PajekParser {
775
917
  numberedNode(ids, pos) {
776
918
  const id = this.idOfNumber(pos + this.base);
777
919
  ids[pos] = id;
778
- this.indexOfPos[pos] = this.sink.addNode(id);
920
+ this.indexOfPos[pos] = this.addNode(id);
779
921
  this.report.counts.nodes++;
780
922
  }
781
923
  /**
@@ -812,7 +954,7 @@ class PajekParser {
812
954
  deferredNode(ids, pos, k, text, line, from) {
813
955
  const id = text === null ? this.idOfNumber(k) : this.coercer.text(text);
814
956
  const before = this.sink.indexOf(id);
815
- const index = this.sink.addNode(id);
957
+ const index = this.addNode(id);
816
958
  ids[pos] = id;
817
959
  this.indexOfPos[pos] = index;
818
960
  this.report.counts.nodes++;
@@ -830,7 +972,7 @@ class PajekParser {
830
972
  * @param line - the line number
831
973
  */
832
974
  edgeLine(text, line) {
833
- let tokens = this.tokens(text, "edge");
975
+ let tokens = this.tokens(text, "edge", line);
834
976
  // a `k:` prefix puts this one line in relation k
835
977
  let { relation } = this;
836
978
  const prefix = RELATION_PREFIX.exec(tokens[0]);
@@ -851,7 +993,7 @@ class PajekParser {
851
993
  valueText = tokens[i];
852
994
  i++;
853
995
  }
854
- const extras = this.extras(tokens, i, "edge");
996
+ const extras = this.extras(tokens, i, "edge", line);
855
997
  const { weightFrom } = this.options;
856
998
  let weight;
857
999
  let valueIsWeight = false;
@@ -883,13 +1025,16 @@ class PajekParser {
883
1025
  * @param line - the line number
884
1026
  */
885
1027
  listLine(text, line) {
886
- const tokens = this.tokens(text, "edge");
1028
+ const tokens = this.tokens(text, "edge", line);
887
1029
  for (const token of tokens) {
888
1030
  if (!LIST_VERTEX.test(token)) {
889
1031
  this.report.counts.skippedEdges++;
890
1032
  throw new LineError("parse-error", PAJEK_ISSUE.LINE, `an adjacency list holds vertex numbers only, not "${token}"`);
891
1033
  }
892
1034
  }
1035
+ if (tokens.some((token) => token.startsWith("-"))) {
1036
+ this.report.warnOnce("coercion", PAJEK_ISSUE.NEGATIVE_LIST_ENTRY, "an adjacency list holds a negative vertex number; it is read as its absolute value and the sign is dropped", { line });
1037
+ }
893
1038
  const u = this.endpoint(Math.abs(Number(tokens[0])));
894
1039
  for (let i = 1; i < tokens.length; i++) {
895
1040
  const v = this.endpoint(Math.abs(Number(tokens[i])));
@@ -905,17 +1050,17 @@ class PajekParser {
905
1050
  * @param line - the line number
906
1051
  */
907
1052
  matrixLine(text, line) {
908
- const tokens = this.tokens(text, "edge");
1053
+ const tokens = this.tokens(text, "edge", line);
909
1054
  const row = this.matrixRows;
910
1055
  this.matrixRows++;
911
1056
  const offset = this.firstMode ?? 0;
912
1057
  const rows = this.firstMode ?? this.vertexCount;
913
1058
  const columns = this.vertexCount - offset;
914
1059
  if (row >= rows) {
915
- throw new LineError("validation-error", PAJEK_ISSUE.MATRIX_ROWS, `*Matrix row ${row + 1} is beyond the ${rows} declared row(s)`);
1060
+ throw new LineError("validation-error", PAJEK_ISSUE.MATRIX_ROWS, `*Matrix row ${row + 1} is beyond the ${rows} declared row${plural(rows)}`);
916
1061
  }
917
1062
  if (tokens.length < columns) {
918
- throw new LineError("parse-error", PAJEK_ISSUE.LINE, `*Matrix row ${row + 1} has ${tokens.length} value(s); the matrix has ${columns} column(s)`);
1063
+ throw new LineError("parse-error", PAJEK_ISSUE.LINE, `*Matrix row ${row + 1} has ${tokens.length} value${plural(tokens.length)}; the matrix has ${columns} column${plural(columns)}`);
919
1064
  }
920
1065
  for (let j = 0; j < columns; j++) {
921
1066
  if (!isNumericText(tokens[j])) {
@@ -923,7 +1068,7 @@ class PajekParser {
923
1068
  }
924
1069
  }
925
1070
  if (tokens.length > columns) {
926
- this.report.warnOnce("validation-error", PAJEK_ISSUE.MATRIX_EXTRA, `*Matrix row ${row + 1} has ${tokens.length} value(s) for ${columns} column(s); the extra values are ignored`, { line });
1071
+ this.report.warnOnce("validation-error", PAJEK_ISSUE.MATRIX_EXTRA, `*Matrix row ${row + 1} has ${tokens.length} value${plural(tokens.length)} for ${columns} column${plural(columns)}; the extra values are ignored`, { line });
927
1072
  }
928
1073
  for (let j = 0; j < columns; j++) {
929
1074
  const value = Number(tokens[j]);
@@ -938,10 +1083,11 @@ class PajekParser {
938
1083
  * A value line of a `*Partition` (integers) or `*Vector` (numbers): the values of the next
939
1084
  * vertices, usually one per line.
940
1085
  * @param text - the line
1086
+ * @param line - the line number
941
1087
  */
942
- objectLine(text) {
1088
+ objectLine(text, line) {
943
1089
  const object = this.objects[this.objects.length - 1];
944
- const tokens = this.tokens(text, "node");
1090
+ const tokens = this.tokens(text, "node", line);
945
1091
  for (const token of tokens) {
946
1092
  if (!isNumericText(token) || (object.kind === "partition" && !isI32(Number(token)))) {
947
1093
  // keep the later values on their vertices
@@ -967,6 +1113,9 @@ class PajekParser {
967
1113
  this.directionSet = true;
968
1114
  this.resolver.setHeader(this.kind === "directed", { line });
969
1115
  }
1116
+ if (this.firstMode !== null && u < this.firstMode === v < this.firstMode) {
1117
+ this.report.warnOnce("validation-error", PAJEK_ISSUE.TWO_MODE_LINE, `a line of the two-mode network joins two vertices of the ${u < this.firstMode ? "first" : "second"} mode`, { line });
1118
+ }
970
1119
  const before = sink.edgeCount;
971
1120
  const e = this.resolver.addEdge(this.idAt(u), this.idAt(v), this.kind, weight, { line });
972
1121
  this.report.counts.edges += sink.edgeCount - before;
@@ -1027,7 +1176,7 @@ class PajekParser {
1027
1176
  */
1028
1177
  weightOf(text) {
1029
1178
  try {
1030
- return parseWeightText(text);
1179
+ return parseWeightText(text, this.report);
1031
1180
  }
1032
1181
  catch (err) {
1033
1182
  this.report.counts.skippedEdges++;
@@ -1039,10 +1188,15 @@ class PajekParser {
1039
1188
  * Tokenize a data line.
1040
1189
  * @param text - the line
1041
1190
  * @param domain - what is skipped on an unterminated quote
1191
+ * @param line - the line number
1042
1192
  * @returns the tokens (at least one: blank lines never reach here)
1043
1193
  */
1044
- tokens(text, domain) {
1045
- const tokens = tokenize(text);
1194
+ tokens(text, domain, line) {
1195
+ const notes = { oddQuote: false };
1196
+ const tokens = tokenize(text, notes);
1197
+ if (notes.oddQuote) {
1198
+ this.report.warnOnce("coercion", PAJEK_ISSUE.QUOTE_IN_TOKEN, "a double quote inside a token is removed and the parts joined (Pajek has no escape for a quote)", { line });
1199
+ }
1046
1200
  if (tokens === null) {
1047
1201
  if (domain === "node") {
1048
1202
  this.report.counts.skippedNodes++;
@@ -1060,22 +1214,16 @@ class PajekParser {
1060
1214
  * @param tokens - the row's tokens
1061
1215
  * @param start - the first token to read
1062
1216
  * @param domain - what is skipped when the row is malformed
1217
+ * @param line - the line number
1063
1218
  * @returns the parsed extras
1064
1219
  */
1065
- extras(tokens, start, domain) {
1220
+ extras(tokens, start, domain, line) {
1066
1221
  const extras = { keys: [], values: [], spells: null };
1222
+ const seen = new Set();
1067
1223
  for (let i = start; i < tokens.length;) {
1068
1224
  const token = tokens[i];
1069
- if (isIntervalToken(token)) {
1070
- try {
1071
- const spells = parseIntervals(token);
1072
- // an empty `[]` is no spell at all
1073
- extras.spells = spells.length === 0 ? null : spells;
1074
- }
1075
- catch (err) {
1076
- this.skip(domain);
1077
- throw new LineError("validation-error", PAJEK_ISSUE.INTERVAL, err instanceof Error ? err.message : String(err));
1078
- }
1225
+ if (token.startsWith("[")) {
1226
+ extras.spells = this.intervals(token, domain);
1079
1227
  i++;
1080
1228
  continue;
1081
1229
  }
@@ -1083,12 +1231,36 @@ class PajekParser {
1083
1231
  this.skip(domain);
1084
1232
  throw new LineError("parse-error", domain === "node" ? PAJEK_ISSUE.VERTEX_LINE : PAJEK_ISSUE.LINE, `parameter "${token}" has no value`);
1085
1233
  }
1234
+ if (seen.has(token)) {
1235
+ this.report.warning("merged", PAJEK_ISSUE.DUPLICATE_ATTRIBUTE, `parameter ${JSON.stringify(token)} is given twice on one line; the later value ${JSON.stringify(tokens[i + 1])} stands`, { line, element: token });
1236
+ }
1237
+ seen.add(token);
1086
1238
  extras.keys.push(token);
1087
1239
  extras.values.push(tokens[i + 1]);
1088
1240
  i += 2;
1089
1241
  }
1090
1242
  return extras;
1091
1243
  }
1244
+ /**
1245
+ * The spells of a row's interval token; a malformed one skips the row.
1246
+ * @param token - the token, starting with "["
1247
+ * @param domain - what is skipped when the token is malformed
1248
+ * @returns the spells, or null for an empty `[]`, which is no spell at all
1249
+ */
1250
+ intervals(token, domain) {
1251
+ if (!isIntervalToken(token)) {
1252
+ this.skip(domain);
1253
+ throw new LineError("validation-error", PAJEK_ISSUE.INTERVAL, `time interval ${token} is not closed by "]"`);
1254
+ }
1255
+ try {
1256
+ const spells = parseIntervals(token);
1257
+ return spells.length === 0 ? null : spells;
1258
+ }
1259
+ catch (err) {
1260
+ this.skip(domain);
1261
+ throw new LineError("validation-error", PAJEK_ISSUE.INTERVAL, err instanceof Error ? err.message : String(err));
1262
+ }
1263
+ }
1092
1264
  /**
1093
1265
  * Count a skipped element.
1094
1266
  * @param domain - node or edge
@@ -1135,7 +1307,7 @@ class PajekParser {
1135
1307
  * @returns true when tokens[1] is the first coordinate
1136
1308
  */
1137
1309
  function startsWithCoordinates(text, tokens) {
1138
- if (text.trimStart().slice(tokens[0].length).trimStart().startsWith('"')) {
1310
+ if (quotedAfterNumber(text, tokens)) {
1139
1311
  return false;
1140
1312
  }
1141
1313
  let run = 0;
@@ -1144,6 +1316,15 @@ function startsWithCoordinates(text, tokens) {
1144
1316
  }
1145
1317
  return run === 2;
1146
1318
  }
1319
+ /**
1320
+ * Whether the token after a row's vertex number is written in quotes.
1321
+ * @param text - the line
1322
+ * @param tokens - its tokens
1323
+ * @returns true when the second token was quoted
1324
+ */
1325
+ function quotedAfterNumber(text, tokens) {
1326
+ return text.trimStart().slice(tokens[0].length).trimStart().startsWith('"');
1327
+ }
1147
1328
  /**
1148
1329
  * Resolve the firstVertex option.
1149
1330
  * @param value - the caller's value
@@ -1161,24 +1342,41 @@ function firstVertexOption(value) {
1161
1342
  found: value,
1162
1343
  });
1163
1344
  }
1164
- const HEAD_PATTERN = /^\s*\*(vertices|network)\b/i;
1165
- /** The Pajek NET importer. */
1345
+ /**
1346
+ * The section a Pajek network starts with, after blank lines and `%` comment lines.
1347
+ * @param text - the head of the input
1348
+ * @returns "vertices" or "network" (lower case), or null when the text starts otherwise
1349
+ */
1350
+ function headSection(text) {
1351
+ let rest = text.trimStart();
1352
+ while (rest.startsWith("%")) {
1353
+ const end = rest.search(/[\r\n]/);
1354
+ rest = end < 0 ? "" : rest.slice(end).trimStart();
1355
+ }
1356
+ return /^\*(vertices|network)\b/i.exec(rest)?.[1].toLowerCase() ?? null;
1357
+ }
1358
+ /**
1359
+ * The Pajek NET importer.
1360
+ * @category Built-in formats
1361
+ */
1166
1362
  export const pajekImporter = Object.freeze({
1167
1363
  format: "pajek",
1364
+ options: Object.freeze(["firstVertex"]),
1168
1365
  extensions: Object.freeze([".net", ".paj"]),
1169
1366
  mimeTypes: Object.freeze(["text/x-pajek", "text/plain"]),
1170
1367
  /**
1171
- * Confidence that the input is a Pajek network: it starts with `*Vertices` (or `*Network`).
1368
+ * Confidence that the input is a Pajek network: it starts with `*Vertices` (or `*Network`),
1369
+ * after any `%` comment lines.
1172
1370
  * @param head - the first bytes
1173
1371
  * @returns 0.9 for a `*Vertices` start, 0.8 for `*Network`, 0 otherwise
1174
1372
  */
1175
1373
  sniff(head) {
1176
- const text = new TextDecoder("utf-8").decode(head.subarray(0, Math.min(head.byteLength, 512)));
1177
- const match = HEAD_PATTERN.exec(text.startsWith(String.fromCharCode(0xfeff)) ? text.slice(1) : text);
1178
- if (match === null) {
1374
+ const text = new TextDecoder("utf-8").decode(head);
1375
+ const section = headSection(text.startsWith("\ufeff") ? text.slice(1) : text);
1376
+ if (section === null) {
1179
1377
  return 0;
1180
1378
  }
1181
- return match[1].toLowerCase() === "vertices" ? 0.9 : 0.8;
1379
+ return section === "vertices" ? 0.9 : 0.8;
1182
1380
  },
1183
1381
  /**
1184
1382
  * Read a Pajek network into the sink.
@@ -1188,6 +1386,10 @@ export const pajekImporter = Object.freeze({
1188
1386
  * @returns the report
1189
1387
  */
1190
1388
  async import(input, sink, options) {
1389
+ if (graphChosen(options)) {
1390
+ // the choice is honored here too, not only by the registry
1391
+ return importChosenGraph(pajekImporter, input, sink, options);
1392
+ }
1191
1393
  const resolved = resolveImportOptions(options, DEFAULTS);
1192
1394
  const firstVertex = firstVertexOption(options?.firstVertex);
1193
1395
  const report = new ImportReportBuilder("pajek", resolved.errorLimit);
@@ -1199,7 +1401,8 @@ export const pajekImporter = Object.freeze({
1199
1401
  let rest = null;
1200
1402
  let restLine = 0;
1201
1403
  let sinceCheck = 0;
1202
- for await (const text of reader) {
1404
+ for await (const raw of reader) {
1405
+ const text = withoutStrayBom(raw);
1203
1406
  if (rest !== null) {
1204
1407
  rest.line(text);
1205
1408
  }
@@ -1215,7 +1418,8 @@ export const pajekImporter = Object.freeze({
1215
1418
  }
1216
1419
  parser.finish();
1217
1420
  if (rest !== null) {
1218
- report.warning("unsupported", PAJEK_ISSUE.MULTIPLE_GRAPHS, `the project file holds ${rest.count} more network(s) after the first; import() reads the first, importAll() reads every one`, { line: restLine });
1421
+ const objects = rest.objects > 0 ? `; ${rest.objects} *Partition / *Vector object(s) after them are not read` : "";
1422
+ report.warning("unsupported", PAJEK_ISSUE.MULTIPLE_GRAPHS, `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}`, { line: restLine });
1219
1423
  }
1220
1424
  throwIfAborted(resolved.signal);
1221
1425
  return report.finish();
@@ -1244,7 +1448,8 @@ export const pajekImporter = Object.freeze({
1244
1448
  // decoding issues belong to the file, so they go to the first network's report
1245
1449
  const reader = new LineReader(input, reports[0], resolved);
1246
1450
  let sinceCheck = 0;
1247
- for await (const text of reader) {
1451
+ for await (const raw of reader) {
1452
+ const text = withoutStrayBom(raw);
1248
1453
  if (parser.line(text, reader.line)) {
1249
1454
  parser.finish();
1250
1455
  reports.push(new ImportReportBuilder("pajek", resolved.errorLimit));
@@ -1261,6 +1466,15 @@ export const pajekImporter = Object.freeze({
1261
1466
  return reports.map((r) => r.finish());
1262
1467
  },
1263
1468
  });
1469
+ /**
1470
+ * A line without the U+FEFF that starts it: a BOM left at the start of a later line by
1471
+ * concatenated files (the decoder removes only the first; the shared text check reports it).
1472
+ * @param text - the line
1473
+ * @returns the line without a leading U+FEFF
1474
+ */
1475
+ function withoutStrayBom(text) {
1476
+ return text.codePointAt(0) === 0xfeff ? text.slice(1) : text;
1477
+ }
1264
1478
  /**
1265
1479
  * Counts the networks of the rest of a project file by their headers alone, with the parser's
1266
1480
  * rule: a network starts at `*Network` after a named or populated one, or at a `*Vertices` after a
@@ -1271,6 +1485,8 @@ class NetworkCounter {
1271
1485
  constructor() {
1272
1486
  /** Networks started so far (the first header fed opens the first). */
1273
1487
  this.count = 0;
1488
+ /** `*Partition` / `*Vector` objects seen; import() does not read them. */
1489
+ this.objects = 0;
1274
1490
  this.named = false;
1275
1491
  this.populated = false;
1276
1492
  this.afterObjectHeader = false;
@@ -1293,8 +1509,12 @@ class NetworkCounter {
1293
1509
  return;
1294
1510
  }
1295
1511
  const { kind } = h;
1296
- if (kind === "partition" || kind === "vector" || kind === "unsupported") {
1512
+ if (kind === "partition" || kind === "vector") {
1297
1513
  this.afterObjectHeader = true;
1514
+ this.objects++;
1515
+ }
1516
+ else if (kind === "unsupported") {
1517
+ this.afterObjectHeader = isProjectObject(h.keyword);
1298
1518
  }
1299
1519
  else if (kind === "network") {
1300
1520
  if (this.count === 0 || this.named || this.populated) {