@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
@@ -58,6 +58,7 @@ import {
58
58
  uniqueColumnName,
59
59
  } from "../../common/attributes.js";
60
60
  import {
61
+ DANGLING_REFERENCE_CODE as SHARED_DANGLING_REFERENCE_CODE,
61
62
  DUPLICATE_NODE_CODE as SHARED_DUPLICATE_NODE_CODE,
62
63
  MISSING_ENDPOINT_CODE as SHARED_MISSING_ENDPOINT_CODE,
63
64
  MISSING_ID_CODE as SHARED_MISSING_ID_CODE,
@@ -74,7 +75,9 @@ import {
74
75
  type ResolvedImportOptions,
75
76
  resolveImportOptions,
76
77
  } from "../../common/options.js";
78
+ import { agree, plural, withArticle } from "../../common/plural.js";
77
79
  import { ImportReportBuilder } from "../../common/report.js";
80
+ import { parseTemporal } from "../../common/temporal.js";
78
81
  import { parseWeightText } from "../../common/weights.js";
79
82
  import {
80
83
  type CommonImportOptions,
@@ -86,72 +89,152 @@ import {
86
89
  import { checkRecordSyntax, RecordReader, type RecordSyntax } from "../csv/records.js";
87
90
  import { type FieldKind, type HeaderField, isHeaderRecord, parseHeaderField } from "./header.js";
88
91
 
89
- /** The format-specific options of the Neo4j importer. */
90
- export interface Neo4jImportOptions {
91
- /** Further node files, each with its own header row(s); read after the primary input. */
92
+ /**
93
+ * The format-specific options of the Neo4j importer.
94
+ * @category Built-in formats
95
+ */
96
+ export interface Neo4jImportOptions extends CommonImportOptions {
97
+ /**
98
+ * More node files, each a string, bytes or a stream with its own header row; read after the
99
+ * main input.
100
+ */
92
101
  nodes?: ImportInput | readonly ImportInput[] | undefined;
93
- /** Relationship files, each with its own header row(s); read after the node files. */
102
+ /**
103
+ * Relationship files, each a string, bytes or a stream with its own header row; read after the
104
+ * node files.
105
+ */
94
106
  relationships?: ImportInput | readonly ImportInput[] | undefined;
95
107
  /**
96
- * The field delimiter (neo4j-admin `--delimiter`); one character. When absent it is sniffed
97
- * from the first rows between "," and a tab, so a `.tsv` file needs no option.
108
+ * The field delimiter, one character (neo4j-admin's `--delimiter`). The default is to detect
109
+ * "," or a tab from the first rows, so a `.tsv` file needs no option. It must differ from
110
+ * `arrayDelimiter`: with `delimiter: ";"`, also pass `arrayDelimiter: ","` or `"|"`.
111
+ * @defaultValue detected
98
112
  */
99
113
  delimiter?: string | undefined;
100
- /** The array delimiter of list values and `:LABEL` cells (neo4j-admin `--array-delimiter`); ";" by default. */
114
+ /**
115
+ * The delimiter inside list values and `:LABEL` cells (neo4j-admin's `--array-delimiter`). It
116
+ * must differ from `delimiter`.
117
+ * @defaultValue ";"
118
+ */
101
119
  arrayDelimiter?: ";" | "," | "|" | undefined;
102
- /** The quote character (neo4j-admin `--quote`); one character; a double quote by default. */
120
+ /**
121
+ * The quote character, one character (neo4j-admin's `--quote`).
122
+ * @defaultValue '"'
123
+ */
103
124
  quote?: string | undefined;
104
125
  }
105
126
 
106
- /** The name of the node list column holding `:LABEL` values. */
127
+ /**
128
+ * The name of the node list column holding `:LABEL` values.
129
+ * @category Built-in formats
130
+ */
107
131
  export const LABELS_COLUMN = "labels";
108
132
 
109
- /** The name of the edge dict column holding `:TYPE` values. */
133
+ /**
134
+ * The name of the edge dict column holding `:TYPE` values.
135
+ * @category Built-in formats
136
+ */
110
137
  export const TYPE_COLUMN = "type";
111
138
 
112
- /** The name of the node dict column holding the id space of `:ID(Space)`. */
139
+ /**
140
+ * The name of the node dict column holding the id space of `:ID(Space)`.
141
+ * @category Built-in formats
142
+ */
113
143
  export const ID_SPACE_COLUMN = "idSpace";
114
144
 
115
- /** The name of the node string column holding the id text of a node of an id space (its id is `Space:id`). */
145
+ /**
146
+ * Neo4j CSV: the name of the node column that holds a node's id as the file wrote it, for a node of an id
147
+ * space (`:ID(Person)`), whose node id is `Person:<id>`. It has nothing to do with the `originalId` role that
148
+ * `sanitizeIds: "mangle"` uses.
149
+ * @category Built-in formats
150
+ */
116
151
  export const ORIGINAL_ID_COLUMN = "originalId";
117
152
 
118
- /** Issue code: a header row (or a whole section) is malformed; the import aborts. */
153
+ /**
154
+ * A header row (or a whole section) is malformed; the import aborts.
155
+ * @category Issue and loss codes
156
+ */
119
157
  export const HEADER_CODE = "E_NEO4J_HEADER";
120
158
 
121
- /** Issue code: a row has a different number of cells than its header. */
159
+ /**
160
+ * A row has a different number of cells than its header.
161
+ * @category Issue and loss codes
162
+ */
122
163
  export const COLUMN_COUNT_CODE = "E_NEO4J_COLUMN_COUNT";
123
164
 
124
- /** Issue code: a node row has an unquoted empty `:ID` cell (a quoted empty cell is the id ""). */
165
+ /**
166
+ * A node row has an unquoted empty `:ID` cell (a quoted empty cell is the id "").
167
+ * @category Issue and loss codes
168
+ */
125
169
  export const MISSING_ID_CODE = SHARED_MISSING_ID_CODE;
126
170
 
127
- /** Issue code: a relationship row has an unquoted empty `:START_ID` or `:END_ID` cell. */
171
+ /**
172
+ * A relationship row has an unquoted empty `:START_ID` or `:END_ID` cell.
173
+ * @category Issue and loss codes
174
+ */
128
175
  export const MISSING_ENDPOINT_CODE = SHARED_MISSING_ENDPOINT_CODE;
129
176
 
130
- /** Issue code: a node id was declared twice (same id space); the later row's properties win. */
177
+ /**
178
+ * A node id was declared twice (same id space); the later row's properties win.
179
+ * @category Issue and loss codes
180
+ */
131
181
  export const DUPLICATE_NODE_CODE = SHARED_DUPLICATE_NODE_CODE;
132
182
 
133
183
  /**
134
- * Issue code: a node id was declared in two id spaces -- a spaced id `Space:id` equals the text of an
135
- * id declared without a space; the later row is skipped.
184
+ * A node id was declared in two id spaces -- a spaced id `Space:id` equals the text of an id declared without a space;
185
+ * the later row is skipped.
186
+ * @category Issue and loss codes
136
187
  */
137
188
  export const ID_SPACE_COLLISION_CODE = "E_NEO4J_ID_SPACE_COLLISION";
138
189
 
139
190
  /**
140
- * Issue code: a `:START_ID(Space)` / `:END_ID(Space)` endpoint's qualified id `Space:id` names a node a
141
- * node row declared in another id space (without a space); the row is skipped.
191
+ * A `:START_ID(Space)` / `:END_ID(Space)` endpoint's qualified id `Space:id` names a node a node row declared in
192
+ * another id space (without a space); the row is skipped.
193
+ * @category Issue and loss codes
142
194
  */
143
195
  export const ENDPOINT_SPACE_CODE = "E_NEO4J_ENDPOINT_SPACE";
144
196
 
145
- /** Issue code: two different id cells became one id under `ids: "number"`. */
197
+ /**
198
+ * Two different id cells became one id under `ids: "number"`.
199
+ * @category Issue and loss codes
200
+ */
146
201
  export const ID_MERGED_CODE = SHARED_ID_MERGED_CODE;
147
202
 
148
- /** Issue code: a header brace option the importer does not act on. */
203
+ /**
204
+ * A header brace option the importer does not act on.
205
+ * @category Issue and loss codes
206
+ */
149
207
  export const HEADER_OPTION_CODE = "W_NEO4J_HEADER_OPTION_IGNORED";
150
208
 
151
- /** Issue code: a reserved column (labels / type / idSpace) lost its role because the sink already holds it. */
209
+ /**
210
+ * You read into a graph builder that already has the labels, type or id space attribute, so this file's one is kept
211
+ * without its role.
212
+ * @category Issue and loss codes
213
+ */
152
214
  export const ROLE_TAKEN_CODE = SHARED_ROLE_TAKEN_CODE;
153
215
 
154
- /** Loss code: `:IGNORE` columns were skipped. */
216
+ /**
217
+ * A relationship row with an empty `:TYPE` cell (neo4j-admin requires a type); the relationship is kept.
218
+ * @category Issue and loss codes
219
+ */
220
+ export const MISSING_TYPE_CODE = "W_NEO4J_MISSING_TYPE";
221
+
222
+ /**
223
+ * A file given under the `nodes` option holds a relationship header, or the reverse.
224
+ * @category Issue and loss codes
225
+ */
226
+ export const SECTION_KIND_CODE = "W_NEO4J_SECTION_KIND";
227
+
228
+ /**
229
+ * Relationship endpoints that no node row declares became nodes (the shared W_DANGLING_REFERENCE).
230
+ * @category Issue and loss codes
231
+ */
232
+ export const DANGLING_REFERENCE_CODE = SHARED_DANGLING_REFERENCE_CODE;
233
+
234
+ /**
235
+ * `:IGNORE` columns were skipped.
236
+ * @category Built-in formats
237
+ */
155
238
  export const IGNORED_COLUMNS_LOSS = "W_NEO4J_IGNORED_COLUMNS";
156
239
 
157
240
  /** The common options the Neo4j importer reads (the rest is reported by reportUnusedOptions). */
@@ -287,16 +370,27 @@ function resolveNeo4jOptions(options: Neo4jImportOptions | undefined): ResolvedN
287
370
  { option: "arrayDelimiter", found: arrayDelimiter },
288
371
  );
289
372
  }
373
+ if (arrayDelimiter === "," && o.delimiter === undefined) {
374
+ throw new GraphFormatError(
375
+ "E_UNSUPPORTED",
376
+ 'option arrayDelimiter "," needs the delimiter option: the field delimiter is a comma unless another is given',
377
+ { option: "arrayDelimiter", found: arrayDelimiter },
378
+ );
379
+ }
290
380
  const syntax = checkRecordSyntax({
291
381
  delimiter: o.delimiter ?? null,
292
382
  quote: o.quote ?? '"',
293
383
  candidates: NEO4J_DELIMITER_CANDIDATES.filter((d) => d !== arrayDelimiter),
294
384
  });
295
385
  if (syntax.delimiter === arrayDelimiter) {
296
- throw new GraphFormatError("E_UNSUPPORTED", "options delimiter and arrayDelimiter must differ", {
297
- option: "arrayDelimiter",
298
- found: arrayDelimiter,
299
- });
386
+ throw new GraphFormatError(
387
+ "E_UNSUPPORTED",
388
+ `options delimiter and arrayDelimiter must differ: both are ${JSON.stringify(arrayDelimiter)}; pass another arrayDelimiter (";", "," or "|")`,
389
+ {
390
+ option: "arrayDelimiter",
391
+ found: arrayDelimiter,
392
+ },
393
+ );
300
394
  }
301
395
  return {
302
396
  nodes: inputList("nodes", o.nodes),
@@ -307,7 +401,7 @@ function resolveNeo4jOptions(options: Neo4jImportOptions | undefined): ResolvedN
307
401
  }
308
402
 
309
403
  /**
310
- * Normalise an input-list option.
404
+ * Normalize an input-list option.
311
405
  * @param name - the option name
312
406
  * @param value - one input, a list of inputs, or undefined
313
407
  * @returns the inputs; E_UNSUPPORTED for anything else
@@ -462,6 +556,9 @@ class Neo4jImportSession {
462
556
 
463
557
  private readonly registry = new NodeRegistry();
464
558
 
559
+ /** The relationship property the weights were read from, when it is not "weight"; the exporter writes it back under that name. */
560
+ weightProperty: string | null = null;
561
+
465
562
  private labelsHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
466
563
 
467
564
  private typeHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
@@ -484,6 +581,18 @@ class Neo4jImportSession {
484
581
  /** Scratch: the property slots of the current row that lost precision. */
485
582
  private readonly precisionSlots: PropertySlot[] = [];
486
583
 
584
+ /** Scratch: the values of the current row that overflowed their float / double type. */
585
+ private readonly overflows: string[] = [];
586
+
587
+ /** The section kind the option of the input being read names, or null for the primary input. */
588
+ private expectedKind: Section["kind"] | null = null;
589
+
590
+ /** Whether any node row was read. */
591
+ private nodeRows = false;
592
+
593
+ /** Nodes relationship endpoints created (no node row declared them yet), the first few by id. */
594
+ private readonly placeholders: { readonly id: NodeId; readonly index: number }[] = [];
595
+
487
596
  /**
488
597
  * Create a session.
489
598
  * @param sink - the sink
@@ -506,39 +615,80 @@ class Neo4jImportSession {
506
615
  }
507
616
 
508
617
  /**
509
- * Read every input.
510
- * @param inputs - the inputs in reading order
618
+ * Read every input. A header-only input lends its header to the headerless input after it (the
619
+ * neo4j-admin convention `--nodes=header.csv,part1.csv`); an empty input is an error only when
620
+ * it is the only one.
621
+ * @param inputs - the inputs in reading order, each with the kind of section its option names (null for the primary input)
511
622
  */
512
- async run(inputs: readonly ImportInput[]): Promise<void> {
513
- const progress = new ProgressTracker(this.common.onProgress, inputs);
514
- for (const input of inputs) {
515
- await this.readInput(input, { ...progress.optionsFor(this.common.signal), encoding: this.common.encoding });
623
+ async run(
624
+ inputs: readonly { readonly input: ImportInput; readonly kind: Section["kind"] | null }[],
625
+ ): Promise<void> {
626
+ const progress = new ProgressTracker(
627
+ this.common.onProgress,
628
+ inputs.map((i) => i.input),
629
+ );
630
+ let carried: Section | null = null;
631
+ for (const { input, kind } of inputs) {
632
+ this.expectedKind = kind;
633
+ if (carried !== null && kind !== null && carried.kind !== kind) {
634
+ // a nodes header never lends itself to a relationships file, nor the reverse
635
+ carried = null;
636
+ }
637
+ carried = await this.readInput(
638
+ input,
639
+ { ...progress.optionsFor(this.common.signal), encoding: this.common.encoding },
640
+ carried,
641
+ inputs.length > 1,
642
+ );
516
643
  progress.finishInput();
517
644
  }
518
645
  if (this.ignoredColumns > 0) {
519
646
  this.report.loss(
520
647
  IGNORED_COLUMNS_LOSS,
521
- `${this.ignoredColumns} :IGNORE column(s) were skipped as the header instructs`,
648
+ `${this.ignoredColumns} :IGNORE column${plural(this.ignoredColumns)} ${agree(this.ignoredColumns, "was", "were")} skipped as the header instructs`,
522
649
  null,
523
650
  this.ignoredColumns,
524
651
  );
525
652
  }
653
+ this.reportDangling();
526
654
  }
527
655
 
528
656
  /**
529
657
  * Read one input: a header row, then data rows until the next header row.
530
658
  * @param input - the input
531
659
  * @param readOptions - cancellation and progress
660
+ * @param carried - the section of a header-only input just before this one, or null
661
+ * @param mayBeEmpty - whether an input without any record is allowed (other inputs exist)
662
+ * @returns this input's section when it held a header and no data row (lent to the next input), else null
532
663
  */
533
- private async readInput(input: ImportInput, readOptions: ReadOptions): Promise<void> {
534
- const reader = new RecordReader(input, this.report, this.options.syntax, readOptions);
535
- let section: Section | null = null;
664
+ private async readInput(
665
+ input: ImportInput,
666
+ readOptions: ReadOptions,
667
+ carried: Section | null,
668
+ mayBeEmpty: boolean,
669
+ ): Promise<Section | null> {
670
+ const reader = new RecordReader(input, this.report, this.options.syntax, {
671
+ ...readOptions,
672
+ allowEmpty: mayBeEmpty,
673
+ });
674
+ let section: Section | null = carried;
675
+ let records = 0;
676
+ let rows = 0;
536
677
  let sinceCheck = 0;
537
678
  for await (const count of reader) {
538
- if (section === null || isHeaderRecord(reader.cells, count)) {
679
+ const { cells, quoted } = reader;
680
+ if (records === 0 && count === 1 && !quoted[0] && cells[0].trim().length === 0) {
681
+ // a whitespace-only line before anything else is a blank line, not an empty header
682
+ continue;
683
+ }
684
+ records++;
685
+ // a quoted marker cell is data ("ref:id") unless the whole record is a quoted header
686
+ if (section === null || isHeaderRecord(cells, count, quoted)) {
539
687
  section = this.declareSection(reader, count);
688
+ rows = 0;
540
689
  continue;
541
690
  }
691
+ rows++;
542
692
  if (section.kind === "node") {
543
693
  this.nodeRow(section, reader, count);
544
694
  } else {
@@ -549,9 +699,14 @@ class Neo4jImportSession {
549
699
  throwIfAborted(readOptions.signal);
550
700
  }
551
701
  }
552
- if (section === null) {
702
+ if (records === 0 && carried === null && !mayBeEmpty) {
553
703
  this.report.fail(HEADER_CODE, "the input has no header row", { line: 1 });
554
704
  }
705
+ // the header lent on: a header-only input's, or the one lent to this input when it had none
706
+ if (section === carried) {
707
+ return carried;
708
+ }
709
+ return rows === 0 ? section : null;
555
710
  }
556
711
 
557
712
  /**
@@ -585,8 +740,19 @@ class Neo4jImportSession {
585
740
  throw headerError("a header mixes :ID with :START_ID / :END_ID");
586
741
  }
587
742
  if (!isNode && !isRelationship) {
743
+ const apoc = fields.some((f) => f.name === "_id" || f.name === "_start");
588
744
  throw headerError(
589
- "a header needs an :ID column (nodes) or :START_ID and :END_ID columns (relationships)",
745
+ apoc
746
+ ? "a header needs an :ID column (nodes) or :START_ID and :END_ID columns (relationships); this is the apoc.export.csv layout (_id, _labels, _start, _end, _type), which is not the neo4j-admin import format"
747
+ : "a header needs an :ID column (nodes) or :START_ID and :END_ID columns (relationships)",
748
+ );
749
+ }
750
+ if (this.expectedKind !== null && this.expectedKind !== (isNode ? "node" : "relationship")) {
751
+ this.report.warning(
752
+ "validation-error",
753
+ SECTION_KIND_CODE,
754
+ `a ${isNode ? "node" : "relationship"} header in a file given as ${this.expectedKind === "node" ? "nodes" : "relationships"}; read by its header`,
755
+ { line },
590
756
  );
591
757
  }
592
758
  checkPropertyNames(fields);
@@ -619,7 +785,15 @@ class Neo4jImportSession {
619
785
  );
620
786
  } catch (err) {
621
787
  if (err instanceof GraphFormatError && !(err instanceof ImportError)) {
622
- this.report.fail(HEADER_CODE, `line ${line}: ${err.message}`, { line }, { cause: err.code });
788
+ // a header written with another delimiter reads as one cell holding it
789
+ const other = [";", "|", "\t", ","].find(
790
+ (d) => d !== reader.delimiter && reader.cells.slice(0, count).some((c) => c.includes(d)),
791
+ );
792
+ const hint =
793
+ other === undefined
794
+ ? ""
795
+ : `; if the file is delimited by ${JSON.stringify(other)}, pass the delimiter option`;
796
+ this.report.fail(HEADER_CODE, `${err.message}${hint}`, { line }, { cause: err.code });
623
797
  }
624
798
  throw err;
625
799
  }
@@ -718,6 +892,9 @@ class Neo4jImportSession {
718
892
  let weightCell = -1;
719
893
  if (weightFrom !== null) {
720
894
  weightCell = fields.findIndex((field) => field.kind === "PROPERTY" && field.name === weightFrom);
895
+ if (weightCell >= 0 && weightFrom !== "weight") {
896
+ this.weightProperty = weightFrom;
897
+ }
721
898
  }
722
899
  const properties = this.declareProperties("edge", fields, line, weightCell);
723
900
  const { space: startSpace } = fields[startCell];
@@ -898,7 +1075,7 @@ class Neo4jImportSession {
898
1075
  report.error(
899
1076
  "validation-error",
900
1077
  COLUMN_COUNT_CODE,
901
- `row has ${count} cell(s) but the header has ${section.width}`,
1078
+ `row has ${count} cell${plural(count)} but the header has ${section.width}`,
902
1079
  { line },
903
1080
  );
904
1081
  report.counts.skippedNodes++;
@@ -921,7 +1098,16 @@ class Neo4jImportSession {
921
1098
  if (section.labelCells.length > 0 || section.extraLabels.length > 0) {
922
1099
  labels = this.labelsOf(section, cells, quoted);
923
1100
  }
924
- const index = sink.addNode(id);
1101
+ this.nodeRows = true;
1102
+ const existed = sink.indexOf(id) !== INVALID_INDEX;
1103
+ let index: number;
1104
+ try {
1105
+ index = sink.addNode(id);
1106
+ } catch (err) {
1107
+ report.recordError(err, { line, element: idText });
1108
+ report.counts.skippedNodes++;
1109
+ return;
1110
+ }
925
1111
  const status = this.registry.declare(index, section.spaceCode);
926
1112
  if (status === "collision") {
927
1113
  report.error(
@@ -944,19 +1130,69 @@ class Neo4jImportSession {
944
1130
  },
945
1131
  );
946
1132
  }
1133
+ // a write the sink refuses is recorded and the rest of the row is still written
1134
+ const set = (column: ColumnHandle, value: unknown): void => {
1135
+ this.guarded(line, idText, () => {
1136
+ sink.setNodeValue(column, index, value);
1137
+ });
1138
+ };
947
1139
  if (section.idHandle !== INVALID_INDEX) {
948
- sink.setNodeValue(section.idHandle, index, idText);
1140
+ set(section.idHandle, idText);
949
1141
  }
950
1142
  if (section.space !== null) {
951
- sink.setNodeValue(this.idSpaceHandle, index, section.space);
952
- sink.setNodeValue(this.originalIdHandle, index, idText);
1143
+ set(this.idSpaceHandle, section.space);
1144
+ set(this.originalIdHandle, idText);
953
1145
  }
954
1146
  if (labels !== undefined) {
955
- sink.setNodeValue(this.labelsHandle, index, labels);
1147
+ set(this.labelsHandle, labels);
956
1148
  }
957
- this.writeProperties("node", section.properties, index);
1149
+ this.writeProperties("node", section.properties, index, line, idText);
958
1150
  this.reportPrecision(line, idText);
959
- report.counts.nodes++;
1151
+ // a node a relationship created before its row (or an earlier row) is counted once
1152
+ if (!existed) {
1153
+ report.counts.nodes++;
1154
+ }
1155
+ }
1156
+
1157
+ /**
1158
+ * Run one sink write, recording a refusal (a GraphFormatError) on the row's line instead of
1159
+ * letting it escape the import.
1160
+ * @param line - the row's line
1161
+ * @param element - the row's element name
1162
+ * @param write - the write
1163
+ */
1164
+ private guarded(line: number, element: string, write: () => void): void {
1165
+ try {
1166
+ write();
1167
+ } catch (err) {
1168
+ if (!(err instanceof GraphFormatError) || err instanceof ImportError) {
1169
+ throw err;
1170
+ }
1171
+ this.report.recordError(err, { line, element });
1172
+ }
1173
+ }
1174
+
1175
+ /**
1176
+ * Report the relationship endpoints no node row declared (the shared W_DANGLING_REFERENCE,
1177
+ * once) when the input has node rows: each became a node, where neo4j-admin refuses the
1178
+ * relationship.
1179
+ */
1180
+ private reportDangling(): void {
1181
+ const dangling = this.placeholders.filter((p) => this.registry.codeAt(p.index) === 0);
1182
+ // without any node row, every node comes from the relationships (a relationships-only file)
1183
+ if (dangling.length === 0 || !this.nodeRows) {
1184
+ return;
1185
+ }
1186
+ const shown = dangling
1187
+ .slice(0, 5)
1188
+ .map((p) => String(p.id))
1189
+ .join(", ");
1190
+ this.report.warning(
1191
+ "validation-error",
1192
+ DANGLING_REFERENCE_CODE,
1193
+ `${dangling.length} relationship endpoint${plural(dangling.length)} ${agree(dangling.length, "names", "name")} no node row and became nodes: ${shown}${dangling.length > 5 ? ", ..." : ""} (neo4j-admin refuses such relationships)`,
1194
+ { element: String(dangling[0].id) },
1195
+ );
960
1196
  }
961
1197
 
962
1198
  /**
@@ -978,7 +1214,7 @@ class Neo4jImportSession {
978
1214
  report.error(
979
1215
  "validation-error",
980
1216
  COLUMN_COUNT_CODE,
981
- `row has ${count} cell(s) but the header has ${section.width}`,
1217
+ `row has ${count} cell${plural(count)} but the header has ${section.width}`,
982
1218
  { line },
983
1219
  );
984
1220
  report.counts.skippedEdges++;
@@ -1027,7 +1263,7 @@ class Neo4jImportSession {
1027
1263
  let weight: number | undefined;
1028
1264
  if (section.weightCell >= 0) {
1029
1265
  try {
1030
- weight = parseWeightText(cells[section.weightCell]);
1266
+ weight = parseWeightText(cells[section.weightCell], this.report);
1031
1267
  } catch (err) {
1032
1268
  report.recordError(err, { line, element });
1033
1269
  report.counts.skippedEdges++;
@@ -1038,6 +1274,9 @@ class Neo4jImportSession {
1038
1274
  report.counts.skippedEdges++;
1039
1275
  return;
1040
1276
  }
1277
+ const created = [source, target].filter(
1278
+ (id, k) => (k === 0 || id !== source) && sink.indexOf(id) === INVALID_INDEX,
1279
+ );
1041
1280
  let edge: number;
1042
1281
  try {
1043
1282
  edge = this.direction.addEdge(source, target, "directed", weight, { line, element });
@@ -1046,13 +1285,27 @@ class Neo4jImportSession {
1046
1285
  report.counts.skippedEdges++;
1047
1286
  return;
1048
1287
  }
1288
+ for (const id of created) {
1289
+ // a node no row declared yet: counted now; reported at the end unless a node row follows
1290
+ report.counts.nodes++;
1291
+ this.placeholders.push({ id, index: sink.indexOf(id) });
1292
+ }
1049
1293
  if (section.typeCell >= 0) {
1050
1294
  const type = cells[section.typeCell];
1051
1295
  if (type.length > 0) {
1052
- sink.setEdgeValue(this.typeHandle, edge, type);
1296
+ this.guarded(line, element, () => {
1297
+ sink.setEdgeValue(this.typeHandle, edge, type);
1298
+ });
1299
+ } else {
1300
+ report.warning(
1301
+ "missing-value",
1302
+ MISSING_TYPE_CODE,
1303
+ "empty :TYPE cell; neo4j-admin requires a relationship type, the relationship is kept without one",
1304
+ { line, element },
1305
+ );
1053
1306
  }
1054
1307
  }
1055
- this.writeProperties("edge", section.properties, edge);
1308
+ this.writeProperties("edge", section.properties, edge, line, element);
1056
1309
  this.reportPrecision(line, element);
1057
1310
  report.counts.edges++;
1058
1311
  }
@@ -1122,6 +1375,7 @@ class Neo4jImportSession {
1122
1375
  ): boolean {
1123
1376
  const { values, texts, precisionSlots } = this;
1124
1377
  precisionSlots.length = 0;
1378
+ this.overflows.length = 0;
1125
1379
  for (const slot of slots) {
1126
1380
  const text = cells[slot.cell];
1127
1381
  texts[slot.cell] = null;
@@ -1132,16 +1386,22 @@ class Neo4jImportSession {
1132
1386
  const { spec } = slot;
1133
1387
  try {
1134
1388
  if (spec.temporal !== null && !spec.list) {
1135
- const parsed = parseDeclaredTemporal(text, spec);
1389
+ const zoned = withZoneOffset(text, spec);
1390
+ const parsed = parseDeclaredTemporal(zoned, spec);
1136
1391
  values[slot.cell] = parsed.value;
1137
- texts[slot.cell] = parsed.text;
1392
+ // a zone name is kept in the companion text, the value is its instant
1393
+ texts[slot.cell] = zoned === text ? parsed.text : text;
1138
1394
  } else {
1139
1395
  values[slot.cell] = parseDeclaredValue(text, spec, this.options.listSyntax);
1396
+ checkNeo4jRange(spec, values[slot.cell], text);
1140
1397
  }
1141
1398
  } catch (err) {
1142
1399
  this.report.recordError(err, { line, element: `${element} ${slot.name}` });
1143
1400
  return false;
1144
1401
  }
1402
+ if (overflows(spec, values[slot.cell], text)) {
1403
+ this.overflows.push(slot.name);
1404
+ }
1145
1405
  if (spec.precision && losesPrecision(spec, text)) {
1146
1406
  precisionSlots.push(slot);
1147
1407
  }
@@ -1154,8 +1414,16 @@ class Neo4jImportSession {
1154
1414
  * @param domain - node or edge
1155
1415
  * @param slots - the section's property slots
1156
1416
  * @param row - the node or edge index
1417
+ * @param line - the row's line
1418
+ * @param element - the row's element name for issues
1157
1419
  */
1158
- private writeProperties(domain: "node" | "edge", slots: readonly PropertySlot[], row: number): void {
1420
+ private writeProperties(
1421
+ domain: "node" | "edge",
1422
+ slots: readonly PropertySlot[],
1423
+ row: number,
1424
+ line: number,
1425
+ element: string,
1426
+ ): void {
1159
1427
  const { sink, values, texts } = this;
1160
1428
  for (const slot of slots) {
1161
1429
  const value = values[slot.cell];
@@ -1163,20 +1431,23 @@ class Neo4jImportSession {
1163
1431
  continue;
1164
1432
  }
1165
1433
  const text = texts[slot.cell];
1166
- if (text !== null && slot.companion === INVALID_INDEX && slot.companionDecl !== null) {
1167
- slot.companion = declareCompanion(sink, domain, slot.companionDecl);
1168
- }
1169
- if (domain === "node") {
1170
- sink.setNodeValue(slot.handle, row, value);
1171
- if (text !== null) {
1172
- sink.setNodeValue(slot.companion, row, text);
1434
+ // a value the sink refuses is recorded on the row's line; the other properties are written
1435
+ this.guarded(line, `${element} ${slot.name}`, () => {
1436
+ if (text !== null && slot.companion === INVALID_INDEX && slot.companionDecl !== null) {
1437
+ slot.companion = declareCompanion(sink, domain, slot.companionDecl);
1173
1438
  }
1174
- } else {
1175
- sink.setEdgeValue(slot.handle, row, value);
1176
- if (text !== null) {
1177
- sink.setEdgeValue(slot.companion, row, text);
1439
+ if (domain === "node") {
1440
+ sink.setNodeValue(slot.handle, row, value);
1441
+ if (text !== null) {
1442
+ sink.setNodeValue(slot.companion, row, text);
1443
+ }
1444
+ } else {
1445
+ sink.setEdgeValue(slot.handle, row, value);
1446
+ if (text !== null) {
1447
+ sink.setEdgeValue(slot.companion, row, text);
1448
+ }
1178
1449
  }
1179
- }
1450
+ });
1180
1451
  }
1181
1452
  }
1182
1453
 
@@ -1195,6 +1466,15 @@ class Neo4jImportSession {
1195
1466
  );
1196
1467
  }
1197
1468
  this.precisionSlots.length = 0;
1469
+ for (const name of this.overflows) {
1470
+ this.report.warning(
1471
+ "precision",
1472
+ PRECISION_CODE,
1473
+ `the value of "${name}" is beyond the range of its float / double type and was stored as the nearest representable value`,
1474
+ { line, element },
1475
+ );
1476
+ }
1477
+ this.overflows.length = 0;
1198
1478
  }
1199
1479
 
1200
1480
  /**
@@ -1242,6 +1522,154 @@ function qualify(id: NodeId, space: string | null): NodeId {
1242
1522
  return space === null ? id : `${space}:${String(id)}`;
1243
1523
  }
1244
1524
 
1525
+ /** The integer range of the narrow neo4j-admin types (Java byte and short). */
1526
+ const INTEGER_RANGES: Readonly<Record<string, readonly [number, number]>> = {
1527
+ byte: [-128, 127],
1528
+ short: [-32768, 32767],
1529
+ };
1530
+
1531
+ /**
1532
+ * A Cypher duration (ISO 8601): unit form `P14DT16H12M` / `PT0.75M` / `P2.5W` (components may be
1533
+ * signed or fractional) or the date-time form `P2012-02-02T14:37:21.545`.
1534
+ */
1535
+ /**
1536
+ * Whether a text is a Cypher duration (ISO 8601): unit form `P14DT16H12M` / `PT0.75M` / `P2.5W`
1537
+ * (components may be signed or fractional) or the date-time form `P2012-02-02T14:37:21.545`.
1538
+ * @param text - the text, trimmed
1539
+ * @returns true when it is
1540
+ */
1541
+ function isDuration(text: string): boolean {
1542
+ if (DURATION_DATE_TIME.test(text)) {
1543
+ return true;
1544
+ }
1545
+ const m = /^[+-]?P([^T]*)(?:T(.*))?$/i.exec(text);
1546
+ if (m === null || m[2] === "" || (m[1] === "" && m[2] === undefined)) {
1547
+ return false;
1548
+ }
1549
+ return unitsInOrder(m[1], "YMWD") && unitsInOrder(m[2] ?? "", "HMS");
1550
+ }
1551
+
1552
+ /** The date-time form of a Cypher duration, `P2012-02-02T14:37:21.545`. */
1553
+ const DURATION_DATE_TIME = /^[+-]?P\d{4}-?\d{2}-?\d{2}T\d{2}:?\d{2}:?\d{2}(?:\.\d+)?$/i;
1554
+
1555
+ /**
1556
+ * Whether a run of duration components (`14D`, `-1.5H30M`) names its units in the order given,
1557
+ * each at most once.
1558
+ * @param text - the components
1559
+ * @param units - the unit letters in their order
1560
+ * @returns true when it does
1561
+ */
1562
+ function unitsInOrder(text: string, units: string): boolean {
1563
+ let rest = text.toUpperCase();
1564
+ let from = 0;
1565
+ while (rest !== "") {
1566
+ const m = /^[-+]?\d+(?:\.\d+)?([A-Z])/.exec(rest);
1567
+ const at = m === null ? -1 : units.indexOf(m[1], from);
1568
+ if (m === null || at < 0) {
1569
+ return false;
1570
+ }
1571
+ from = at + 1;
1572
+ rest = rest.slice(m[0].length);
1573
+ }
1574
+ return true;
1575
+ }
1576
+
1577
+ /**
1578
+ * Check the neo4j-admin types the shared parser maps to a wider dtype: byte and short values within
1579
+ * their Java range, a char of one character, a duration in Cypher's syntax.
1580
+ * @param spec - the declared type
1581
+ * @param value - the parsed value (a list of items for an array type)
1582
+ * @param text - the cell text, for the error
1583
+ */
1584
+ function checkNeo4jRange(spec: DeclaredTypeSpec, value: unknown, text: string): void {
1585
+ const base = spec.declared.trim().toLowerCase().replace(/\[\]$/, "");
1586
+ const items: unknown[] = spec.list && Array.isArray(value) ? value : [value];
1587
+ const range = INTEGER_RANGES[base];
1588
+ for (const item of items) {
1589
+ let bad = false;
1590
+ if (range !== undefined) {
1591
+ bad = typeof item === "number" && (item < range[0] || item > range[1]);
1592
+ } else if (base === "char") {
1593
+ // a Java char: one UTF-16 code unit
1594
+ bad = typeof item === "string" && item.length !== 1;
1595
+ } else if (base === "duration") {
1596
+ bad = typeof item === "string" && !isDuration(item.trim());
1597
+ }
1598
+ if (bad) {
1599
+ const span = range === undefined ? "" : ` (${range[0]} to ${range[1]})`;
1600
+ throw new GraphFormatError("E_COLUMN_TYPE", `"${text}" is not ${withArticle(base)}${span}`, {
1601
+ value: text,
1602
+ kind: base,
1603
+ });
1604
+ }
1605
+ }
1606
+ }
1607
+
1608
+ /** The largest finite f32. */
1609
+ const F32_MAX = 3.4028234663852886e38;
1610
+
1611
+ /**
1612
+ * Whether a float / double cell overflowed: a finite literal read as an infinity, or a float beyond
1613
+ * the f32 range (Java's Double.parseDouble accepts both silently; the value changes).
1614
+ * @param spec - the declared type
1615
+ * @param value - the parsed value
1616
+ * @param text - the cell text
1617
+ * @returns true when the stored value is not the one written
1618
+ */
1619
+ function overflows(spec: DeclaredTypeSpec, value: unknown, text: string): boolean {
1620
+ if ((spec.kind !== "float" && spec.kind !== "double") || /inf|nan/i.test(text)) {
1621
+ return false;
1622
+ }
1623
+ const items: unknown[] = spec.list && Array.isArray(value) ? value : [value];
1624
+ return items.some(
1625
+ (v) => typeof v === "number" && (!Number.isFinite(v) || (spec.kind === "float" && Math.abs(v) > F32_MAX)),
1626
+ );
1627
+ }
1628
+
1629
+ /**
1630
+ * A Cypher date-time with a zone name (`2020-01-01T00:00:00[Europe/Berlin]`) as one with a UTC
1631
+ * offset the temporal parser reads: the offset the text gives, else the zone's offset at that wall
1632
+ * time. Any other text is returned unchanged.
1633
+ * @param text - the cell text
1634
+ * @param spec - the declared type
1635
+ * @returns the text to parse
1636
+ */
1637
+ function withZoneOffset(text: string, spec: DeclaredTypeSpec): string {
1638
+ const trimmed = text.trim();
1639
+ const open = trimmed.lastIndexOf("[");
1640
+ if (spec.temporal !== "dateTime" || open < 0 || !trimmed.endsWith("]")) {
1641
+ return text;
1642
+ }
1643
+ const local = trimmed.slice(0, open);
1644
+ const zone = trimmed.slice(open + 1, -1);
1645
+ if (zone === "" || zone.includes("]") || /[\n\r\u2028\u2029]/.test(local)) {
1646
+ return text;
1647
+ }
1648
+ let format: Intl.DateTimeFormat;
1649
+ try {
1650
+ format = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "longOffset" });
1651
+ } catch {
1652
+ throw new GraphFormatError("E_COLUMN_TYPE", `"${text}" names an unknown time zone ${zone}`, { value: text });
1653
+ }
1654
+ if (/(Z|z|[+-]\d{2}(:?\d{2})?)$/.test(local)) {
1655
+ return local;
1656
+ }
1657
+ const wall = parseTemporal(local, "localDateTime").value;
1658
+ const offsetAt = (ms: number): number => {
1659
+ const name = format.formatToParts(new Date(ms)).find((p) => p.type === "timeZoneName")?.value ?? "GMT";
1660
+ const o = /GMT([+-])(\d{2}):(\d{2})/.exec(name);
1661
+ if (o === null) {
1662
+ return 0;
1663
+ }
1664
+ return (o[1] === "-" ? -1 : 1) * (Number(o[2]) * 60 + Number(o[3]));
1665
+ };
1666
+ // the offset at the instant the wall time names (twice, so a wall time near a transition settles)
1667
+ const minutes = offsetAt(wall - offsetAt(wall) * 60_000);
1668
+ const sign = minutes < 0 ? "-" : "+";
1669
+ const abs = Math.abs(minutes);
1670
+ return `${local}${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
1671
+ }
1672
+
1245
1673
  /**
1246
1674
  * The value of a quoted empty cell: an empty string for a string-kind property, an empty list for
1247
1675
  * a list property, unset for anything else (neo4j-admin's default `--ignore-empty-strings=false`).
@@ -1298,9 +1726,13 @@ function sniffNeo4j(head: Uint8Array): number {
1298
1726
  return isHeaderRecord(cells, cells.length) ? 0.95 : 0;
1299
1727
  }
1300
1728
 
1301
- /** The Neo4j importer. */
1729
+ /**
1730
+ * The Neo4j importer.
1731
+ * @category Built-in formats
1732
+ */
1302
1733
  export const neo4jImporter: GraphImporter<Neo4jImportOptions> = Object.freeze({
1303
1734
  format: NEO4J,
1735
+ options: Object.freeze(["arrayDelimiter", "delimiter", "nodes", "quote", "relationships"]),
1304
1736
  extensions: Object.freeze([".csv", ".tsv"]),
1305
1737
  mimeTypes: Object.freeze(["text/csv", "text/tab-separated-values"]),
1306
1738
  sniff: sniffNeo4j,
@@ -1324,7 +1756,14 @@ export const neo4jImporter: GraphImporter<Neo4jImportOptions> = Object.freeze({
1324
1756
  // column and every relationship is directed
1325
1757
  reportUnusedOptions(options, report, USED_OPTIONS);
1326
1758
  const session = new Neo4jImportSession(sink, report, common, format);
1327
- await session.run([input, ...format.nodes, ...format.relationships]);
1759
+ await session.run([
1760
+ { input, kind: null },
1761
+ ...format.nodes.map((file) => ({ input: file, kind: "node" as const })),
1762
+ ...format.relationships.map((file) => ({ input: file, kind: "relationship" as const })),
1763
+ ]);
1764
+ if (session.weightProperty !== null) {
1765
+ sink.setMeta({ extra: { neo4j: { weightProperty: session.weightProperty } } });
1766
+ }
1328
1767
  throwIfAborted(common.signal);
1329
1768
  return report.finish();
1330
1769
  },