@graphty/graph-io 0.3.18 → 0.3.20

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 (235) hide show
  1. package/README.md +80 -2
  2. package/dist/chunks/{escape-D-gZWO26.js → escape-B-tkk_Cl.js} +74 -16
  3. package/dist/chunks/escape-B-tkk_Cl.js.map +1 -0
  4. package/dist/chunks/{writer-GAdltGmC.js → export-Bh60Dv-n.js} +7 -252
  5. package/dist/chunks/export-Bh60Dv-n.js.map +1 -0
  6. package/dist/chunks/exporter-BGrsWamJ.js +736 -0
  7. package/dist/chunks/exporter-BGrsWamJ.js.map +1 -0
  8. package/dist/chunks/exporter-DIeGJXAZ.js +834 -0
  9. package/dist/chunks/exporter-DIeGJXAZ.js.map +1 -0
  10. package/dist/chunks/{importer-Br_QeAeE.js → importer-BWY2FFc5.js} +28 -6
  11. package/dist/chunks/importer-BWY2FFc5.js.map +1 -0
  12. package/dist/chunks/importer-BaQpCEcJ.js +2194 -0
  13. package/dist/chunks/importer-BaQpCEcJ.js.map +1 -0
  14. package/dist/chunks/importer-DD-xv9_Y.js +1471 -0
  15. package/dist/chunks/importer-DD-xv9_Y.js.map +1 -0
  16. package/dist/chunks/{importer-aNJfe0qu.js → importer-DEcKhsmQ.js} +2 -2
  17. package/dist/chunks/{importer-aNJfe0qu.js.map → importer-DEcKhsmQ.js.map} +1 -1
  18. package/dist/chunks/{importer-DHagxvDD.js → importer-DIrbbnAf.js} +6 -4
  19. package/dist/chunks/{importer-DHagxvDD.js.map → importer-DIrbbnAf.js.map} +1 -1
  20. package/dist/chunks/importer-D_e7e7LX.js +3055 -0
  21. package/dist/chunks/importer-D_e7e7LX.js.map +1 -0
  22. package/dist/chunks/{importer-Du5crN9l.js → importer-Wv4C4gaF.js} +278 -82
  23. package/dist/chunks/importer-Wv4C4gaF.js.map +1 -0
  24. package/dist/chunks/importer-vi3bSdtw.js +1713 -0
  25. package/dist/chunks/importer-vi3bSdtw.js.map +1 -0
  26. package/dist/chunks/{importer-d0uQxFp6.js → importer-zGLo8Dg_.js} +6 -4
  27. package/dist/chunks/{importer-d0uQxFp6.js.map → importer-zGLo8Dg_.js.map} +1 -1
  28. package/dist/chunks/json-elements-DDS8N2Dd.js +779 -0
  29. package/dist/chunks/json-elements-DDS8N2Dd.js.map +1 -0
  30. package/dist/chunks/{records-Bk9jgodz.js → records-dbRkxwaq.js} +2 -2
  31. package/dist/chunks/{records-Bk9jgodz.js.map → records-dbRkxwaq.js.map} +1 -1
  32. package/dist/chunks/{report-BOk0p5y8.js → report-B1z4WT9e.js} +143 -111
  33. package/dist/chunks/{report-BOk0p5y8.js.map → report-B1z4WT9e.js.map} +1 -1
  34. package/dist/chunks/weights-Dzba96G3.js +176 -0
  35. package/dist/chunks/weights-Dzba96G3.js.map +1 -0
  36. package/dist/chunks/writer-C7flM-Ih.js +77 -0
  37. package/dist/chunks/writer-C7flM-Ih.js.map +1 -0
  38. package/dist/csv.js +6 -4
  39. package/dist/csv.js.map +1 -1
  40. package/dist/cx.d.ts +1 -0
  41. package/dist/cx.js +6 -0
  42. package/dist/cx.js.map +1 -0
  43. package/dist/cx2.d.ts +1 -0
  44. package/dist/cx2.js +10 -0
  45. package/dist/cx2.js.map +1 -0
  46. package/dist/cys.d.ts +1 -0
  47. package/dist/cys.js +6 -0
  48. package/dist/cys.js.map +1 -0
  49. package/dist/dot.js +1 -1
  50. package/dist/gexf.js +16 -4
  51. package/dist/gexf.js.map +1 -1
  52. package/dist/gml.js +13 -14
  53. package/dist/gml.js.map +1 -1
  54. package/dist/graph-io.js +302 -399
  55. package/dist/graph-io.js.map +1 -1
  56. package/dist/graphml.js +1 -1
  57. package/dist/json.js +1 -1
  58. package/dist/neo4j.js +6 -4
  59. package/dist/neo4j.js.map +1 -1
  60. package/dist/obo.js +1 -1
  61. package/dist/pajek.js +1 -1
  62. package/dist/src/common/cell-budget.d.ts +84 -0
  63. package/dist/src/common/cell-budget.d.ts.map +1 -0
  64. package/dist/src/common/cell-budget.js +138 -0
  65. package/dist/src/common/cell-budget.js.map +1 -0
  66. package/dist/src/common/input.d.ts +10 -0
  67. package/dist/src/common/input.d.ts.map +1 -1
  68. package/dist/src/common/input.js +39 -0
  69. package/dist/src/common/input.js.map +1 -1
  70. package/dist/src/common/json-elements.d.ts +286 -0
  71. package/dist/src/common/json-elements.d.ts.map +1 -0
  72. package/dist/src/common/json-elements.js +926 -0
  73. package/dist/src/common/json-elements.js.map +1 -0
  74. package/dist/src/common/options.d.ts +6 -0
  75. package/dist/src/common/options.d.ts.map +1 -1
  76. package/dist/src/common/options.js +1 -1
  77. package/dist/src/common/options.js.map +1 -1
  78. package/dist/src/common/xml.d.ts +37 -3
  79. package/dist/src/common/xml.d.ts.map +1 -1
  80. package/dist/src/common/xml.js +86 -6
  81. package/dist/src/common/xml.js.map +1 -1
  82. package/dist/src/common/zip.d.ts +92 -0
  83. package/dist/src/common/zip.d.ts.map +1 -0
  84. package/dist/src/common/zip.js +399 -0
  85. package/dist/src/common/zip.js.map +1 -0
  86. package/dist/src/formats/cx/importer.d.ts +133 -0
  87. package/dist/src/formats/cx/importer.d.ts.map +1 -0
  88. package/dist/src/formats/cx/importer.js +2220 -0
  89. package/dist/src/formats/cx/importer.js.map +1 -0
  90. package/dist/src/formats/cx/index.d.ts +7 -0
  91. package/dist/src/formats/cx/index.d.ts.map +1 -0
  92. package/dist/src/formats/cx/index.js +7 -0
  93. package/dist/src/formats/cx/index.js.map +1 -0
  94. package/dist/src/formats/cx2/exporter.d.ts +59 -0
  95. package/dist/src/formats/cx2/exporter.d.ts.map +1 -0
  96. package/dist/src/formats/cx2/exporter.js +864 -0
  97. package/dist/src/formats/cx2/exporter.js.map +1 -0
  98. package/dist/src/formats/cx2/importer.d.ts +169 -0
  99. package/dist/src/formats/cx2/importer.d.ts.map +1 -0
  100. package/dist/src/formats/cx2/importer.js +1652 -0
  101. package/dist/src/formats/cx2/importer.js.map +1 -0
  102. package/dist/src/formats/cx2/index.d.ts +7 -0
  103. package/dist/src/formats/cx2/index.d.ts.map +1 -0
  104. package/dist/src/formats/cx2/index.js +7 -0
  105. package/dist/src/formats/cx2/index.js.map +1 -0
  106. package/dist/src/formats/cys/constants.d.ts +68 -0
  107. package/dist/src/formats/cys/constants.d.ts.map +1 -0
  108. package/dist/src/formats/cys/constants.js +76 -0
  109. package/dist/src/formats/cys/constants.js.map +1 -0
  110. package/dist/src/formats/cys/importer.d.ts +38 -0
  111. package/dist/src/formats/cys/importer.d.ts.map +1 -0
  112. package/dist/src/formats/cys/importer.js +830 -0
  113. package/dist/src/formats/cys/importer.js.map +1 -0
  114. package/dist/src/formats/cys/index.d.ts +7 -0
  115. package/dist/src/formats/cys/index.d.ts.map +1 -0
  116. package/dist/src/formats/cys/index.js +7 -0
  117. package/dist/src/formats/cys/index.js.map +1 -0
  118. package/dist/src/formats/cys/session.d.ts +132 -0
  119. package/dist/src/formats/cys/session.d.ts.map +1 -0
  120. package/dist/src/formats/cys/session.js +315 -0
  121. package/dist/src/formats/cys/session.js.map +1 -0
  122. package/dist/src/formats/cys/tables.d.ts +90 -0
  123. package/dist/src/formats/cys/tables.d.ts.map +1 -0
  124. package/dist/src/formats/cys/tables.js +293 -0
  125. package/dist/src/formats/cys/tables.js.map +1 -0
  126. package/dist/src/formats/dot/exporter.d.ts +2 -0
  127. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  128. package/dist/src/formats/dot/exporter.js +12 -0
  129. package/dist/src/formats/dot/exporter.js.map +1 -1
  130. package/dist/src/formats/dot/importer.js +6 -2
  131. package/dist/src/formats/dot/importer.js.map +1 -1
  132. package/dist/src/formats/gexf/exporter.d.ts +2 -0
  133. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  134. package/dist/src/formats/gexf/exporter.js +6 -0
  135. package/dist/src/formats/gexf/exporter.js.map +1 -1
  136. package/dist/src/formats/gexf/importer.d.ts +0 -2
  137. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  138. package/dist/src/formats/gexf/importer.js +1 -3
  139. package/dist/src/formats/gexf/importer.js.map +1 -1
  140. package/dist/src/formats/gexf/index.d.ts +1 -1
  141. package/dist/src/formats/gexf/index.js +2 -2
  142. package/dist/src/formats/gexf/index.js.map +1 -1
  143. package/dist/src/formats/gml/exporter.d.ts +0 -8
  144. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  145. package/dist/src/formats/gml/exporter.js +8 -18
  146. package/dist/src/formats/gml/exporter.js.map +1 -1
  147. package/dist/src/formats/json/importer.d.ts.map +1 -1
  148. package/dist/src/formats/json/importer.js +6 -104
  149. package/dist/src/formats/json/importer.js.map +1 -1
  150. package/dist/src/formats/xgmml/columns.d.ts +152 -0
  151. package/dist/src/formats/xgmml/columns.d.ts.map +1 -0
  152. package/dist/src/formats/xgmml/columns.js +593 -0
  153. package/dist/src/formats/xgmml/columns.js.map +1 -0
  154. package/dist/src/formats/xgmml/constants.d.ts +196 -0
  155. package/dist/src/formats/xgmml/constants.d.ts.map +1 -0
  156. package/dist/src/formats/xgmml/constants.js +197 -0
  157. package/dist/src/formats/xgmml/constants.js.map +1 -0
  158. package/dist/src/formats/xgmml/document.d.ts +361 -0
  159. package/dist/src/formats/xgmml/document.d.ts.map +1 -0
  160. package/dist/src/formats/xgmml/document.js +695 -0
  161. package/dist/src/formats/xgmml/document.js.map +1 -0
  162. package/dist/src/formats/xgmml/emit.d.ts +341 -0
  163. package/dist/src/formats/xgmml/emit.d.ts.map +1 -0
  164. package/dist/src/formats/xgmml/emit.js +1275 -0
  165. package/dist/src/formats/xgmml/emit.js.map +1 -0
  166. package/dist/src/formats/xgmml/exporter.d.ts +24 -0
  167. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -0
  168. package/dist/src/formats/xgmml/exporter.js +999 -0
  169. package/dist/src/formats/xgmml/exporter.js.map +1 -0
  170. package/dist/src/formats/xgmml/importer.d.ts +58 -0
  171. package/dist/src/formats/xgmml/importer.d.ts.map +1 -0
  172. package/dist/src/formats/xgmml/importer.js +360 -0
  173. package/dist/src/formats/xgmml/importer.js.map +1 -0
  174. package/dist/src/formats/xgmml/index.d.ts +8 -0
  175. package/dist/src/formats/xgmml/index.d.ts.map +1 -0
  176. package/dist/src/formats/xgmml/index.js +8 -0
  177. package/dist/src/formats/xgmml/index.js.map +1 -0
  178. package/dist/src/formats/xgmml/values.d.ts +60 -0
  179. package/dist/src/formats/xgmml/values.d.ts.map +1 -0
  180. package/dist/src/formats/xgmml/values.js +148 -0
  181. package/dist/src/formats/xgmml/values.js.map +1 -0
  182. package/dist/src/index.d.ts +4 -0
  183. package/dist/src/index.d.ts.map +1 -1
  184. package/dist/src/index.js +4 -0
  185. package/dist/src/index.js.map +1 -1
  186. package/dist/src/registry.d.ts +7 -0
  187. package/dist/src/registry.d.ts.map +1 -1
  188. package/dist/src/registry.js +30 -10
  189. package/dist/src/registry.js.map +1 -1
  190. package/dist/src/sniff.d.ts +1 -1
  191. package/dist/src/sniff.d.ts.map +1 -1
  192. package/dist/src/sniff.js +4 -0
  193. package/dist/src/sniff.js.map +1 -1
  194. package/dist/xgmml.d.ts +1 -0
  195. package/dist/xgmml.js +9 -0
  196. package/dist/xgmml.js.map +1 -0
  197. package/package.json +25 -2
  198. package/src/common/cell-budget.ts +170 -0
  199. package/src/common/input.ts +40 -0
  200. package/src/common/json-elements.ts +1147 -0
  201. package/src/common/options.ts +1 -1
  202. package/src/common/xml.ts +127 -6
  203. package/src/common/zip.ts +472 -0
  204. package/src/formats/cx/importer.ts +2733 -0
  205. package/src/formats/cx/index.ts +7 -0
  206. package/src/formats/cx2/exporter.ts +1036 -0
  207. package/src/formats/cx2/importer.ts +2187 -0
  208. package/src/formats/cx2/index.ts +7 -0
  209. package/src/formats/cys/constants.ts +99 -0
  210. package/src/formats/cys/importer.ts +1100 -0
  211. package/src/formats/cys/index.ts +7 -0
  212. package/src/formats/cys/session.ts +428 -0
  213. package/src/formats/cys/tables.ts +400 -0
  214. package/src/formats/dot/exporter.ts +17 -0
  215. package/src/formats/dot/importer.ts +6 -2
  216. package/src/formats/gexf/exporter.ts +11 -0
  217. package/src/formats/gexf/importer.ts +1 -2
  218. package/src/formats/gexf/index.ts +1 -1
  219. package/src/formats/gml/exporter.ts +8 -19
  220. package/src/formats/json/importer.ts +6 -105
  221. package/src/formats/xgmml/columns.ts +747 -0
  222. package/src/formats/xgmml/constants.ts +265 -0
  223. package/src/formats/xgmml/document.ts +975 -0
  224. package/src/formats/xgmml/emit.ts +1563 -0
  225. package/src/formats/xgmml/exporter.ts +1215 -0
  226. package/src/formats/xgmml/importer.ts +491 -0
  227. package/src/formats/xgmml/index.ts +8 -0
  228. package/src/formats/xgmml/values.ts +183 -0
  229. package/src/index.ts +19 -0
  230. package/src/registry.ts +46 -15
  231. package/src/sniff.ts +18 -1
  232. package/dist/chunks/escape-D-gZWO26.js.map +0 -1
  233. package/dist/chunks/importer-Br_QeAeE.js.map +0 -1
  234. package/dist/chunks/importer-Du5crN9l.js.map +0 -1
  235. package/dist/chunks/writer-GAdltGmC.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-elements-DDS8N2Dd.js","sources":["../../src/common/json-elements.ts"],"sourcesContent":["/**\n * The JSON reading the two CX formats share (design section 1.0 of\n * design/graph-io/cytoscape-and-obo/design.md): the exact-integer rewrite (also used by the JSON\n * importer), a streaming scanner that walks a CX document's top-level array and hands over each\n * aspect element as soon as its closing brace arrives (section 1.2.5), and the CX id rule of\n * section 1.0.2.\n *\n * The scanner never holds the document as one string, so a file larger than the longest string a\n * JavaScript engine can hold is still read: it keeps the text of the element being read and\n * nothing else. Each element is parsed with `JSON.parse`; an integer literal beyond 2^53 is kept as\n * an ExactInteger (its digits) so an id never loses a digit.\n */\n\nimport {\n type ColumnDecl,\n type ColumnHandle,\n GraphFormatError,\n type GraphSink,\n INVALID_INDEX,\n type NodeId,\n} from \"@graphty/graph-format\";\n\nimport { declareResolved, uniqueColumnName } from \"./attributes.js\";\nimport {\n ASPECT_ORDER_CODE,\n BAD_ASPECT_BLOCK_CODE,\n COLUMN_RENAMED_CODE,\n COUNT_MISMATCH_CODE,\n STATUS_FAILED_CODE,\n STATUS_WARNING_CODE,\n} from \"./codes.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\n\n// ============================================================ exact integers\n\n/** A run of 16 digits not inside a fraction: the shortest integer literal that can exceed 2^53 (9007199254740992). */\nexport const MAYBE_UNSAFE_INTEGER = /(?<![0-9.])[0-9]{16}/;\n\n/**\n * The prefix of the string a non-standard token or an exact integer is rewritten to (a NUL\n * character first, which no sensible attribute value starts with).\n */\nconst SENTINEL = `${String.fromCharCode(0)}graph-io:`;\n\n/** The sentinel prefix of an integer literal kept as its digits. */\nconst EXACT_SENTINEL = `${SENTINEL}exact:`;\n\n/** The non-standard tokens Python's json module writes, longest first so -Infinity wins over a bare minus. */\nconst NONSTANDARD_TOKENS: readonly (readonly [string, number])[] = [\n [\"-Infinity\", -Infinity],\n [\"Infinity\", Infinity],\n [\"NaN\", NaN],\n];\n\n/** A JSON integer literal (no fraction, no exponent, no leading zero), as CANONICAL_INTEGER in common/ids.ts. */\nconst INTEGER_LITERAL = /^-?(0|[1-9][0-9]*)$/;\n\n/** What rewriteNumbers() found. */\ninterface RewrittenNumbers {\n /** The rewritten text. */\n readonly text: string;\n /** The non-standard tokens seen (only when they were allowed). */\n readonly tokens: Set<string>;\n /** The integer literals beyond 2^53 that were quoted. */\n readonly bigIntegers: string[];\n}\n\n/**\n * Rewrite the numbers JSON.parse cannot read exactly, outside strings and in value positions only:\n * NaN / Infinity / -Infinity become sentinel strings (when `nonstandard` is true), an integer\n * literal that is not a safe integer becomes a string of its digits (or, with `exactSentinel`, a\n * sentinel string reviveExact() turns into an ExactInteger). A container stack tells a value\n * position (after `:`, `[`, or `,` inside an array) from a key position, so `{NaN: 1}` stays invalid.\n * @param text - the document text\n * @param options - which rewrites apply\n * @param options.nonstandard - rewrite NaN / Infinity / -Infinity (default true)\n * @param options.exactSentinel - quote big integers as sentinels instead of bare digits (default false)\n * @returns the rewritten text, the non-standard tokens seen and the integer literals quoted\n */\nexport function rewriteNumbers(\n text: string,\n options: { readonly nonstandard?: boolean; readonly exactSentinel?: boolean } = {},\n): RewrittenNumbers {\n const nonstandard = options.nonstandard ?? true;\n const exactSentinel = options.exactSentinel ?? false;\n const parts: string[] = [];\n const tokens = new Set<string>();\n const bigIntegers: string[] = [];\n const arrays: boolean[] = [];\n let expectValue = true;\n let copied = 0;\n let i = 0;\n const n = text.length;\n while (i < n) {\n const ch = text[i];\n if (ch === '\"') {\n i++;\n while (i < n && text[i] !== '\"') {\n i += text[i] === \"\\\\\" ? 2 : 1;\n }\n i++;\n expectValue = false;\n continue;\n }\n if (ch === \"{\" || ch === \"[\") {\n arrays.push(ch === \"[\");\n expectValue = ch === \"[\";\n } else if (ch === \"}\" || ch === \"]\") {\n arrays.pop();\n expectValue = false;\n } else if (ch === \":\") {\n expectValue = true;\n } else if (ch === \",\") {\n expectValue = arrays.length > 0 && arrays[arrays.length - 1];\n } else if (expectValue && ch !== \" \" && ch !== \"\\t\" && ch !== \"\\n\" && ch !== \"\\r\") {\n expectValue = false;\n const token = nonstandard ? NONSTANDARD_TOKENS.find(([word]) => text.startsWith(word, i)) : undefined;\n let end = i;\n let replacement: string | null = null;\n if (token !== undefined) {\n end = i + token[0].length;\n tokens.add(token[0]);\n replacement = JSON.stringify(`${SENTINEL}${token[0]}`);\n } else if (ch === \"-\" || (ch >= \"0\" && ch <= \"9\")) {\n end = i + 1;\n while (end < n && \"0123456789+-.eE\".includes(text[end])) {\n end++;\n }\n const literal = text.slice(i, end);\n if (INTEGER_LITERAL.test(literal) && !Number.isSafeInteger(Number(literal))) {\n bigIntegers.push(literal);\n replacement = exactSentinel ? JSON.stringify(`${EXACT_SENTINEL}${literal}`) : `\"${literal}\"`;\n }\n }\n if (replacement !== null) {\n parts.push(text.slice(copied, i), replacement);\n copied = end;\n }\n i = Math.max(end, i + 1);\n continue;\n }\n i++;\n }\n parts.push(text.slice(copied));\n return { text: parts.join(\"\"), tokens, bigIntegers };\n}\n\n/**\n * The JSON.parse reviver that turns the non-standard sentinel strings of rewriteNumbers() back into\n * numbers.\n * @param _key - the member key (unused)\n * @param value - the parsed value\n * @returns the number for a sentinel string, the value otherwise\n */\nexport function reviveNonstandard(_key: string, value: unknown): unknown {\n if (typeof value === \"string\" && value.startsWith(SENTINEL)) {\n const found = NONSTANDARD_TOKENS.find(([word]) => word === value.slice(SENTINEL.length));\n return found === undefined ? value : found[1];\n }\n return value;\n}\n\n/** An integer literal beyond 2^53, kept as its exact digits (a CX id must never lose a digit). */\nexport class ExactInteger {\n /** The digits, with a leading minus for a negative value. */\n readonly digits: string;\n\n /**\n * Wrap the digits of an integer literal.\n * @param digits - the literal\n */\n constructor(digits: string) {\n this.digits = digits;\n }\n\n /**\n * The nearest number (JSON.stringify writes it as such).\n * @returns the number\n */\n toJSON(): number {\n return Number(this.digits);\n }\n}\n\n/**\n * The JSON.parse reviver of an element whose big integers were rewritten as exact sentinels.\n * @param _key - the member key (unused)\n * @param value - the parsed value\n * @returns an ExactInteger for an exact sentinel, the value otherwise\n */\nfunction reviveExact(_key: string, value: unknown): unknown {\n if (typeof value === \"string\" && value.startsWith(EXACT_SENTINEL)) {\n return new ExactInteger(value.slice(EXACT_SENTINEL.length));\n }\n return value;\n}\n\n/**\n * Parse one JSON value, keeping integer literals beyond 2^53 as ExactInteger.\n * @param text - the JSON text\n * @returns the value and whether it may hold an ExactInteger; SyntaxError when it is not JSON\n */\nexport function parseExact(text: string): { readonly value: unknown; readonly exact: boolean } {\n if (!MAYBE_UNSAFE_INTEGER.test(text)) {\n return { value: JSON.parse(text) as unknown, exact: false };\n }\n const scan = rewriteNumbers(text, { nonstandard: false, exactSentinel: true });\n if (scan.bigIntegers.length === 0) {\n return { value: JSON.parse(text) as unknown, exact: false };\n }\n return { value: JSON.parse(scan.text, reviveExact) as unknown, exact: true };\n}\n\n/**\n * A parsed value with every ExactInteger replaced by its nearest number, for metadata and for\n * values that are not ids.\n * @param value - the value\n * @param onPrecision - called with the digits of each integer that lost precision\n * @returns the plain JSON value\n */\nexport function plainJson(value: unknown, onPrecision?: (digits: string) => void): unknown {\n if (value instanceof ExactInteger) {\n onPrecision?.(value.digits);\n return Number(value.digits);\n }\n if (Array.isArray(value)) {\n return value.map((item) => plainJson(item, onPrecision));\n }\n if (typeof value === \"object\" && value !== null) {\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n out[key] = plainJson(item, onPrecision);\n }\n return out;\n }\n return value;\n}\n\n// ============================================================ the streaming aspect scanner\n\n/** A problem with the document's JSON; the importer turns it into a fatal E_SYNTAX or E_EMPTY_INPUT. */\nexport class JsonScanError extends Error {\n /** The 1-based line of the problem. */\n readonly line: number;\n\n /** Whether the document holds nothing but whitespace. */\n readonly empty: boolean;\n\n /**\n * Create the error.\n * @param message - what is wrong\n * @param line - the 1-based line\n * @param empty - whether the input is empty\n */\n constructor(message: string, line: number, empty = false) {\n super(message);\n this.name = \"JsonScanError\";\n this.line = line;\n this.empty = empty;\n }\n}\n\n/** One thing the scanner found in a CX document. */\nexport type AspectEvent =\n | {\n /** A top-level value that is not an array: the whole document, parsed. */\n readonly kind: \"root\";\n readonly value: unknown;\n readonly line: number;\n }\n | {\n /** A member `{\"<aspect>\": [` whose elements follow one by one. */\n readonly kind: \"block\";\n readonly aspect: string;\n readonly block: number;\n readonly line: number;\n }\n | {\n /** One element of the current block, parsed. */\n readonly kind: \"element\";\n readonly aspect: string;\n readonly block: number;\n readonly value: unknown;\n /** Whether the value may hold an ExactInteger. */\n readonly exact: boolean;\n /** The element's JSON text (for lexical checks of its ids). */\n readonly text: string;\n readonly line: number;\n }\n | {\n /** Keys after the first one of a block member (`{\"nodes\": [...], \"edges\": [...]}`); their values were skipped. */\n readonly kind: \"extraKeys\";\n readonly aspect: string;\n readonly block: number;\n readonly keys: readonly string[];\n readonly line: number;\n }\n | {\n /**\n * An element, or a member, nested deeper than MAX_ELEMENT_DEPTH: not parsed, so nothing\n * downstream recurses into it (aspect: the block's aspect, null for a member).\n */\n readonly kind: \"deep\";\n readonly aspect: string | null;\n readonly block: number;\n readonly depth: number;\n readonly line: number;\n }\n | {\n /** Any other member of the top-level array (a CX2 descriptor, `{\"ndexStatus\": {...}}`, `1`), parsed. */\n readonly kind: \"member\";\n readonly block: number;\n readonly value: unknown;\n readonly exact: boolean;\n readonly line: number;\n };\n\n/**\n * Whether a character code is whitespace to JSON.\n * @param c - the character code\n * @returns true for space, line feed, carriage return and tab\n */\nconst isSpace = (c: number): boolean => c === 32 || c === 10 || c === 13 || c === 9;\n\n/**\n * Whether a character code ends a bare scalar (number, true, false, null).\n * @param c - the character code\n * @returns true for whitespace, a comma, a closing bracket or brace, or a colon\n */\nconst endsScalar = (c: number): boolean => isSpace(c) || c === 44 || c === 93 || c === 125 || c === 58;\n\nconst QUOTE_OR_BACKSLASH = /[\"\\\\]/g;\n\n/**\n * The deepest nesting of an element the scanner parses. graph-format refuses JSON values (a json\n * cell, meta.extra) nested deeper than 256 levels, and a kept element sits a few levels down\n * meta.extra; a deeper element is reported instead, before anything recurses into it.\n */\nconst MAX_ELEMENT_DEPTH = 200;\n\n/**\n * A cursor over text chunks with capture: the text of the value being read is kept across chunk\n * boundaries as a list of parts, so a long element costs linear time.\n */\nclass ChunkCursor {\n private readonly iterator: AsyncIterator<string>;\n\n private buf = \"\";\n\n /** The read position in buf. */\n pos = 0;\n\n /** The line number of buf's first character. */\n private linesBefore = 1;\n\n private parts: string[] | null = null;\n\n private captureStart = 0;\n\n /** The deepest nesting the last skipValue() reached. */\n deepest = 0;\n\n /**\n * Wrap text chunks.\n * @param chunks - the decoded text\n */\n constructor(chunks: AsyncIterable<string>) {\n this.iterator = chunks[Symbol.asyncIterator]();\n }\n\n /**\n * Read the next chunk once the current one is consumed.\n * @returns false at the end of the input\n */\n async fill(): Promise<boolean> {\n if (this.parts !== null) {\n this.parts.push(this.buf.slice(this.captureStart));\n this.captureStart = 0;\n }\n this.linesBefore += countNewlines(this.buf, 0, this.buf.length);\n for (;;) {\n const next = await this.iterator.next();\n if (next.done === true) {\n this.buf = \"\";\n this.pos = 0;\n return false;\n }\n if (next.value.length > 0) {\n this.buf = next.value;\n this.pos = 0;\n return true;\n }\n }\n }\n\n /**\n * The 1-based line of the read position.\n * @returns the line\n */\n line(): number {\n return this.linesBefore + countNewlines(this.buf, 0, Math.min(this.pos, this.buf.length));\n }\n\n /**\n * Skip whitespace and return the next character without consuming it.\n * @returns the character, or \"\" at the end of the input\n */\n async peek(): Promise<string> {\n for (;;) {\n while (this.pos < this.buf.length && isSpace(this.buf.charCodeAt(this.pos))) {\n this.pos++;\n }\n if (this.pos < this.buf.length) {\n return this.buf[this.pos];\n }\n if (!(await this.fill())) {\n return \"\";\n }\n }\n }\n\n /** Start keeping the text from the read position. */\n beginCapture(): void {\n this.parts = [];\n this.captureStart = this.pos;\n }\n\n /**\n * Stop keeping text.\n * @returns the text kept since beginCapture()\n */\n endCapture(): string {\n const parts = this.parts ?? [];\n parts.push(this.buf.slice(this.captureStart, this.pos));\n this.parts = null;\n return parts.length === 1 ? parts[0] : parts.join(\"\");\n }\n\n /** Drop the capture without building its text. */\n dropCapture(): void {\n this.parts = null;\n }\n\n /**\n * Advance past one JSON value starting at the read position (which holds its first\n * character), tracking strings and nesting only; the value is validated when it is parsed.\n * @param startDepth - the nesting depth already entered (1 when the opening brace was consumed)\n */\n async skipValue(startDepth = 0): Promise<void> {\n let depth = startDepth;\n this.deepest = depth;\n if (depth === 0) {\n const c = this.buf.charCodeAt(this.pos);\n if (c === 34) {\n this.pos++;\n await this.skipString();\n return;\n }\n if (c !== 123 && c !== 91) {\n await this.skipScalar();\n return;\n }\n }\n for (;;) {\n if (this.pos >= this.buf.length && !(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a value\", this.line());\n }\n const c = this.buf.charCodeAt(this.pos);\n this.pos++;\n if (c === 34) {\n await this.skipString();\n } else if (c === 123 || c === 91) {\n depth++;\n if (depth > this.deepest) {\n this.deepest = depth;\n }\n } else if (c === 125 || c === 93) {\n depth--;\n if (depth === 0) {\n return;\n }\n }\n }\n }\n\n /** Advance past the rest of a string whose opening quote was consumed. */\n private async skipString(): Promise<void> {\n for (;;) {\n QUOTE_OR_BACKSLASH.lastIndex = this.pos;\n const found = QUOTE_OR_BACKSLASH.exec(this.buf);\n if (found === null) {\n this.pos = this.buf.length;\n if (!(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a string\", this.line());\n }\n continue;\n }\n this.pos = found.index + 1;\n if (found[0] === '\"') {\n return;\n }\n // a backslash: skip the escaped character, which may be in the next chunk\n if (this.pos >= this.buf.length && !(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a string\", this.line());\n }\n this.pos++;\n }\n }\n\n /** Advance past a bare scalar (a number, true, false, null or a stray word). */\n private async skipScalar(): Promise<void> {\n for (;;) {\n while (this.pos < this.buf.length && !endsScalar(this.buf.charCodeAt(this.pos))) {\n this.pos++;\n }\n if (this.pos < this.buf.length || !(await this.fill())) {\n return;\n }\n }\n }\n\n /**\n * Read one JSON string literal at the read position (its opening quote).\n * @returns the decoded string\n */\n async readString(): Promise<string> {\n this.beginCapture();\n this.pos++;\n await this.skipString();\n const literal = this.endCapture();\n try {\n return JSON.parse(literal) as string;\n } catch (err) {\n throw new JsonScanError(`invalid string: ${messageOf(err)}`, this.line());\n }\n }\n}\n\n/**\n * The number of line feeds in a slice of a string.\n * @param text - the string\n * @param from - the first index\n * @param to - one past the last index\n * @returns the count\n */\nfunction countNewlines(text: string, from: number, to: number): number {\n let count = 0;\n let i = text.indexOf(\"\\n\", from);\n while (i >= 0 && i < to) {\n count++;\n i = text.indexOf(\"\\n\", i + 1);\n }\n return count;\n}\n\n/**\n * The message of a caught value.\n * @param err - the thrown value\n * @returns the message\n */\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Parse a captured value, turning a parse failure into a JsonScanError at its line.\n * @param text - the value text\n * @param line - the line the value starts on\n * @returns the value and whether it may hold an ExactInteger\n */\nfunction parseAt(text: string, line: number): { readonly value: unknown; readonly exact: boolean } {\n try {\n return parseExact(text);\n } catch (err) {\n throw new JsonScanError(`invalid JSON: ${messageOf(err)}`, line);\n }\n}\n\n/**\n * Walk a CX document (CX1 or CX2): a top-level array of members, each normally a one-key object\n * `{\"<aspect>\": [elements]}`. The elements of such a block are parsed and yielded one at a time;\n * any other member is parsed whole and yielded as a \"member\". A document that is not an array is\n * yielded as one \"root\" event. Syntax errors throw JsonScanError.\n * @param chunks - the decoded text\n * @yields the events in document order\n * @returns nothing\n */\nexport async function* scanAspects(chunks: AsyncIterable<string>): AsyncGenerator<AspectEvent, void, undefined> {\n const cur = new ChunkCursor(chunks);\n const first = await cur.peek();\n if (first === \"\") {\n throw new JsonScanError(\"the input is empty\", 1, true);\n }\n if (first !== \"[\") {\n const line = cur.line();\n cur.beginCapture();\n await cur.skipValue();\n const { value } = parseAt(cur.endCapture(), line);\n if ((await cur.peek()) !== \"\") {\n throw new JsonScanError(\"unexpected text after the document\", cur.line());\n }\n yield { kind: \"root\", value, line };\n return;\n }\n cur.pos++;\n let block = 0;\n if ((await cur.peek()) === \"]\") {\n cur.pos++;\n } else {\n for (;;) {\n yield* member(cur, block);\n block++;\n const next = await cur.peek();\n if (next === \",\") {\n cur.pos++;\n continue;\n }\n if (next === \"]\") {\n cur.pos++;\n break;\n }\n throw new JsonScanError(\n next === \"\" ? \"the document ends before its closing bracket\" : `expected \",\" or \"]\", found \"${next}\"`,\n cur.line(),\n );\n }\n }\n if ((await cur.peek()) !== \"\") {\n throw new JsonScanError(\"unexpected text after the closing bracket\", cur.line());\n }\n}\n\n/**\n * Read one member of the top-level array.\n * @param cur - the cursor at the member's first character\n * @param block - the member's position among the members\n * @yields the member's events\n * @returns nothing\n */\nasync function* member(cur: ChunkCursor, block: number): AsyncGenerator<AspectEvent, void, undefined> {\n const c = await cur.peek();\n const line = cur.line();\n if (c === \"\" || c === \"]\" || c === \",\") {\n throw new JsonScanError(c === \"\" ? \"the document ends inside its array\" : `unexpected \"${c}\"`, line);\n }\n if (c !== \"{\") {\n cur.beginCapture();\n await cur.skipValue();\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n yield { kind: \"deep\", aspect: null, block, depth: cur.deepest, line };\n return;\n }\n const parsed = parseAt(cur.endCapture(), line);\n yield { kind: \"member\", block, value: parsed.value, exact: parsed.exact, line };\n return;\n }\n cur.beginCapture();\n cur.pos++;\n const k = await cur.peek();\n if (k !== '\"') {\n // `{}` or not JSON: parse the whole member so a syntax error is reported as such\n await cur.skipValue(1);\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n yield { kind: \"deep\", aspect: null, block, depth: cur.deepest, line };\n return;\n }\n const parsed = parseAt(cur.endCapture(), line);\n yield { kind: \"member\", block, value: parsed.value, exact: parsed.exact, line };\n return;\n }\n // the outer capture keeps the member's text in case it is not a block; readString nests its own\n const outer = cur.endCapture();\n const keyLine = cur.line();\n const aspect = await cur.readString();\n if ((await cur.peek()) !== \":\") {\n throw new JsonScanError(`expected \":\" after the key \"${aspect}\"`, cur.line());\n }\n cur.pos++;\n const v = await cur.peek();\n if (v !== \"[\") {\n // not a block: read the rest of the object and parse the member whole\n cur.beginCapture();\n await cur.skipValue(1);\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n yield { kind: \"deep\", aspect, block, depth: cur.deepest, line };\n return;\n }\n const rest = cur.endCapture();\n const text = `${outer}${JSON.stringify(aspect)}:${rest}`;\n const parsed = parseAt(text, keyLine);\n yield { kind: \"member\", block, value: parsed.value, exact: parsed.exact, line };\n return;\n }\n cur.pos++;\n yield { kind: \"block\", aspect, block, line };\n if ((await cur.peek()) === \"]\") {\n cur.pos++;\n } else {\n for (;;) {\n const e = await cur.peek();\n if (e === \"\") {\n throw new JsonScanError(`the document ends inside the \"${aspect}\" block`, cur.line());\n }\n const elementLine = cur.line();\n cur.beginCapture();\n await cur.skipValue();\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n yield { kind: \"deep\", aspect, block, depth: cur.deepest, line: elementLine };\n } else {\n const text = cur.endCapture();\n const parsed = parseAt(text, elementLine);\n yield {\n kind: \"element\",\n aspect,\n block,\n value: parsed.value,\n exact: parsed.exact,\n text,\n line: elementLine,\n };\n }\n const next = await cur.peek();\n if (next === \",\") {\n cur.pos++;\n continue;\n }\n if (next === \"]\") {\n cur.pos++;\n break;\n }\n throw new JsonScanError(\n next === \"\"\n ? `the document ends inside the \"${aspect}\" block`\n : `expected \",\" or \"]\" in the \"${aspect}\" block, found \"${next}\"`,\n cur.line(),\n );\n }\n }\n const end = await cur.peek();\n if (end === \"}\") {\n cur.pos++;\n return;\n }\n if (end !== \",\") {\n throw new JsonScanError(\n end === \"\" ? \"the document ends inside a member\" : `expected \"}\" after the \"${aspect}\" block`,\n cur.line(),\n );\n }\n // more keys: a malformed block; skip their values and name them\n const keys: string[] = [];\n const extraLine = cur.line();\n while ((await cur.peek()) === \",\") {\n cur.pos++;\n if ((await cur.peek()) !== '\"') {\n throw new JsonScanError(\"expected a key\", cur.line());\n }\n keys.push(await cur.readString());\n if ((await cur.peek()) !== \":\") {\n throw new JsonScanError('expected \":\"', cur.line());\n }\n cur.pos++;\n await cur.peek();\n cur.beginCapture();\n await cur.skipValue();\n parseAt(cur.endCapture(), cur.line());\n }\n if ((await cur.peek()) !== \"}\") {\n throw new JsonScanError('expected \"}\"', cur.line());\n }\n cur.pos++;\n yield { kind: \"extraKeys\", aspect, block, keys, line: extraLine };\n}\n\n/**\n * Report an element or member the scanner refused for its depth (E_BAD_ASPECT_BLOCK; skipped).\n * @param report - the report\n * @param event - the \"deep\" event\n */\nexport function reportTooDeep(report: ImportReportBuilder, event: Extract<AspectEvent, { kind: \"deep\" }>): void {\n report.error(\n \"parse-error\",\n BAD_ASPECT_BLOCK_CODE,\n `${event.aspect === null ? \"a member of the document\" : `an element of \"${event.aspect}\"`} is nested ${event.depth} levels deep, more than the ${MAX_ELEMENT_DEPTH} a graph keeps; skipped`,\n { line: event.line, element: event.aspect },\n );\n}\n\n// ============================================================ the CX id rule (design section 1.0.2)\n\n/** How an id was spelled, when it was not a plain safe integer. */\ntype CxIdNote = \"text\" | \"precision\" | null;\n\n/** A CX id, decided per id: a safe integer is a number, a larger one its exact digits. */\ninterface CxId {\n /** The node or edge id. */\n readonly id: NodeId;\n /** \"text\": spelled as a string or a non-integer literal (W_ID_TEXT_TYPE); \"precision\": beyond 2^53 (W_PRECISION). */\n readonly note: CxIdNote;\n}\n\nconst DECIMAL_INTEGER_TEXT = /^-?(0|[1-9][0-9]*)$/;\n\n/**\n * Apply the CX id rule (design section 1.0.2) to a parsed value: a safe integer is the id; an\n * integer beyond 2^53 is its exact digit string; a string spelling a decimal integer is that\n * integer (as Jackson coerces it); anything else is E_INVALID_ID.\n * @param raw - the parsed value\n * @param inexactLiteral - whether the number was written as a non-integer literal (`5.0`, `1e3`)\n * @returns the id and how it was spelled\n */\nexport function cxId(raw: unknown, inexactLiteral = false): CxId {\n if (raw instanceof ExactInteger) {\n return { id: raw.digits, note: \"precision\" };\n }\n if (typeof raw === \"number\") {\n if (Number.isSafeInteger(raw)) {\n return { id: raw === 0 ? 0 : raw, note: inexactLiteral ? \"text\" : null };\n }\n throw invalidCxId(raw, Number.isInteger(raw) ? \"an integer beyond 2^53 written inexactly\" : \"not an integer\");\n }\n if (typeof raw === \"string\" && raw !== \"-0\" && DECIMAL_INTEGER_TEXT.test(raw)) {\n const n = Number(raw);\n return Number.isSafeInteger(n) ? { id: n, note: \"text\" } : { id: raw, note: \"precision\" };\n }\n throw invalidCxId(raw, \"a CX id is an integer\");\n}\n\n/**\n * The E_INVALID_ID error of a rejected CX id.\n * @param raw - the value\n * @param reason - why\n * @returns the error\n */\nfunction invalidCxId(raw: unknown, reason: string): GraphFormatError {\n const shown = typeof raw === \"string\" || typeof raw === \"number\" ? JSON.stringify(raw) : typeof raw;\n return new GraphFormatError(\"E_INVALID_ID\", `invalid id ${raw === null ? \"null\" : shown}: ${reason}`, {\n reason,\n });\n}\n\n/**\n * Whether the element text writes a top-level key's number as a non-integer literal (`\"id\": 5.0`,\n * `\"@id\": 1e3`). A lexical check, so the parsed value (5) cannot tell. Only a key of the element\n * itself counts, not one nested in its `v` (where `s` and `id` are ordinary attribute names).\n * @param text - the element's JSON text\n * @param key - the key\n * @returns true when the literal holds a fraction or an exponent\n */\nexport function inexactLiteral(text: string, key: string): boolean {\n const quoted = JSON.stringify(key);\n let depth = 0;\n for (let i = 0; i < text.length; i++) {\n const c = text[i];\n if (c === \"{\" || c === \"[\") {\n depth++;\n } else if (c === \"}\" || c === \"]\") {\n depth--;\n } else if (c === '\"') {\n const start = i;\n for (i++; i < text.length && text[i] !== '\"'; i++) {\n if (text[i] === \"\\\\\") {\n i++;\n }\n }\n // the key itself (a string at depth 1 followed by a colon), not a value spelled like it\n if (depth === 1 && text.startsWith(quoted, start) && /^\\s*:/.test(text.slice(i + 1, i + 16))) {\n const match = /^\\s*:\\s*(-?[0-9][0-9.eE+-]*)/.exec(text.slice(i + 1, i + 64));\n return match !== null && /[.eE]/.test(match[1]);\n }\n }\n }\n return false;\n}\n\n// ============================================================ Cytoscape positions (design section 1.0.2)\n\n/** The name of the position column of every Cytoscape-family importer. */\nexport const POSITION_COLUMN = \"position\";\n\n/** The name of the stacking-order column (Cytoscape's NODE_Z_LOCATION). */\nconst Z_COLUMN = \"z\";\n\n/**\n * The position column of a Cytoscape-family importer: f32 x3, y-up, 2 source dimensions.\n * @param format - the importer's format name\n * @param sourceDims - 3 when z goes into the position (zAs \"position\")\n * @returns the declaration\n */\nexport function positionDecl(format: string, sourceDims = 2): ColumnDecl {\n return {\n name: POSITION_COLUMN,\n dtype: \"f32\",\n components: 3,\n role: \"position\",\n mutable: true,\n nullable: true,\n extra: { sourceDims, units: \"file\" },\n origin: { format, namespace: \"cytoscape\" },\n };\n}\n\n/**\n * The stacking-order column `z` (f64, origin namespace \"cytoscape\").\n * @param format - the importer's format name\n * @returns the declaration\n */\nexport function zDecl(format: string): ColumnDecl {\n return {\n name: Z_COLUMN,\n dtype: \"f64\",\n nullable: true,\n origin: { format, id: null, namespace: \"cytoscape\" },\n extra: { cytoscape: \"z\" },\n };\n}\n\n/**\n * Cytoscape's screen y (growing downward) as graph-io's y (growing upward), and back: a negation\n * that never yields -0.\n * @param y - the coordinate\n * @returns the flipped coordinate\n */\nexport function flipY(y: number): number {\n return y === 0 ? 0 : -y;\n}\n\n// ============================================================ stream structure shared by CX1 and CX2\n\n/** The aspect names of the stream structure, not of the network. */\nconst STRUCTURE_ASPECTS: ReadonlySet<string> = new Set([\"metaData\", \"status\", \"numberVerification\"]);\n\n/**\n * The order, metadata and status rules CX1 and CX2 share (design section 1.0.1): a third\n * `metaData` or an aspect after the post-metadata or after `status` is W_ASPECT_ORDER; declared\n * element counts are compared with what was read (W_COUNT_MISMATCH); `status.success: false` is\n * fatal (E_STATUS_FAILED) and `success: true` with an error text a warning (W_STATUS_WARNING).\n */\nexport class CxStructure {\n private readonly report: ImportReportBuilder;\n\n /** Elements read per aspect name. */\n readonly counts = new Map<string, number>();\n\n /** Blocks read per aspect name. */\n readonly blocks = new Map<string, number>();\n\n /** The element counts the metadata declares, by aspect name (a later declaration wins). */\n private readonly declared = new Map<string, number>();\n\n private metaDataBlocks = 0;\n\n private networkBlocks = 0;\n\n private postMetadata = false;\n\n private statusSeen = false;\n\n /** The status element, when one was read. */\n status: unknown = undefined;\n\n /**\n * Whether a status block was read.\n * @returns true once a status block began\n */\n get hasStatus(): boolean {\n return this.statusSeen;\n }\n\n /**\n * Create the checker.\n * @param report - the report\n */\n constructor(report: ImportReportBuilder) {\n this.report = report;\n }\n\n /**\n * Record the start of a block (or a member holding one aspect).\n * @param aspect - the aspect name\n * @param line - its line\n */\n block(aspect: string, line: number): void {\n this.blocks.set(aspect, (this.blocks.get(aspect) ?? 0) + 1);\n if (this.statusSeen) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `the \"${aspect}\" block comes after the status block, which must be last; it is read`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:status`,\n );\n }\n if (aspect === \"metaData\") {\n this.metaDataBlocks++;\n if (this.networkBlocks > 0) {\n this.postMetadata = true;\n }\n if (this.metaDataBlocks > 2) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `${this.metaDataBlocks} metaData blocks; a document has at most two (pre and post)`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:metaData`,\n );\n }\n return;\n }\n if (aspect === \"status\") {\n this.statusSeen = true;\n return;\n }\n if (STRUCTURE_ASPECTS.has(aspect)) {\n return;\n }\n this.networkBlocks++;\n if (this.postMetadata) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `the \"${aspect}\" block comes after the post-metadata, where only status is allowed; it is read`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:post`,\n );\n }\n }\n\n /**\n * Record one element of a block; metaData and status elements are checked here.\n * @param aspect - the aspect name\n * @param value - the parsed element\n * @param line - its line\n */\n element(aspect: string, value: unknown, line: number): void {\n this.counts.set(aspect, (this.counts.get(aspect) ?? 0) + 1);\n if (aspect === \"metaData\") {\n if (isRecord(value) && typeof value.name === \"string\" && typeof value.elementCount === \"number\") {\n this.declared.set(value.name, value.elementCount);\n }\n return;\n }\n if (aspect === \"status\") {\n this.readStatus(value, line);\n }\n }\n\n /**\n * Check a status element: success false is fatal, an error text with success true a warning.\n * @param value - the element\n * @param line - its line\n */\n private readStatus(value: unknown, line: number): void {\n if (this.status !== undefined) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n \"more than one status element; the last is used\",\n { line, element: \"status\" },\n `${ASPECT_ORDER_CODE}:status2`,\n );\n }\n this.status = value;\n if (!isRecord(value)) {\n return;\n }\n const error = typeof value.error === \"string\" ? value.error : \"\";\n if (value.success === false) {\n const message = `the producer marked the document as failed${error.length > 0 ? `: ${error}` : \"\"}`;\n this.report.error(\"validation-error\", STATUS_FAILED_CODE, message, { line, element: \"status\" });\n throw this.report.abort(message, { code: STATUS_FAILED_CODE });\n }\n if (value.success === true && error.length > 0) {\n this.report.warning(\"validation-error\", STATUS_WARNING_CODE, `the producer reports a warning: ${error}`, {\n line,\n element: \"status\",\n });\n }\n }\n\n /**\n * Whether the status element is one object with a boolean success.\n * @returns true for a well-formed status\n */\n statusWellFormed(): boolean {\n return isRecord(this.status) && typeof this.status.success === \"boolean\";\n }\n\n /** Compare the declared element counts with what was read (W_COUNT_MISMATCH per aspect). */\n checkCounts(): void {\n for (const [name, declared] of this.declared) {\n const read = this.counts.get(name) ?? 0;\n if (read !== declared) {\n this.report.warning(\n \"validation-error\",\n COUNT_MISMATCH_CODE,\n `metaData declares ${declared} \"${name}\" element(s); ${read} were read`,\n { element: name },\n );\n }\n }\n }\n}\n\n/**\n * Whether a value is a plain JSON object.\n * @param value - any value\n * @returns true for a non-null, non-array object\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof ExactInteger);\n}\n\n/**\n * Declare a column under a name no column of the table holds yet: a per-element visual property\n * whose name an attribute already has gets `<name>#2` (W_COLUMN_RENAMED), even when the two share a\n * shape, so the visual value never overwrites the attribute.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the declaration\n * @param report - the report the rename is recorded in\n * @returns the handle\n */\nexport function declareFresh(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n decl: ColumnDecl,\n report: ImportReportBuilder,\n): ColumnHandle {\n const taken = (name: string): boolean =>\n (domain === \"node\" ? sink.nodeColumn(name) : sink.edgeColumn(name)) !== INVALID_INDEX;\n let fresh = decl;\n if (taken(decl.name)) {\n fresh = { ...decl, name: uniqueColumnName(decl.name, null, taken) };\n report.warning(\n \"coercion\",\n COLUMN_RENAMED_CODE,\n `${domain} column \"${decl.name}\" renamed to \"${fresh.name}\": an attribute holds that name`,\n { element: decl.name },\n );\n }\n return declareResolved(sink, domain, fresh, report).handle;\n}\n"],"names":["inexactLiteral"],"mappings":";;AAoCO,MAAM,uBAAuB,WAAA,sBAAA;AAMpC,MAAM,WAAW,GAAG,OAAO,aAAa,CAAC,CAAC;AAG1C,MAAM,iBAAiB,GAAG,QAAQ;AAGlC,MAAM,qBAA6D;AAAA,EAC/D,CAAC,aAAa,SAAS;AAAA,EACvB,CAAC,YAAY,QAAQ;AAAA,EACrB,CAAC,OAAO,GAAG;AACf;AAGA,MAAM,kBAAkB;AAwBjB,SAAS,eACZ,MACA,UAAgF,IAChE;AAChB,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,QAAkB,CAAA;AACxB,QAAM,6BAAa,IAAA;AACnB,QAAM,cAAwB,CAAA;AAC9B,QAAM,SAAoB,CAAA;AAC1B,MAAI,cAAc;AAClB,MAAI,SAAS;AACb,MAAI,IAAI;AACR,QAAM,IAAI,KAAK;AACf,SAAO,IAAI,GAAG;AACV,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,KAAK;AACZ;AACA,aAAO,IAAI,KAAK,KAAK,CAAC,MAAM,KAAK;AAC7B,aAAK,KAAK,CAAC,MAAM,OAAO,IAAI;AAAA,MAChC;AACA;AACA,oBAAc;AACd;AAAA,IACJ;AACA,QAAI,OAAO,OAAO,OAAO,KAAK;AAC1B,aAAO,KAAK,OAAO,GAAG;AACtB,oBAAc,OAAO;AAAA,IACzB,WAAW,OAAO,OAAO,OAAO,KAAK;AACjC,aAAO,IAAA;AACP,oBAAc;AAAA,IAClB,WAAW,OAAO,KAAK;AACnB,oBAAc;AAAA,IAClB,WAAW,OAAO,KAAK;AACnB,oBAAc,OAAO,SAAS,KAAK,OAAO,OAAO,SAAS,CAAC;AAAA,IAC/D,WAAW,eAAe,OAAO,OAAO,OAAO,OAAQ,OAAO,QAAQ,OAAO,MAAM;AAC/E,oBAAc;AACd,YAAM,QAAQ,cAAc,mBAAmB,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK,WAAW,MAAM,CAAC,CAAC,IAAI;AAC5F,UAAI,MAAM;AACV,UAAI,cAA6B;AACjC,UAAI,UAAU,QAAW;AACrB,cAAM,IAAI,MAAM,CAAC,EAAE;AACnB,eAAO,IAAI,MAAM,CAAC,CAAC;AACnB,sBAAc,KAAK,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC,EAAE;AAAA,MACzD,WAAW,OAAO,OAAQ,MAAM,OAAO,MAAM,KAAM;AAC/C,cAAM,IAAI;AACV,eAAO,MAAM,KAAK,kBAAkB,SAAS,KAAK,GAAG,CAAC,GAAG;AACrD;AAAA,QACJ;AACA,cAAM,UAAU,KAAK,MAAM,GAAG,GAAG;AACjC,YAAI,gBAAgB,KAAK,OAAO,KAAK,CAAC,OAAO,cAAc,OAAO,OAAO,CAAC,GAAG;AACzE,sBAAY,KAAK,OAAO;AACxB,wBAAc,gBAAgB,KAAK,UAAU,GAAG,cAAc,GAAG,OAAO,EAAE,IAAI,IAAI,OAAO;AAAA,QAC7F;AAAA,MACJ;AACA,UAAI,gBAAgB,MAAM;AACtB,cAAM,KAAK,KAAK,MAAM,QAAQ,CAAC,GAAG,WAAW;AAC7C,iBAAS;AAAA,MACb;AACA,UAAI,KAAK,IAAI,KAAK,IAAI,CAAC;AACvB;AAAA,IACJ;AACA;AAAA,EACJ;AACA,QAAM,KAAK,KAAK,MAAM,MAAM,CAAC;AAC7B,SAAO,EAAE,MAAM,MAAM,KAAK,EAAE,GAAG,QAAQ,YAAA;AAC3C;AASO,SAAS,kBAAkB,MAAc,OAAyB;AACrE,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,QAAQ,GAAG;AACzD,UAAM,QAAQ,mBAAmB,KAAK,CAAC,CAAC,IAAI,MAAM,SAAS,MAAM,MAAM,SAAS,MAAM,CAAC;AACvF,WAAO,UAAU,SAAY,QAAQ,MAAM,CAAC;AAAA,EAChD;AACA,SAAO;AACX;AAGO,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtB,YAAY,QAAgB;AACxB,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAiB;AACb,WAAO,OAAO,KAAK,MAAM;AAAA,EAC7B;AACJ;AAQA,SAAS,YAAY,MAAc,OAAyB;AACxD,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,cAAc,GAAG;AAC/D,WAAO,IAAI,aAAa,MAAM,MAAM,eAAe,MAAM,CAAC;AAAA,EAC9D;AACA,SAAO;AACX;AAOO,SAAS,WAAW,MAAoE;AAC3F,MAAI,CAAC,qBAAqB,KAAK,IAAI,GAAG;AAClC,WAAO,EAAE,OAAO,KAAK,MAAM,IAAI,GAAc,OAAO,MAAA;AAAA,EACxD;AACA,QAAM,OAAO,eAAe,MAAM,EAAE,aAAa,OAAO,eAAe,MAAM;AAC7E,MAAI,KAAK,YAAY,WAAW,GAAG;AAC/B,WAAO,EAAE,OAAO,KAAK,MAAM,IAAI,GAAc,OAAO,MAAA;AAAA,EACxD;AACA,SAAO,EAAE,OAAO,KAAK,MAAM,KAAK,MAAM,WAAW,GAAc,OAAO,KAAA;AAC1E;AASO,SAAS,UAAU,OAAgB,aAAiD;AACvF,MAAI,iBAAiB,cAAc;AAC/B,kBAAc,MAAM,MAAM;AAC1B,WAAO,OAAO,MAAM,MAAM;AAAA,EAC9B;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,WAAO,MAAM,IAAI,CAAC,SAAS,UAAU,MAAM,WAAW,CAAC;AAAA,EAC3D;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC7C,UAAM,MAA+B,CAAA;AACrC,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC7C,UAAI,GAAG,IAAI,UAAU,MAAM,WAAW;AAAA,IAC1C;AACA,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAKO,MAAM,sBAAsB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAarC,YAAY,SAAiB,MAAc,QAAQ,OAAO;AACtD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACjB;AACJ;AA8DA,MAAM,UAAU,CAAC,MAAuB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM;AAOlF,MAAM,aAAa,CAAC,MAAuB,QAAQ,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,MAAM;AAEpG,MAAM,qBAAqB;AAO3B,MAAM,oBAAoB;AAM1B,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBd,YAAY,QAA+B;AAnB3C,SAAQ,MAAM;AAGd,SAAA,MAAM;AAGN,SAAQ,cAAc;AAEtB,SAAQ,QAAyB;AAEjC,SAAQ,eAAe;AAGvB,SAAA,UAAU;AAON,SAAK,WAAW,OAAO,OAAO,aAAa,EAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAyB;AAC3B,QAAI,KAAK,UAAU,MAAM;AACrB,WAAK,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,YAAY,CAAC;AACjD,WAAK,eAAe;AAAA,IACxB;AACA,SAAK,eAAe,cAAc,KAAK,KAAK,GAAG,KAAK,IAAI,MAAM;AAC9D,eAAS;AACL,YAAM,OAAO,MAAM,KAAK,SAAS,KAAA;AACjC,UAAI,KAAK,SAAS,MAAM;AACpB,aAAK,MAAM;AACX,aAAK,MAAM;AACX,eAAO;AAAA,MACX;AACA,UAAI,KAAK,MAAM,SAAS,GAAG;AACvB,aAAK,MAAM,KAAK;AAChB,aAAK,MAAM;AACX,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAe;AACX,WAAO,KAAK,cAAc,cAAc,KAAK,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,MAAM,CAAC;AAAA,EAC5F;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAwB;AAC1B,eAAS;AACL,aAAO,KAAK,MAAM,KAAK,IAAI,UAAU,QAAQ,KAAK,IAAI,WAAW,KAAK,GAAG,CAAC,GAAG;AACzE,aAAK;AAAA,MACT;AACA,UAAI,KAAK,MAAM,KAAK,IAAI,QAAQ;AAC5B,eAAO,KAAK,IAAI,KAAK,GAAG;AAAA,MAC5B;AACA,UAAI,CAAE,MAAM,KAAK,QAAS;AACtB,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA,EAGA,eAAqB;AACjB,SAAK,QAAQ,CAAA;AACb,SAAK,eAAe,KAAK;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAqB;AACjB,UAAM,QAAQ,KAAK,SAAS,CAAA;AAC5B,UAAM,KAAK,KAAK,IAAI,MAAM,KAAK,cAAc,KAAK,GAAG,CAAC;AACtD,SAAK,QAAQ;AACb,WAAO,MAAM,WAAW,IAAI,MAAM,CAAC,IAAI,MAAM,KAAK,EAAE;AAAA,EACxD;AAAA;AAAA,EAGA,cAAoB;AAChB,SAAK,QAAQ;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAU,aAAa,GAAkB;AAC3C,QAAI,QAAQ;AACZ,SAAK,UAAU;AACf,QAAI,UAAU,GAAG;AACb,YAAM,IAAI,KAAK,IAAI,WAAW,KAAK,GAAG;AACtC,UAAI,MAAM,IAAI;AACV,aAAK;AACL,cAAM,KAAK,WAAA;AACX;AAAA,MACJ;AACA,UAAI,MAAM,OAAO,MAAM,IAAI;AACvB,cAAM,KAAK,WAAA;AACX;AAAA,MACJ;AAAA,IACJ;AACA,eAAS;AACL,UAAI,KAAK,OAAO,KAAK,IAAI,UAAU,CAAE,MAAM,KAAK,QAAS;AACrD,cAAM,IAAI,cAAc,oCAAoC,KAAK,MAAM;AAAA,MAC3E;AACA,YAAM,IAAI,KAAK,IAAI,WAAW,KAAK,GAAG;AACtC,WAAK;AACL,UAAI,MAAM,IAAI;AACV,cAAM,KAAK,WAAA;AAAA,MACf,WAAW,MAAM,OAAO,MAAM,IAAI;AAC9B;AACA,YAAI,QAAQ,KAAK,SAAS;AACtB,eAAK,UAAU;AAAA,QACnB;AAAA,MACJ,WAAW,MAAM,OAAO,MAAM,IAAI;AAC9B;AACA,YAAI,UAAU,GAAG;AACb;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA,EAGA,MAAc,aAA4B;AACtC,eAAS;AACL,yBAAmB,YAAY,KAAK;AACpC,YAAM,QAAQ,mBAAmB,KAAK,KAAK,GAAG;AAC9C,UAAI,UAAU,MAAM;AAChB,aAAK,MAAM,KAAK,IAAI;AACpB,YAAI,CAAE,MAAM,KAAK,QAAS;AACtB,gBAAM,IAAI,cAAc,qCAAqC,KAAK,MAAM;AAAA,QAC5E;AACA;AAAA,MACJ;AACA,WAAK,MAAM,MAAM,QAAQ;AACzB,UAAI,MAAM,CAAC,MAAM,KAAK;AAClB;AAAA,MACJ;AAEA,UAAI,KAAK,OAAO,KAAK,IAAI,UAAU,CAAE,MAAM,KAAK,QAAS;AACrD,cAAM,IAAI,cAAc,qCAAqC,KAAK,MAAM;AAAA,MAC5E;AACA,WAAK;AAAA,IACT;AAAA,EACJ;AAAA;AAAA,EAGA,MAAc,aAA4B;AACtC,eAAS;AACL,aAAO,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,WAAW,KAAK,IAAI,WAAW,KAAK,GAAG,CAAC,GAAG;AAC7E,aAAK;AAAA,MACT;AACA,UAAI,KAAK,MAAM,KAAK,IAAI,UAAU,CAAE,MAAM,KAAK,QAAS;AACpD;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAA8B;AAChC,SAAK,aAAA;AACL,SAAK;AACL,UAAM,KAAK,WAAA;AACX,UAAM,UAAU,KAAK,WAAA;AACrB,QAAI;AACA,aAAO,KAAK,MAAM,OAAO;AAAA,IAC7B,SAAS,KAAK;AACV,YAAM,IAAI,cAAc,mBAAmB,UAAU,GAAG,CAAC,IAAI,KAAK,MAAM;AAAA,IAC5E;AAAA,EACJ;AACJ;AASA,SAAS,cAAc,MAAc,MAAc,IAAoB;AACnE,MAAI,QAAQ;AACZ,MAAI,IAAI,KAAK,QAAQ,MAAM,IAAI;AAC/B,SAAO,KAAK,KAAK,IAAI,IAAI;AACrB;AACA,QAAI,KAAK,QAAQ,MAAM,IAAI,CAAC;AAAA,EAChC;AACA,SAAO;AACX;AAOA,SAAS,UAAU,KAAsB;AACrC,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC1D;AAQA,SAAS,QAAQ,MAAc,MAAoE;AAC/F,MAAI;AACA,WAAO,WAAW,IAAI;AAAA,EAC1B,SAAS,KAAK;AACV,UAAM,IAAI,cAAc,iBAAiB,UAAU,GAAG,CAAC,IAAI,IAAI;AAAA,EACnE;AACJ;AAWA,gBAAuB,YAAY,QAA6E;AAC5G,QAAM,MAAM,IAAI,YAAY,MAAM;AAClC,QAAM,QAAQ,MAAM,IAAI,KAAA;AACxB,MAAI,UAAU,IAAI;AACd,UAAM,IAAI,cAAc,sBAAsB,GAAG,IAAI;AAAA,EACzD;AACA,MAAI,UAAU,KAAK;AACf,UAAM,OAAO,IAAI,KAAA;AACjB,QAAI,aAAA;AACJ,UAAM,IAAI,UAAA;AACV,UAAM,EAAE,MAAA,IAAU,QAAQ,IAAI,WAAA,GAAc,IAAI;AAChD,QAAK,MAAM,IAAI,KAAA,MAAY,IAAI;AAC3B,YAAM,IAAI,cAAc,sCAAsC,IAAI,MAAM;AAAA,IAC5E;AACA,UAAM,EAAE,MAAM,QAAQ,OAAO,KAAA;AAC7B;AAAA,EACJ;AACA,MAAI;AACJ,MAAI,QAAQ;AACZ,MAAK,MAAM,IAAI,KAAA,MAAY,KAAK;AAC5B,QAAI;AAAA,EACR,OAAO;AACH,eAAS;AACL,aAAO,OAAO,KAAK,KAAK;AACxB;AACA,YAAM,OAAO,MAAM,IAAI,KAAA;AACvB,UAAI,SAAS,KAAK;AACd,YAAI;AACJ;AAAA,MACJ;AACA,UAAI,SAAS,KAAK;AACd,YAAI;AACJ;AAAA,MACJ;AACA,YAAM,IAAI;AAAA,QACN,SAAS,KAAK,iDAAiD,+BAA+B,IAAI;AAAA,QAClG,IAAI,KAAA;AAAA,MAAK;AAAA,IAEjB;AAAA,EACJ;AACA,MAAK,MAAM,IAAI,KAAA,MAAY,IAAI;AAC3B,UAAM,IAAI,cAAc,6CAA6C,IAAI,MAAM;AAAA,EACnF;AACJ;AASA,gBAAgB,OAAO,KAAkB,OAA6D;AAClG,QAAM,IAAI,MAAM,IAAI,KAAA;AACpB,QAAM,OAAO,IAAI,KAAA;AACjB,MAAI,MAAM,MAAM,MAAM,OAAO,MAAM,KAAK;AACpC,UAAM,IAAI,cAAc,MAAM,KAAK,uCAAuC,eAAe,CAAC,KAAK,IAAI;AAAA,EACvG;AACA,MAAI,MAAM,KAAK;AACX,QAAI,aAAA;AACJ,UAAM,IAAI,UAAA;AACV,QAAI,IAAI,UAAU,mBAAmB;AACjC,UAAI,YAAA;AACJ,YAAM,EAAE,MAAM,QAAQ,QAAQ,MAAM,OAAO,OAAO,IAAI,SAAS,KAAA;AAC/D;AAAA,IACJ;AACA,UAAM,SAAS,QAAQ,IAAI,WAAA,GAAc,IAAI;AAC7C,UAAM,EAAE,MAAM,UAAU,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAA;AACzE;AAAA,EACJ;AACA,MAAI,aAAA;AACJ,MAAI;AACJ,QAAM,IAAI,MAAM,IAAI,KAAA;AACpB,MAAI,MAAM,KAAK;AAEX,UAAM,IAAI,UAAU,CAAC;AACrB,QAAI,IAAI,UAAU,mBAAmB;AACjC,UAAI,YAAA;AACJ,YAAM,EAAE,MAAM,QAAQ,QAAQ,MAAM,OAAO,OAAO,IAAI,SAAS,KAAA;AAC/D;AAAA,IACJ;AACA,UAAM,SAAS,QAAQ,IAAI,WAAA,GAAc,IAAI;AAC7C,UAAM,EAAE,MAAM,UAAU,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAA;AACzE;AAAA,EACJ;AAEA,QAAM,QAAQ,IAAI,WAAA;AAClB,QAAM,UAAU,IAAI,KAAA;AACpB,QAAM,SAAS,MAAM,IAAI,WAAA;AACzB,MAAK,MAAM,IAAI,KAAA,MAAY,KAAK;AAC5B,UAAM,IAAI,cAAc,+BAA+B,MAAM,KAAK,IAAI,MAAM;AAAA,EAChF;AACA,MAAI;AACJ,QAAM,IAAI,MAAM,IAAI,KAAA;AACpB,MAAI,MAAM,KAAK;AAEX,QAAI,aAAA;AACJ,UAAM,IAAI,UAAU,CAAC;AACrB,QAAI,IAAI,UAAU,mBAAmB;AACjC,UAAI,YAAA;AACJ,YAAM,EAAE,MAAM,QAAQ,QAAQ,OAAO,OAAO,IAAI,SAAS,KAAA;AACzD;AAAA,IACJ;AACA,UAAM,OAAO,IAAI,WAAA;AACjB,UAAM,OAAO,GAAG,KAAK,GAAG,KAAK,UAAU,MAAM,CAAC,IAAI,IAAI;AACtD,UAAM,SAAS,QAAQ,MAAM,OAAO;AACpC,UAAM,EAAE,MAAM,UAAU,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAA;AACzE;AAAA,EACJ;AACA,MAAI;AACJ,QAAM,EAAE,MAAM,SAAS,QAAQ,OAAO,KAAA;AACtC,MAAK,MAAM,IAAI,KAAA,MAAY,KAAK;AAC5B,QAAI;AAAA,EACR,OAAO;AACH,eAAS;AACL,YAAM,IAAI,MAAM,IAAI,KAAA;AACpB,UAAI,MAAM,IAAI;AACV,cAAM,IAAI,cAAc,iCAAiC,MAAM,WAAW,IAAI,MAAM;AAAA,MACxF;AACA,YAAM,cAAc,IAAI,KAAA;AACxB,UAAI,aAAA;AACJ,YAAM,IAAI,UAAA;AACV,UAAI,IAAI,UAAU,mBAAmB;AACjC,YAAI,YAAA;AACJ,cAAM,EAAE,MAAM,QAAQ,QAAQ,OAAO,OAAO,IAAI,SAAS,MAAM,YAAA;AAAA,MACnE,OAAO;AACH,cAAM,OAAO,IAAI,WAAA;AACjB,cAAM,SAAS,QAAQ,MAAM,WAAW;AACxC,cAAM;AAAA,UACF,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,OAAO,OAAO;AAAA,UACd,OAAO,OAAO;AAAA,UACd;AAAA,UACA,MAAM;AAAA,QAAA;AAAA,MAEd;AACA,YAAM,OAAO,MAAM,IAAI,KAAA;AACvB,UAAI,SAAS,KAAK;AACd,YAAI;AACJ;AAAA,MACJ;AACA,UAAI,SAAS,KAAK;AACd,YAAI;AACJ;AAAA,MACJ;AACA,YAAM,IAAI;AAAA,QACN,SAAS,KACH,iCAAiC,MAAM,YACvC,+BAA+B,MAAM,mBAAmB,IAAI;AAAA,QAClE,IAAI,KAAA;AAAA,MAAK;AAAA,IAEjB;AAAA,EACJ;AACA,QAAM,MAAM,MAAM,IAAI,KAAA;AACtB,MAAI,QAAQ,KAAK;AACb,QAAI;AACJ;AAAA,EACJ;AACA,MAAI,QAAQ,KAAK;AACb,UAAM,IAAI;AAAA,MACN,QAAQ,KAAK,sCAAsC,2BAA2B,MAAM;AAAA,MACpF,IAAI,KAAA;AAAA,IAAK;AAAA,EAEjB;AAEA,QAAM,OAAiB,CAAA;AACvB,QAAM,YAAY,IAAI,KAAA;AACtB,SAAQ,MAAM,IAAI,KAAA,MAAY,KAAK;AAC/B,QAAI;AACJ,QAAK,MAAM,IAAI,KAAA,MAAY,KAAK;AAC5B,YAAM,IAAI,cAAc,kBAAkB,IAAI,MAAM;AAAA,IACxD;AACA,SAAK,KAAK,MAAM,IAAI,WAAA,CAAY;AAChC,QAAK,MAAM,IAAI,KAAA,MAAY,KAAK;AAC5B,YAAM,IAAI,cAAc,gBAAgB,IAAI,MAAM;AAAA,IACtD;AACA,QAAI;AACJ,UAAM,IAAI,KAAA;AACV,QAAI,aAAA;AACJ,UAAM,IAAI,UAAA;AACV,YAAQ,IAAI,WAAA,GAAc,IAAI,MAAM;AAAA,EACxC;AACA,MAAK,MAAM,IAAI,KAAA,MAAY,KAAK;AAC5B,UAAM,IAAI,cAAc,gBAAgB,IAAI,MAAM;AAAA,EACtD;AACA,MAAI;AACJ,QAAM,EAAE,MAAM,aAAa,QAAQ,OAAO,MAAM,MAAM,UAAA;AAC1D;AAOO,SAAS,cAAc,QAA6B,OAAqD;AAC5G,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA,GAAG,MAAM,WAAW,OAAO,6BAA6B,kBAAkB,MAAM,MAAM,GAAG,cAAc,MAAM,KAAK,+BAA+B,iBAAiB;AAAA,IAClK,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM,OAAA;AAAA,EAAO;AAElD;AAeA,MAAM,uBAAuB;AAUtB,SAAS,KAAK,KAAcA,kBAAiB,OAAa;AAC7D,MAAI,eAAe,cAAc;AAC7B,WAAO,EAAE,IAAI,IAAI,QAAQ,MAAM,YAAA;AAAA,EACnC;AACA,MAAI,OAAO,QAAQ,UAAU;AACzB,QAAI,OAAO,cAAc,GAAG,GAAG;AAC3B,aAAO,EAAE,IAAI,QAAQ,IAAI,IAAI,KAAK,MAAMA,kBAAiB,SAAS,KAAA;AAAA,IACtE;AACA,UAAM,YAAY,KAAK,OAAO,UAAU,GAAG,IAAI,6CAA6C,gBAAgB;AAAA,EAChH;AACA,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,qBAAqB,KAAK,GAAG,GAAG;AAC3E,UAAM,IAAI,OAAO,GAAG;AACpB,WAAO,OAAO,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,OAAA,IAAW,EAAE,IAAI,KAAK,MAAM,YAAA;AAAA,EAChF;AACA,QAAM,YAAY,KAAK,uBAAuB;AAClD;AAQA,SAAS,YAAY,KAAc,QAAkC;AACjE,QAAM,QAAQ,OAAO,QAAQ,YAAY,OAAO,QAAQ,WAAW,KAAK,UAAU,GAAG,IAAI,OAAO;AAChG,SAAO,IAAI,iBAAiB,gBAAgB,cAAc,QAAQ,OAAO,SAAS,KAAK,KAAK,MAAM,IAAI;AAAA,IAClG;AAAA,EAAA,CACH;AACL;AAUO,SAAS,eAAe,MAAc,KAAsB;AAC/D,QAAM,SAAS,KAAK,UAAU,GAAG;AACjC,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,MAAM,OAAO,MAAM,KAAK;AACxB;AAAA,IACJ,WAAW,MAAM,OAAO,MAAM,KAAK;AAC/B;AAAA,IACJ,WAAW,MAAM,KAAK;AAClB,YAAM,QAAQ;AACd,WAAK,KAAK,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK,KAAK;AAC/C,YAAI,KAAK,CAAC,MAAM,MAAM;AAClB;AAAA,QACJ;AAAA,MACJ;AAEA,UAAI,UAAU,KAAK,KAAK,WAAW,QAAQ,KAAK,KAAK,QAAQ,KAAK,KAAK,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC,GAAG;AAC1F,cAAM,QAAQ,+BAA+B,KAAK,KAAK,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;AAC3E,eAAO,UAAU,QAAQ,QAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,MAClD;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAKO,MAAM,kBAAkB;AAG/B,MAAM,WAAW;AAQV,SAAS,aAAa,QAAgB,aAAa,GAAe;AACrE,SAAO;AAAA,IACH,MAAM;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,OAAO,EAAE,YAAY,OAAO,OAAA;AAAA,IAC5B,QAAQ,EAAE,QAAQ,WAAW,YAAA;AAAA,EAAY;AAEjD;AAOO,SAAS,MAAM,QAA4B;AAC9C,SAAO;AAAA,IACH,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ,EAAE,QAAQ,IAAI,MAAM,WAAW,YAAA;AAAA,IACvC,OAAO,EAAE,WAAW,IAAA;AAAA,EAAI;AAEhC;AAQO,SAAS,MAAM,GAAmB;AACrC,SAAO,MAAM,IAAI,IAAI,CAAC;AAC1B;AAKA,MAAM,oBAAyC,oBAAI,IAAI,CAAC,YAAY,UAAU,oBAAoB,CAAC;AAQ5F,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCrB,YAAY,QAA6B;AA/BzC,SAAS,6BAAa,IAAA;AAGtB,SAAS,6BAAa,IAAA;AAGtB,SAAiB,+BAAe,IAAA;AAEhC,SAAQ,iBAAiB;AAEzB,SAAQ,gBAAgB;AAExB,SAAQ,eAAe;AAEvB,SAAQ,aAAa;AAGrB,SAAA,SAAkB;AAed,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAVA,IAAI,YAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,QAAgB,MAAoB;AACtC,SAAK,OAAO,IAAI,SAAS,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,CAAC;AAC1D,QAAI,KAAK,YAAY;AACjB,WAAK,OAAO;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,MAAM;AAAA,QACd,EAAE,MAAM,SAAS,OAAA;AAAA,QACjB,GAAG,iBAAiB;AAAA,MAAA;AAAA,IAE5B;AACA,QAAI,WAAW,YAAY;AACvB,WAAK;AACL,UAAI,KAAK,gBAAgB,GAAG;AACxB,aAAK,eAAe;AAAA,MACxB;AACA,UAAI,KAAK,iBAAiB,GAAG;AACzB,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,GAAG,KAAK,cAAc;AAAA,UACtB,EAAE,MAAM,SAAS,OAAA;AAAA,UACjB,GAAG,iBAAiB;AAAA,QAAA;AAAA,MAE5B;AACA;AAAA,IACJ;AACA,QAAI,WAAW,UAAU;AACrB,WAAK,aAAa;AAClB;AAAA,IACJ;AACA,QAAI,kBAAkB,IAAI,MAAM,GAAG;AAC/B;AAAA,IACJ;AACA,SAAK;AACL,QAAI,KAAK,cAAc;AACnB,WAAK,OAAO;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,MAAM;AAAA,QACd,EAAE,MAAM,SAAS,OAAA;AAAA,QACjB,GAAG,iBAAiB;AAAA,MAAA;AAAA,IAE5B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,QAAgB,OAAgB,MAAoB;AACxD,SAAK,OAAO,IAAI,SAAS,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,CAAC;AAC1D,QAAI,WAAW,YAAY;AACvB,UAAI,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS,YAAY,OAAO,MAAM,iBAAiB,UAAU;AAC7F,aAAK,SAAS,IAAI,MAAM,MAAM,MAAM,YAAY;AAAA,MACpD;AACA;AAAA,IACJ;AACA,QAAI,WAAW,UAAU;AACrB,WAAK,WAAW,OAAO,IAAI;AAAA,IAC/B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,OAAgB,MAAoB;AACnD,QAAI,KAAK,WAAW,QAAW;AAC3B,WAAK,OAAO;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,EAAE,MAAM,SAAS,SAAA;AAAA,QACjB,GAAG,iBAAiB;AAAA,MAAA;AAAA,IAE5B;AACA,SAAK,SAAS;AACd,QAAI,CAAC,SAAS,KAAK,GAAG;AAClB;AAAA,IACJ;AACA,UAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAC9D,QAAI,MAAM,YAAY,OAAO;AACzB,YAAM,UAAU,6CAA6C,MAAM,SAAS,IAAI,KAAK,KAAK,KAAK,EAAE;AACjG,WAAK,OAAO,MAAM,oBAAoB,oBAAoB,SAAS,EAAE,MAAM,SAAS,UAAU;AAC9F,YAAM,KAAK,OAAO,MAAM,SAAS,EAAE,MAAM,oBAAoB;AAAA,IACjE;AACA,QAAI,MAAM,YAAY,QAAQ,MAAM,SAAS,GAAG;AAC5C,WAAK,OAAO,QAAQ,oBAAoB,qBAAqB,mCAAmC,KAAK,IAAI;AAAA,QACrG;AAAA,QACA,SAAS;AAAA,MAAA,CACZ;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAA4B;AACxB,WAAO,SAAS,KAAK,MAAM,KAAK,OAAO,KAAK,OAAO,YAAY;AAAA,EACnE;AAAA;AAAA,EAGA,cAAoB;AAChB,eAAW,CAAC,MAAM,QAAQ,KAAK,KAAK,UAAU;AAC1C,YAAM,OAAO,KAAK,OAAO,IAAI,IAAI,KAAK;AACtC,UAAI,SAAS,UAAU;AACnB,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,qBAAqB,QAAQ,KAAK,IAAI,iBAAiB,IAAI;AAAA,UAC3D,EAAE,SAAS,KAAA;AAAA,QAAK;AAAA,MAExB;AAAA,IACJ;AAAA,EACJ;AACJ;AAOO,SAAS,SAAS,OAAkD;AACvE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,KAAK,EAAE,iBAAiB;AACtG;AAYO,SAAS,aACZ,MACA,QACA,MACA,QACY;AACZ,QAAM,QAAQ,CAAC,UACV,WAAW,SAAS,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,OAAO;AAC5E,MAAI,QAAQ;AACZ,MAAI,MAAM,KAAK,IAAI,GAAG;AAClB,YAAQ,EAAE,GAAG,MAAM,MAAM,iBAAiB,KAAK,MAAM,MAAM,KAAK,EAAA;AAChE,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,GAAG,MAAM,YAAY,KAAK,IAAI,iBAAiB,MAAM,IAAI;AAAA,MACzD,EAAE,SAAS,KAAK,KAAA;AAAA,IAAK;AAAA,EAE7B;AACA,SAAO,gBAAgB,MAAM,QAAQ,OAAO,MAAM,EAAE;AACxD;"}
@@ -1,5 +1,5 @@
1
1
  import { GraphFormatError } from "@graphty/graph-format";
2
- import { t as textChunks } from "./report-BOk0p5y8.js";
2
+ import { x as textChunks } from "./report-B1z4WT9e.js";
3
3
  const UNCLOSED_QUOTE_CODE = "E_CSV_UNCLOSED_QUOTE";
4
4
  const BAD_QUOTE_CODE = "E_CSV_QUOTE";
5
5
  const DELIMITER_CANDIDATES = Object.freeze([",", " ", ";", "|", " "]);
@@ -615,4 +615,4 @@ export {
615
615
  checkRecordSyntax as c,
616
616
  sniffNewline as s
617
617
  };
618
- //# sourceMappingURL=records-Bk9jgodz.js.map
618
+ //# sourceMappingURL=records-dbRkxwaq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"records-Bk9jgodz.js","sources":["../../src/formats/csv/records.ts"],"sourcesContent":["/**\n * The one streaming CSV record reader of the package (design sections 8.2 and 8.4: hand-written\n * tokenisers per format; CSV and Neo4j CSV are line-oriented over a byte stream): RFC 4180 records\n * with a configurable single-character delimiter and quote, a doubled quote inside a quoted field,\n * quoted fields spanning lines, LF / CRLF / lone-CR terminators, read from the common text reader\n * one chunk at a time as a character state machine. Only the open field is ever held, so a field\n * spanning many chunks costs its length once and a multi-gigabyte file is never buffered.\n *\n * `RecordReader` (used by the Neo4j importer) keeps one reusable cell array and one reusable\n * \"was quoted\" array: a quoted empty field (`\"\"`) is an empty string while an unquoted empty field\n * means \"not set\", and only the tokeniser can tell the two apart. `CsvRecordReader` (the CSV\n * importer) wraps it with delimiter sniffing over a bounded preview and yields a fresh cell array\n * per row. A malformed quoted field is fatal for both: an unterminated quote swallows the rest of\n * the file, and text after a closing quote makes every later cell boundary unreliable.\n */\n\nimport { GraphFormatError } from \"@graphty/graph-format\";\n\nimport { type ReadOptions, textChunks } from \"../../common/input.js\";\nimport { type ImportReportBuilder } from \"../../common/report.js\";\nimport { type ImportInput } from \"../../types.js\";\n\n/** Issue code: a quoted field is never closed; the import aborts (everything after it would be one cell). */\nexport const UNCLOSED_QUOTE_CODE = \"E_CSV_UNCLOSED_QUOTE\";\n\n/** Issue code: a closing quote is followed by text other than a delimiter or a line break; the import aborts. */\nexport const BAD_QUOTE_CODE = \"E_CSV_QUOTE\";\n\n/** The delimiters tried, in priority order, when none is given. */\nexport const DELIMITER_CANDIDATES: readonly string[] = Object.freeze([\",\", \"\\t\", \";\", \"|\", \" \"]);\n\n/** Rows the delimiter sniff looks at. */\nconst PREVIEW_ROWS = 10;\n\n/** Characters buffered for the sniff when the input has fewer than PREVIEW_ROWS line breaks. */\nconst PREVIEW_CHARS = 64 * 1024;\n\nconst LF = 10;\nconst CR = 13;\n\n/** Tokeniser state: at the start of a field. */\nconst START = 0;\n/** Tokeniser state: inside an unquoted field. */\nconst UNQUOTED = 1;\n/** Tokeniser state: inside a quoted field. */\nconst QUOTED = 2;\n/** Tokeniser state: just after a quote inside a quoted field (a second quote is a literal, anything else ends the field). */\nconst CLOSING = 3;\n/** Tokeniser state: after a closed quoted field, before the delimiter (text here is malformed). */\nconst AFTER_QUOTED = 4;\n/** Inside a leading comment line (skipped up to its line break). */\nconst COMMENT = 5;\n\ntype State = typeof START | typeof UNQUOTED | typeof QUOTED | typeof CLOSING | typeof AFTER_QUOTED | typeof COMMENT;\n\n/** The delimiter and quote of a reader. */\nexport interface RecordSyntax {\n /** The field delimiter, one character; null sniffs it from the first rows. */\n readonly delimiter: string | null;\n /** The quote character, one character. */\n readonly quote: string;\n /** The delimiters tried when `delimiter` is null; DELIMITER_CANDIDATES by default. */\n readonly candidates?: readonly string[] | undefined;\n /**\n * Characters that open a comment line while no record has been read yet (SNAP `#`, KONECT\n * `%`): such leading lines are skipped, kept in `leadingComments` and left out of the delimiter\n * sniff. A later line starting with one of them is an ordinary record. Default: none.\n */\n readonly comments?: readonly string[] | undefined;\n /**\n * Whether the delimiter sniff skips a candidate under which a closing quote is followed by\n * other text (see sniffDelimiter). False by default: that text then aborts the read.\n */\n readonly skipQuoteErrors?: boolean | undefined;\n}\n\n/**\n * Check a delimiter or quote option: exactly one character that is not a line break, and the two\n * must differ.\n * @param syntax - the delimiter (or null for sniffing) and quote\n * @returns the syntax unchanged; E_UNSUPPORTED when invalid\n */\nexport function checkRecordSyntax(syntax: RecordSyntax): RecordSyntax {\n for (const [option, value] of [\n [\"delimiter\", syntax.delimiter],\n [\"quote\", syntax.quote],\n ] as const) {\n if (value === null) {\n continue;\n }\n if (value.length !== 1 || value === \"\\n\" || value === \"\\r\") {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option ${option}: ${JSON.stringify(value)} is not a single non-line-break character`,\n { option, found: value },\n );\n }\n }\n if (syntax.delimiter !== null && syntax.delimiter === syntax.quote) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"options delimiter and quote must differ\", {\n option: \"delimiter\",\n found: syntax.delimiter,\n });\n }\n return syntax;\n}\n\n/**\n * Sniff the line terminator of a text: a lone `\\r` only when the text has `\\r` and no `\\n` at all\n * (classic Mac files); `\\n` otherwise.\n * @param text - the preview text\n * @returns \"\\n\" or \"\\r\"\n */\nexport function sniffNewline(text: string): \"\\n\" | \"\\r\" {\n return !text.includes(\"\\n\") && text.includes(\"\\r\") ? \"\\r\" : \"\\n\";\n}\n\n/**\n * Split a text into records synchronously (the first `maxRows` of them), honouring quotes; for\n * the delimiter sniff and the registry's head sniff, where the input is a bounded preview.\n * @param text - the text\n * @param delimiter - the delimiter\n * @param quote - the quote character\n * @param maxRows - the most rows to return\n * @param strictQuotes - return null when a closing quote is followed by text other than the\n * delimiter or a line break (the import would abort under this delimiter)\n * @returns the rows as cell arrays (blank lines skipped), or null (see strictQuotes)\n */\nfunction splitRecords(\n text: string,\n delimiter: string,\n quote: string,\n maxRows: number,\n strictQuotes = false,\n): string[][] | null {\n const rows: string[][] = [];\n const delimiterCode = delimiter.charCodeAt(0);\n const quoteCode = quote.charCodeAt(0);\n let cells: string[] = [];\n let state: State = START;\n let segment = 0;\n let field = \"\";\n const n = text.length;\n for (let i = 0; i < n && rows.length < maxRows; i++) {\n const c = text.charCodeAt(i);\n if (state === QUOTED) {\n if (c === quoteCode) {\n field += text.slice(segment, i);\n state = CLOSING;\n }\n continue;\n }\n if (state === CLOSING) {\n if (c === quoteCode) {\n field += quote;\n segment = i + 1;\n state = QUOTED;\n continue;\n }\n if (strictQuotes && c !== delimiterCode && c !== LF && c !== CR) {\n return null;\n }\n state = AFTER_QUOTED;\n segment = i;\n }\n if (c === delimiterCode || c === LF || c === CR) {\n if (state === UNQUOTED || state === AFTER_QUOTED) {\n field += text.slice(segment, i);\n }\n if (c === delimiterCode) {\n cells.push(field);\n field = \"\";\n state = START;\n } else {\n if (state !== START || cells.length > 0) {\n cells.push(field);\n rows.push(cells);\n cells = [];\n field = \"\";\n }\n state = START;\n if (c === CR && text.charCodeAt(i + 1) === LF) {\n i++;\n }\n }\n segment = i + 1;\n continue;\n }\n if (state === START) {\n if (c === quoteCode) {\n state = QUOTED;\n segment = i + 1;\n } else {\n state = UNQUOTED;\n segment = i;\n }\n }\n }\n if (rows.length < maxRows && (state !== START || cells.length > 0)) {\n if (state === UNQUOTED || state === AFTER_QUOTED || state === QUOTED) {\n field += text.slice(segment, n);\n }\n cells.push(field);\n rows.push(cells);\n }\n return rows;\n}\n\n/**\n * The preview text without its leading comment lines (those starting with one of the comment\n * characters), so the sniff sees the records only.\n * @param text - the preview text\n * @param comments - the comment characters\n * @returns the text from the first non-comment line on\n */\nfunction stripLeadingComments(text: string, comments: readonly string[]): string {\n if (comments.length === 0) {\n return text;\n }\n let at = 0;\n while (at < text.length && comments.includes(text[at])) {\n const lf = text.indexOf(\"\\n\", at);\n const cr = text.indexOf(\"\\r\", at);\n const end = Math.min(lf < 0 ? Infinity : lf, cr < 0 ? Infinity : cr);\n if (end === Infinity) {\n return \"\";\n }\n at = end + 1;\n if (text[at - 1] === \"\\r\" && text[at] === \"\\n\") {\n at++;\n }\n }\n return at === 0 ? text : text.slice(at);\n}\n\n/**\n * Sniff the delimiter of a text: every candidate is tried over the first rows and the one whose\n * field count is most consistent across rows wins (ties broken by the higher field count); a\n * candidate that yields fewer than two fields per row on average is never chosen. `null` when no\n * candidate qualifies (a single-column file).\n * @param text - the preview text\n * @param newline - the sniffed line terminator (unused by the splitter, which reads every kind;\n * kept for callers that sniffed it)\n * @param candidates - the delimiters to try, in priority order\n * @param quote - the quote character\n * @param skipQuoteErrors - also never choose a candidate under which a closing quote is followed\n * by other text. Only for inputs whose field counts say nothing (an adjacency table's rows vary in\n * width): elsewhere a stray character after a quote would make a worse candidate win silently\n * instead of aborting the read\n * @returns the delimiter, or null\n */\nexport function sniffDelimiter(\n text: string,\n newline: \"\\n\" | \"\\r\" = \"\\n\",\n candidates: readonly string[] = DELIMITER_CANDIDATES,\n quote = '\"',\n skipQuoteErrors = false,\n): string | null {\n let best: string | null = null;\n let bestDelta = Infinity;\n let bestAverage = 0;\n const terminated = text.endsWith(\"\\n\") || text.endsWith(\"\\r\");\n for (const delimiter of candidates) {\n if (delimiter === quote || delimiter === newline) {\n continue;\n }\n const split = splitRecords(text, delimiter, quote, PREVIEW_ROWS, skipQuoteErrors);\n if (split === null) {\n // a quoted cell this delimiter does not close: it is not the file's delimiter\n continue;\n }\n let rows = split.filter((row) => !isBlankRow(row));\n if (rows.length > 1 && !terminated) {\n // the preview is a prefix of the file: its last row may be cut short\n rows = rows.slice(0, -1);\n }\n if (rows.length === 0) {\n continue;\n }\n let total = 0;\n let delta = 0;\n for (let i = 0; i < rows.length; i++) {\n const count = rows[i].length;\n total += count;\n if (i > 0) {\n delta += Math.abs(count - rows[i - 1].length);\n }\n }\n const average = total / rows.length;\n if (average > 1.99 && (delta < bestDelta || (delta === bestDelta && average > bestAverage))) {\n best = delimiter;\n bestDelta = delta;\n bestAverage = average;\n }\n }\n return best;\n}\n\n/**\n * Whether a parsed row is a blank line: one cell holding only whitespace.\n * @param row - the row\n * @returns true for a blank line\n */\nfunction isBlankRow(row: readonly string[]): boolean {\n return row.length === 1 && row[0].trim().length === 0;\n}\n\n/**\n * Reads the records of one input. Iterate with `for await (const count of reader)`: each\n * iteration fills `reader.cells[0..count)` and `reader.quoted[0..count)` and sets `reader.line`\n * to the 1-based line the record started on. Blank lines (an empty unquoted record of one field)\n * are skipped. The arrays are reused between records. When the syntax gives no delimiter, the\n * first rows (PREVIEW_ROWS, or PREVIEW_CHARS characters) are buffered and the delimiter sniffed\n * from them before the first record is yielded.\n */\nexport class RecordReader implements AsyncIterable<number> {\n /** The cells of the record most recently yielded; only the first `count` entries are valid. */\n readonly cells: string[] = [];\n\n /** Whether each cell of the record most recently yielded was quoted. */\n readonly quoted: boolean[] = [];\n\n /** The leading comment lines (without their line breaks), in order; empty without `syntax.comments`. */\n readonly leadingComments: string[] = [];\n\n private readonly input: ImportInput;\n\n private readonly report: ImportReportBuilder;\n\n private readonly readOptions: ReadOptions;\n\n /** The delimiter (null while unsniffed), quote and comment characters. */\n readonly syntax: RecordSyntax;\n\n private delimiterText: string | null;\n\n private recordLine = 0;\n\n /**\n * Create a reader; nothing is read until iteration starts.\n * @param input - the input (or an async iterable of already decoded text chunks)\n * @param report - the report decode and quoting errors are recorded in\n * @param syntax - the delimiter (null to sniff) and quote (already checked)\n * @param readOptions - cancellation and progress\n */\n constructor(input: ImportInput, report: ImportReportBuilder, syntax: RecordSyntax, readOptions: ReadOptions) {\n this.input = input;\n this.report = report;\n this.readOptions = readOptions;\n this.syntax = syntax;\n this.delimiterText = syntax.delimiter;\n }\n\n /**\n * The 1-based line the most recently yielded record started on.\n * @returns the line number; 0 before the first record\n */\n get line(): number {\n return this.recordLine;\n }\n\n /**\n * The delimiter in use.\n * @returns the given or sniffed delimiter; null before the sniff ran\n */\n get delimiter(): string | null {\n return this.delimiterText;\n }\n\n /**\n * Iterate the records.\n * @yields the number of cells of each record\n * @returns nothing\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<number, void, undefined> {\n const scanner = new RecordScanner(this);\n for await (const chunk of this.chunks()) {\n // the first chunk arrives after the sniff (or with the given delimiter)\n scanner.setDelimiter((this.delimiterText ?? \",\").charCodeAt(0));\n let i = 0;\n while (i < chunk.length) {\n i = scanner.scan(chunk, i);\n if (scanner.ready) {\n scanner.ready = false;\n this.recordLine = scanner.recordLine;\n yield scanner.count;\n scanner.count = 0;\n }\n }\n scanner.endChunk(chunk);\n }\n if (scanner.finish()) {\n this.recordLine = scanner.recordLine;\n yield scanner.count;\n }\n }\n\n /**\n * Record a text-after-quote error (fatal).\n * @param line - the line the field started on\n * @returns never\n */\n badQuote(line: number): never {\n return this.report.fail(BAD_QUOTE_CODE, `line ${line}: text after the closing quote of a quoted field`, {\n line,\n });\n }\n\n /**\n * Record an unclosed-quote error (fatal).\n * @param line - the line the field started on\n * @returns never\n */\n unclosedQuote(line: number): never {\n return this.report.fail(UNCLOSED_QUOTE_CODE, `a quoted field starting on line ${line} is never closed`, {\n line,\n });\n }\n\n /**\n * The decoded text chunks of the input; when the delimiter is to be sniffed, the first rows\n * are buffered (bounded by PREVIEW_ROWS line breaks or PREVIEW_CHARS characters), the sniff\n * runs, and the buffered text is yielded as one chunk before the rest streams through.\n * @yields the text chunks\n * @returns nothing\n */\n private async *chunks(): AsyncGenerator<string, void, undefined> {\n const source = textChunks(this.input, this.report, this.readOptions);\n if (this.delimiterText !== null) {\n yield* source;\n return;\n }\n const pieces: string[] = [];\n let length = 0;\n let breaks = 0;\n let lastWasCr = false;\n let sniffed = false;\n for await (const chunk of source) {\n if (sniffed) {\n yield chunk;\n continue;\n }\n pieces.push(chunk);\n length += chunk.length;\n for (let i = 0; i < chunk.length && breaks < PREVIEW_ROWS; i++) {\n const c = chunk.charCodeAt(i);\n if (c === LF) {\n if (!lastWasCr) {\n breaks++;\n }\n } else if (c === CR) {\n breaks++;\n }\n lastWasCr = c === CR;\n }\n if (breaks < PREVIEW_ROWS && length < PREVIEW_CHARS) {\n continue;\n }\n const text = pieces.join(\"\");\n pieces.length = 0;\n this.sniff(text);\n sniffed = true;\n yield text;\n }\n if (!sniffed) {\n const text = pieces.join(\"\");\n if (text.length > 0) {\n this.sniff(text);\n yield text;\n }\n }\n }\n\n /**\n * Sniff the delimiter from the preview text and record it.\n * @param text - the preview\n */\n private sniff(text: string): void {\n const candidates = this.syntax.candidates ?? DELIMITER_CANDIDATES;\n // the sniff is a heuristic over the first rows: a single chunk holding a huge quoted cell\n // is capped so the candidate scans stay bounded\n const body = stripLeadingComments(text.slice(0, PREVIEW_CHARS), this.syntax.comments ?? []);\n this.delimiterText =\n sniffDelimiter(body, sniffNewline(body), candidates, this.syntax.quote, this.syntax.skipQuoteErrors) ??\n candidates[0];\n }\n}\n\n/**\n * The record state machine of RecordReader, resumable at every record end so the reader can\n * yield a record before the next one overwrites the shared cell arrays. Every state lives on the\n * instance; `scan()` runs the per-character loop over one chunk from a position and returns where\n * it stopped (after the character that completed a record, with `ready` set, or the chunk end).\n */\nclass RecordScanner {\n /** Whether a record just completed. */\n ready = false;\n\n /** The cell count of the record in progress (the completed one while `ready`). */\n count = 0;\n\n /** The line the completed record started on (valid while `ready`, and after finish()). */\n recordLine = 1;\n\n /** The line the record in progress started on. */\n private startLine = 1;\n\n private readonly reader: RecordReader;\n\n private readonly quoteCode: number;\n\n private readonly quoteText: string;\n\n private readonly commentCodes: readonly number[];\n\n private delimiterCode = -1;\n\n private state: State = START;\n\n private field = \"\";\n\n private fieldQuoted = false;\n\n private line = 1;\n\n private lastWasCr = false;\n\n /** Whether no record has started yet (comment lines are recognised until one does). */\n private leading: boolean;\n\n private comment = \"\";\n\n /** The start of the unconsumed run of the current chunk that belongs to the field or comment. */\n private segment = 0;\n\n /**\n * Create a scanner over a reader's cell arrays.\n * @param reader - the reader\n */\n constructor(reader: RecordReader) {\n this.reader = reader;\n this.quoteCode = reader.syntax.quote.charCodeAt(0);\n this.quoteText = reader.syntax.quote;\n this.commentCodes = (reader.syntax.comments ?? []).map((c) => c.charCodeAt(0));\n this.leading = this.commentCodes.length > 0;\n }\n\n /**\n * Set the delimiter once it is known (before the first chunk is scanned).\n * @param code - the delimiter's character code\n */\n setDelimiter(code: number): void {\n if (this.delimiterCode < 0) {\n this.delimiterCode = code;\n }\n }\n\n /**\n * Scan one chunk from a position until a record completes or the chunk ends.\n * @param chunk - the chunk\n * @param from - where to start\n * @returns the position after the last character consumed\n */\n scan(chunk: string, from: number): number {\n const { quoteCode, delimiterCode } = this;\n const n = chunk.length;\n let i = from;\n this.segment = from;\n while (i < n) {\n const c = chunk.charCodeAt(i);\n if (this.state === COMMENT) {\n if (c === LF || c === CR) {\n this.comment += chunk.slice(this.segment, i);\n this.reader.leadingComments.push(this.comment);\n this.comment = \"\";\n this.state = START;\n this.endLine(c);\n this.segment = i + 1;\n }\n i++;\n continue;\n }\n if (this.state === QUOTED) {\n // jump to the next quote; the run in between is field text whose line breaks are counted\n const q = chunk.indexOf(this.quoteText, i);\n const end = q < 0 ? n : q;\n if (end > i) {\n this.countLineBreaks(chunk, i, end);\n }\n if (q < 0) {\n i = n;\n break;\n }\n this.field += chunk.slice(this.segment, q);\n this.state = CLOSING;\n this.lastWasCr = false;\n i = q + 1;\n continue;\n }\n if (this.state === CLOSING) {\n if (c === quoteCode) {\n this.field += this.quoteText;\n this.segment = i + 1;\n this.state = QUOTED;\n this.lastWasCr = false;\n i++;\n continue;\n }\n if (c !== delimiterCode && c !== LF && c !== CR) {\n this.reader.badQuote(this.startLine);\n }\n this.state = AFTER_QUOTED;\n this.segment = i;\n // fall through to the delimiter / line-end handling below without consuming c\n }\n if (c === delimiterCode || c === LF || c === CR) {\n if (c === LF && this.lastWasCr) {\n // the second half of a CRLF already ended the record\n this.lastWasCr = false;\n this.segment = i + 1;\n i++;\n continue;\n }\n if (this.state === UNQUOTED) {\n this.field += chunk.slice(this.segment, i);\n }\n if (c === delimiterCode) {\n this.push();\n this.state = START;\n this.lastWasCr = false;\n this.segment = i + 1;\n i++;\n continue;\n }\n const blank = this.state === START && this.count === 0;\n if (!blank) {\n this.push();\n this.ready = true;\n this.recordLine = this.startLine;\n }\n this.state = START;\n this.endLine(c);\n this.segment = i + 1;\n i++;\n if (this.ready) {\n return i;\n }\n continue;\n }\n this.lastWasCr = false;\n if (this.state === START) {\n if (this.leading && this.count === 0 && this.commentCodes.includes(c)) {\n this.state = COMMENT;\n this.segment = i;\n i++;\n continue;\n }\n this.leading = false;\n if (c === quoteCode) {\n this.state = QUOTED;\n this.fieldQuoted = true;\n this.segment = i + 1;\n } else {\n this.state = UNQUOTED;\n this.segment = i;\n }\n }\n i++;\n }\n return i;\n }\n\n /**\n * Keep the tail of a chunk that belongs to an open field or comment.\n * @param chunk - the chunk just scanned to its end\n */\n endChunk(chunk: string): void {\n const n = chunk.length;\n if (this.segment >= n) {\n return;\n }\n if (this.state === COMMENT) {\n this.comment += chunk.slice(this.segment, n);\n } else if (this.state === QUOTED || this.state === UNQUOTED) {\n this.field += chunk.slice(this.segment, n);\n }\n this.segment = n;\n }\n\n /**\n * The end of the input: an open comment is kept, an open quote is fatal, an open record\n * (no trailing line break) is completed.\n * @returns true when a last record is ready\n */\n finish(): boolean {\n const { state } = this;\n if (state === COMMENT) {\n this.reader.leadingComments.push(this.comment);\n return false;\n }\n if (state === QUOTED) {\n this.reader.unclosedQuote(this.startLine);\n }\n if (state !== START || this.count > 0) {\n this.push();\n this.recordLine = this.startLine;\n return true;\n }\n return false;\n }\n\n /**\n * Count the line breaks of a run of quoted text (a CRLF counts once, like the record loop).\n * @param chunk - the chunk\n * @param from - the first index of the run\n * @param to - the index after the run\n */\n private countLineBreaks(chunk: string, from: number, to: number): void {\n let { line, lastWasCr } = this;\n for (let i = from; i < to; i++) {\n const c = chunk.charCodeAt(i);\n if (c === LF) {\n if (!lastWasCr) {\n line++;\n }\n lastWasCr = false;\n } else if (c === CR) {\n line++;\n lastWasCr = true;\n } else {\n lastWasCr = false;\n }\n }\n this.line = line;\n this.lastWasCr = lastWasCr;\n }\n\n /** Store the field in progress as the next cell. */\n private push(): void {\n const { reader, count } = this;\n reader.cells[count] = this.field;\n reader.quoted[count] = this.fieldQuoted;\n this.count = count + 1;\n this.field = \"\";\n this.fieldQuoted = false;\n }\n\n /**\n * Count a line break that ends a record or a comment line.\n * @param c - the LF or CR\n */\n private endLine(c: number): void {\n if (!(c === LF && this.lastWasCr)) {\n this.line++;\n }\n this.startLine = this.line;\n this.lastWasCr = c === CR;\n }\n}\n\n/** What the CSV importer's reader needs besides the input. */\nexport interface CsvReaderOptions extends ReadOptions {\n /** The delimiter; null or undefined sniffs it from the preview. */\n readonly delimiter?: string | null | undefined;\n /** The characters opening a leading comment line (RecordSyntax.comments); none by default. */\n readonly comments?: readonly string[] | undefined;\n /** RecordSyntax.skipQuoteErrors; false by default. */\n readonly skipQuoteErrors?: boolean | undefined;\n}\n\n/**\n * Records of a CSV input, one string array per row, streamed: iterate with\n * `for await (const row of reader)` and read `reader.line` for the 1-based line the row starts\n * on. The delimiter is known after the first row (`reader.delimiter`). Rows come out as fresh\n * arrays of cell texts exactly as written (no trimming, no typing); `reader.quoted` tells, for\n * the row just yielded, which cells were quoted (a quoted empty cell is the empty string, an\n * unquoted one is \"not set\"). Blank lines and whitespace-only single-cell lines are skipped.\n */\nexport class CsvRecordReader implements AsyncIterable<string[]> {\n private readonly inner: RecordReader;\n\n /**\n * Create a reader; nothing is read until iteration starts.\n * @param input - the input\n * @param report - the report parse errors are recorded in\n * @param options - the delimiter, cancellation and progress\n */\n constructor(input: ImportInput, report: ImportReportBuilder, options: CsvReaderOptions = {}) {\n this.inner = new RecordReader(\n input,\n report,\n {\n delimiter: options.delimiter ?? null,\n quote: '\"',\n comments: options.comments,\n skipQuoteErrors: options.skipQuoteErrors,\n },\n { signal: options.signal, onProgress: options.onProgress, encoding: options.encoding },\n );\n }\n\n /**\n * The leading comment lines the reader skipped (SNAP `#` lines, the KONECT `%` header), in order.\n * @returns the lines without their line breaks\n */\n get leadingComments(): readonly string[] {\n return this.inner.leadingComments;\n }\n\n /**\n * The 1-based line the most recently yielded row starts on.\n * @returns the line; 0 before the first row\n */\n get line(): number {\n return this.inner.line;\n }\n\n /**\n * The delimiter in use.\n * @returns the given or sniffed delimiter; null before the sniff ran\n */\n get delimiter(): string | null {\n return this.inner.delimiter;\n }\n\n /**\n * Whether each cell of the row most recently yielded was quoted.\n * @returns the flags (valid for the first `row.length` entries)\n */\n get quoted(): readonly boolean[] {\n return this.inner.quoted;\n }\n\n /**\n * Iterate the rows.\n * @yields one row at a time as its cell texts\n * @returns nothing\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<string[], void, undefined> {\n const { inner } = this;\n for await (const count of inner) {\n if (count === 1 && !inner.quoted[0] && inner.cells[0].trim().length === 0) {\n continue;\n }\n yield inner.cells.slice(0, count);\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"records-dbRkxwaq.js","sources":["../../src/formats/csv/records.ts"],"sourcesContent":["/**\n * The one streaming CSV record reader of the package (design sections 8.2 and 8.4: hand-written\n * tokenisers per format; CSV and Neo4j CSV are line-oriented over a byte stream): RFC 4180 records\n * with a configurable single-character delimiter and quote, a doubled quote inside a quoted field,\n * quoted fields spanning lines, LF / CRLF / lone-CR terminators, read from the common text reader\n * one chunk at a time as a character state machine. Only the open field is ever held, so a field\n * spanning many chunks costs its length once and a multi-gigabyte file is never buffered.\n *\n * `RecordReader` (used by the Neo4j importer) keeps one reusable cell array and one reusable\n * \"was quoted\" array: a quoted empty field (`\"\"`) is an empty string while an unquoted empty field\n * means \"not set\", and only the tokeniser can tell the two apart. `CsvRecordReader` (the CSV\n * importer) wraps it with delimiter sniffing over a bounded preview and yields a fresh cell array\n * per row. A malformed quoted field is fatal for both: an unterminated quote swallows the rest of\n * the file, and text after a closing quote makes every later cell boundary unreliable.\n */\n\nimport { GraphFormatError } from \"@graphty/graph-format\";\n\nimport { type ReadOptions, textChunks } from \"../../common/input.js\";\nimport { type ImportReportBuilder } from \"../../common/report.js\";\nimport { type ImportInput } from \"../../types.js\";\n\n/** Issue code: a quoted field is never closed; the import aborts (everything after it would be one cell). */\nexport const UNCLOSED_QUOTE_CODE = \"E_CSV_UNCLOSED_QUOTE\";\n\n/** Issue code: a closing quote is followed by text other than a delimiter or a line break; the import aborts. */\nexport const BAD_QUOTE_CODE = \"E_CSV_QUOTE\";\n\n/** The delimiters tried, in priority order, when none is given. */\nexport const DELIMITER_CANDIDATES: readonly string[] = Object.freeze([\",\", \"\\t\", \";\", \"|\", \" \"]);\n\n/** Rows the delimiter sniff looks at. */\nconst PREVIEW_ROWS = 10;\n\n/** Characters buffered for the sniff when the input has fewer than PREVIEW_ROWS line breaks. */\nconst PREVIEW_CHARS = 64 * 1024;\n\nconst LF = 10;\nconst CR = 13;\n\n/** Tokeniser state: at the start of a field. */\nconst START = 0;\n/** Tokeniser state: inside an unquoted field. */\nconst UNQUOTED = 1;\n/** Tokeniser state: inside a quoted field. */\nconst QUOTED = 2;\n/** Tokeniser state: just after a quote inside a quoted field (a second quote is a literal, anything else ends the field). */\nconst CLOSING = 3;\n/** Tokeniser state: after a closed quoted field, before the delimiter (text here is malformed). */\nconst AFTER_QUOTED = 4;\n/** Inside a leading comment line (skipped up to its line break). */\nconst COMMENT = 5;\n\ntype State = typeof START | typeof UNQUOTED | typeof QUOTED | typeof CLOSING | typeof AFTER_QUOTED | typeof COMMENT;\n\n/** The delimiter and quote of a reader. */\nexport interface RecordSyntax {\n /** The field delimiter, one character; null sniffs it from the first rows. */\n readonly delimiter: string | null;\n /** The quote character, one character. */\n readonly quote: string;\n /** The delimiters tried when `delimiter` is null; DELIMITER_CANDIDATES by default. */\n readonly candidates?: readonly string[] | undefined;\n /**\n * Characters that open a comment line while no record has been read yet (SNAP `#`, KONECT\n * `%`): such leading lines are skipped, kept in `leadingComments` and left out of the delimiter\n * sniff. A later line starting with one of them is an ordinary record. Default: none.\n */\n readonly comments?: readonly string[] | undefined;\n /**\n * Whether the delimiter sniff skips a candidate under which a closing quote is followed by\n * other text (see sniffDelimiter). False by default: that text then aborts the read.\n */\n readonly skipQuoteErrors?: boolean | undefined;\n}\n\n/**\n * Check a delimiter or quote option: exactly one character that is not a line break, and the two\n * must differ.\n * @param syntax - the delimiter (or null for sniffing) and quote\n * @returns the syntax unchanged; E_UNSUPPORTED when invalid\n */\nexport function checkRecordSyntax(syntax: RecordSyntax): RecordSyntax {\n for (const [option, value] of [\n [\"delimiter\", syntax.delimiter],\n [\"quote\", syntax.quote],\n ] as const) {\n if (value === null) {\n continue;\n }\n if (value.length !== 1 || value === \"\\n\" || value === \"\\r\") {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option ${option}: ${JSON.stringify(value)} is not a single non-line-break character`,\n { option, found: value },\n );\n }\n }\n if (syntax.delimiter !== null && syntax.delimiter === syntax.quote) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"options delimiter and quote must differ\", {\n option: \"delimiter\",\n found: syntax.delimiter,\n });\n }\n return syntax;\n}\n\n/**\n * Sniff the line terminator of a text: a lone `\\r` only when the text has `\\r` and no `\\n` at all\n * (classic Mac files); `\\n` otherwise.\n * @param text - the preview text\n * @returns \"\\n\" or \"\\r\"\n */\nexport function sniffNewline(text: string): \"\\n\" | \"\\r\" {\n return !text.includes(\"\\n\") && text.includes(\"\\r\") ? \"\\r\" : \"\\n\";\n}\n\n/**\n * Split a text into records synchronously (the first `maxRows` of them), honouring quotes; for\n * the delimiter sniff and the registry's head sniff, where the input is a bounded preview.\n * @param text - the text\n * @param delimiter - the delimiter\n * @param quote - the quote character\n * @param maxRows - the most rows to return\n * @param strictQuotes - return null when a closing quote is followed by text other than the\n * delimiter or a line break (the import would abort under this delimiter)\n * @returns the rows as cell arrays (blank lines skipped), or null (see strictQuotes)\n */\nfunction splitRecords(\n text: string,\n delimiter: string,\n quote: string,\n maxRows: number,\n strictQuotes = false,\n): string[][] | null {\n const rows: string[][] = [];\n const delimiterCode = delimiter.charCodeAt(0);\n const quoteCode = quote.charCodeAt(0);\n let cells: string[] = [];\n let state: State = START;\n let segment = 0;\n let field = \"\";\n const n = text.length;\n for (let i = 0; i < n && rows.length < maxRows; i++) {\n const c = text.charCodeAt(i);\n if (state === QUOTED) {\n if (c === quoteCode) {\n field += text.slice(segment, i);\n state = CLOSING;\n }\n continue;\n }\n if (state === CLOSING) {\n if (c === quoteCode) {\n field += quote;\n segment = i + 1;\n state = QUOTED;\n continue;\n }\n if (strictQuotes && c !== delimiterCode && c !== LF && c !== CR) {\n return null;\n }\n state = AFTER_QUOTED;\n segment = i;\n }\n if (c === delimiterCode || c === LF || c === CR) {\n if (state === UNQUOTED || state === AFTER_QUOTED) {\n field += text.slice(segment, i);\n }\n if (c === delimiterCode) {\n cells.push(field);\n field = \"\";\n state = START;\n } else {\n if (state !== START || cells.length > 0) {\n cells.push(field);\n rows.push(cells);\n cells = [];\n field = \"\";\n }\n state = START;\n if (c === CR && text.charCodeAt(i + 1) === LF) {\n i++;\n }\n }\n segment = i + 1;\n continue;\n }\n if (state === START) {\n if (c === quoteCode) {\n state = QUOTED;\n segment = i + 1;\n } else {\n state = UNQUOTED;\n segment = i;\n }\n }\n }\n if (rows.length < maxRows && (state !== START || cells.length > 0)) {\n if (state === UNQUOTED || state === AFTER_QUOTED || state === QUOTED) {\n field += text.slice(segment, n);\n }\n cells.push(field);\n rows.push(cells);\n }\n return rows;\n}\n\n/**\n * The preview text without its leading comment lines (those starting with one of the comment\n * characters), so the sniff sees the records only.\n * @param text - the preview text\n * @param comments - the comment characters\n * @returns the text from the first non-comment line on\n */\nfunction stripLeadingComments(text: string, comments: readonly string[]): string {\n if (comments.length === 0) {\n return text;\n }\n let at = 0;\n while (at < text.length && comments.includes(text[at])) {\n const lf = text.indexOf(\"\\n\", at);\n const cr = text.indexOf(\"\\r\", at);\n const end = Math.min(lf < 0 ? Infinity : lf, cr < 0 ? Infinity : cr);\n if (end === Infinity) {\n return \"\";\n }\n at = end + 1;\n if (text[at - 1] === \"\\r\" && text[at] === \"\\n\") {\n at++;\n }\n }\n return at === 0 ? text : text.slice(at);\n}\n\n/**\n * Sniff the delimiter of a text: every candidate is tried over the first rows and the one whose\n * field count is most consistent across rows wins (ties broken by the higher field count); a\n * candidate that yields fewer than two fields per row on average is never chosen. `null` when no\n * candidate qualifies (a single-column file).\n * @param text - the preview text\n * @param newline - the sniffed line terminator (unused by the splitter, which reads every kind;\n * kept for callers that sniffed it)\n * @param candidates - the delimiters to try, in priority order\n * @param quote - the quote character\n * @param skipQuoteErrors - also never choose a candidate under which a closing quote is followed\n * by other text. Only for inputs whose field counts say nothing (an adjacency table's rows vary in\n * width): elsewhere a stray character after a quote would make a worse candidate win silently\n * instead of aborting the read\n * @returns the delimiter, or null\n */\nexport function sniffDelimiter(\n text: string,\n newline: \"\\n\" | \"\\r\" = \"\\n\",\n candidates: readonly string[] = DELIMITER_CANDIDATES,\n quote = '\"',\n skipQuoteErrors = false,\n): string | null {\n let best: string | null = null;\n let bestDelta = Infinity;\n let bestAverage = 0;\n const terminated = text.endsWith(\"\\n\") || text.endsWith(\"\\r\");\n for (const delimiter of candidates) {\n if (delimiter === quote || delimiter === newline) {\n continue;\n }\n const split = splitRecords(text, delimiter, quote, PREVIEW_ROWS, skipQuoteErrors);\n if (split === null) {\n // a quoted cell this delimiter does not close: it is not the file's delimiter\n continue;\n }\n let rows = split.filter((row) => !isBlankRow(row));\n if (rows.length > 1 && !terminated) {\n // the preview is a prefix of the file: its last row may be cut short\n rows = rows.slice(0, -1);\n }\n if (rows.length === 0) {\n continue;\n }\n let total = 0;\n let delta = 0;\n for (let i = 0; i < rows.length; i++) {\n const count = rows[i].length;\n total += count;\n if (i > 0) {\n delta += Math.abs(count - rows[i - 1].length);\n }\n }\n const average = total / rows.length;\n if (average > 1.99 && (delta < bestDelta || (delta === bestDelta && average > bestAverage))) {\n best = delimiter;\n bestDelta = delta;\n bestAverage = average;\n }\n }\n return best;\n}\n\n/**\n * Whether a parsed row is a blank line: one cell holding only whitespace.\n * @param row - the row\n * @returns true for a blank line\n */\nfunction isBlankRow(row: readonly string[]): boolean {\n return row.length === 1 && row[0].trim().length === 0;\n}\n\n/**\n * Reads the records of one input. Iterate with `for await (const count of reader)`: each\n * iteration fills `reader.cells[0..count)` and `reader.quoted[0..count)` and sets `reader.line`\n * to the 1-based line the record started on. Blank lines (an empty unquoted record of one field)\n * are skipped. The arrays are reused between records. When the syntax gives no delimiter, the\n * first rows (PREVIEW_ROWS, or PREVIEW_CHARS characters) are buffered and the delimiter sniffed\n * from them before the first record is yielded.\n */\nexport class RecordReader implements AsyncIterable<number> {\n /** The cells of the record most recently yielded; only the first `count` entries are valid. */\n readonly cells: string[] = [];\n\n /** Whether each cell of the record most recently yielded was quoted. */\n readonly quoted: boolean[] = [];\n\n /** The leading comment lines (without their line breaks), in order; empty without `syntax.comments`. */\n readonly leadingComments: string[] = [];\n\n private readonly input: ImportInput;\n\n private readonly report: ImportReportBuilder;\n\n private readonly readOptions: ReadOptions;\n\n /** The delimiter (null while unsniffed), quote and comment characters. */\n readonly syntax: RecordSyntax;\n\n private delimiterText: string | null;\n\n private recordLine = 0;\n\n /**\n * Create a reader; nothing is read until iteration starts.\n * @param input - the input (or an async iterable of already decoded text chunks)\n * @param report - the report decode and quoting errors are recorded in\n * @param syntax - the delimiter (null to sniff) and quote (already checked)\n * @param readOptions - cancellation and progress\n */\n constructor(input: ImportInput, report: ImportReportBuilder, syntax: RecordSyntax, readOptions: ReadOptions) {\n this.input = input;\n this.report = report;\n this.readOptions = readOptions;\n this.syntax = syntax;\n this.delimiterText = syntax.delimiter;\n }\n\n /**\n * The 1-based line the most recently yielded record started on.\n * @returns the line number; 0 before the first record\n */\n get line(): number {\n return this.recordLine;\n }\n\n /**\n * The delimiter in use.\n * @returns the given or sniffed delimiter; null before the sniff ran\n */\n get delimiter(): string | null {\n return this.delimiterText;\n }\n\n /**\n * Iterate the records.\n * @yields the number of cells of each record\n * @returns nothing\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<number, void, undefined> {\n const scanner = new RecordScanner(this);\n for await (const chunk of this.chunks()) {\n // the first chunk arrives after the sniff (or with the given delimiter)\n scanner.setDelimiter((this.delimiterText ?? \",\").charCodeAt(0));\n let i = 0;\n while (i < chunk.length) {\n i = scanner.scan(chunk, i);\n if (scanner.ready) {\n scanner.ready = false;\n this.recordLine = scanner.recordLine;\n yield scanner.count;\n scanner.count = 0;\n }\n }\n scanner.endChunk(chunk);\n }\n if (scanner.finish()) {\n this.recordLine = scanner.recordLine;\n yield scanner.count;\n }\n }\n\n /**\n * Record a text-after-quote error (fatal).\n * @param line - the line the field started on\n * @returns never\n */\n badQuote(line: number): never {\n return this.report.fail(BAD_QUOTE_CODE, `line ${line}: text after the closing quote of a quoted field`, {\n line,\n });\n }\n\n /**\n * Record an unclosed-quote error (fatal).\n * @param line - the line the field started on\n * @returns never\n */\n unclosedQuote(line: number): never {\n return this.report.fail(UNCLOSED_QUOTE_CODE, `a quoted field starting on line ${line} is never closed`, {\n line,\n });\n }\n\n /**\n * The decoded text chunks of the input; when the delimiter is to be sniffed, the first rows\n * are buffered (bounded by PREVIEW_ROWS line breaks or PREVIEW_CHARS characters), the sniff\n * runs, and the buffered text is yielded as one chunk before the rest streams through.\n * @yields the text chunks\n * @returns nothing\n */\n private async *chunks(): AsyncGenerator<string, void, undefined> {\n const source = textChunks(this.input, this.report, this.readOptions);\n if (this.delimiterText !== null) {\n yield* source;\n return;\n }\n const pieces: string[] = [];\n let length = 0;\n let breaks = 0;\n let lastWasCr = false;\n let sniffed = false;\n for await (const chunk of source) {\n if (sniffed) {\n yield chunk;\n continue;\n }\n pieces.push(chunk);\n length += chunk.length;\n for (let i = 0; i < chunk.length && breaks < PREVIEW_ROWS; i++) {\n const c = chunk.charCodeAt(i);\n if (c === LF) {\n if (!lastWasCr) {\n breaks++;\n }\n } else if (c === CR) {\n breaks++;\n }\n lastWasCr = c === CR;\n }\n if (breaks < PREVIEW_ROWS && length < PREVIEW_CHARS) {\n continue;\n }\n const text = pieces.join(\"\");\n pieces.length = 0;\n this.sniff(text);\n sniffed = true;\n yield text;\n }\n if (!sniffed) {\n const text = pieces.join(\"\");\n if (text.length > 0) {\n this.sniff(text);\n yield text;\n }\n }\n }\n\n /**\n * Sniff the delimiter from the preview text and record it.\n * @param text - the preview\n */\n private sniff(text: string): void {\n const candidates = this.syntax.candidates ?? DELIMITER_CANDIDATES;\n // the sniff is a heuristic over the first rows: a single chunk holding a huge quoted cell\n // is capped so the candidate scans stay bounded\n const body = stripLeadingComments(text.slice(0, PREVIEW_CHARS), this.syntax.comments ?? []);\n this.delimiterText =\n sniffDelimiter(body, sniffNewline(body), candidates, this.syntax.quote, this.syntax.skipQuoteErrors) ??\n candidates[0];\n }\n}\n\n/**\n * The record state machine of RecordReader, resumable at every record end so the reader can\n * yield a record before the next one overwrites the shared cell arrays. Every state lives on the\n * instance; `scan()` runs the per-character loop over one chunk from a position and returns where\n * it stopped (after the character that completed a record, with `ready` set, or the chunk end).\n */\nclass RecordScanner {\n /** Whether a record just completed. */\n ready = false;\n\n /** The cell count of the record in progress (the completed one while `ready`). */\n count = 0;\n\n /** The line the completed record started on (valid while `ready`, and after finish()). */\n recordLine = 1;\n\n /** The line the record in progress started on. */\n private startLine = 1;\n\n private readonly reader: RecordReader;\n\n private readonly quoteCode: number;\n\n private readonly quoteText: string;\n\n private readonly commentCodes: readonly number[];\n\n private delimiterCode = -1;\n\n private state: State = START;\n\n private field = \"\";\n\n private fieldQuoted = false;\n\n private line = 1;\n\n private lastWasCr = false;\n\n /** Whether no record has started yet (comment lines are recognised until one does). */\n private leading: boolean;\n\n private comment = \"\";\n\n /** The start of the unconsumed run of the current chunk that belongs to the field or comment. */\n private segment = 0;\n\n /**\n * Create a scanner over a reader's cell arrays.\n * @param reader - the reader\n */\n constructor(reader: RecordReader) {\n this.reader = reader;\n this.quoteCode = reader.syntax.quote.charCodeAt(0);\n this.quoteText = reader.syntax.quote;\n this.commentCodes = (reader.syntax.comments ?? []).map((c) => c.charCodeAt(0));\n this.leading = this.commentCodes.length > 0;\n }\n\n /**\n * Set the delimiter once it is known (before the first chunk is scanned).\n * @param code - the delimiter's character code\n */\n setDelimiter(code: number): void {\n if (this.delimiterCode < 0) {\n this.delimiterCode = code;\n }\n }\n\n /**\n * Scan one chunk from a position until a record completes or the chunk ends.\n * @param chunk - the chunk\n * @param from - where to start\n * @returns the position after the last character consumed\n */\n scan(chunk: string, from: number): number {\n const { quoteCode, delimiterCode } = this;\n const n = chunk.length;\n let i = from;\n this.segment = from;\n while (i < n) {\n const c = chunk.charCodeAt(i);\n if (this.state === COMMENT) {\n if (c === LF || c === CR) {\n this.comment += chunk.slice(this.segment, i);\n this.reader.leadingComments.push(this.comment);\n this.comment = \"\";\n this.state = START;\n this.endLine(c);\n this.segment = i + 1;\n }\n i++;\n continue;\n }\n if (this.state === QUOTED) {\n // jump to the next quote; the run in between is field text whose line breaks are counted\n const q = chunk.indexOf(this.quoteText, i);\n const end = q < 0 ? n : q;\n if (end > i) {\n this.countLineBreaks(chunk, i, end);\n }\n if (q < 0) {\n i = n;\n break;\n }\n this.field += chunk.slice(this.segment, q);\n this.state = CLOSING;\n this.lastWasCr = false;\n i = q + 1;\n continue;\n }\n if (this.state === CLOSING) {\n if (c === quoteCode) {\n this.field += this.quoteText;\n this.segment = i + 1;\n this.state = QUOTED;\n this.lastWasCr = false;\n i++;\n continue;\n }\n if (c !== delimiterCode && c !== LF && c !== CR) {\n this.reader.badQuote(this.startLine);\n }\n this.state = AFTER_QUOTED;\n this.segment = i;\n // fall through to the delimiter / line-end handling below without consuming c\n }\n if (c === delimiterCode || c === LF || c === CR) {\n if (c === LF && this.lastWasCr) {\n // the second half of a CRLF already ended the record\n this.lastWasCr = false;\n this.segment = i + 1;\n i++;\n continue;\n }\n if (this.state === UNQUOTED) {\n this.field += chunk.slice(this.segment, i);\n }\n if (c === delimiterCode) {\n this.push();\n this.state = START;\n this.lastWasCr = false;\n this.segment = i + 1;\n i++;\n continue;\n }\n const blank = this.state === START && this.count === 0;\n if (!blank) {\n this.push();\n this.ready = true;\n this.recordLine = this.startLine;\n }\n this.state = START;\n this.endLine(c);\n this.segment = i + 1;\n i++;\n if (this.ready) {\n return i;\n }\n continue;\n }\n this.lastWasCr = false;\n if (this.state === START) {\n if (this.leading && this.count === 0 && this.commentCodes.includes(c)) {\n this.state = COMMENT;\n this.segment = i;\n i++;\n continue;\n }\n this.leading = false;\n if (c === quoteCode) {\n this.state = QUOTED;\n this.fieldQuoted = true;\n this.segment = i + 1;\n } else {\n this.state = UNQUOTED;\n this.segment = i;\n }\n }\n i++;\n }\n return i;\n }\n\n /**\n * Keep the tail of a chunk that belongs to an open field or comment.\n * @param chunk - the chunk just scanned to its end\n */\n endChunk(chunk: string): void {\n const n = chunk.length;\n if (this.segment >= n) {\n return;\n }\n if (this.state === COMMENT) {\n this.comment += chunk.slice(this.segment, n);\n } else if (this.state === QUOTED || this.state === UNQUOTED) {\n this.field += chunk.slice(this.segment, n);\n }\n this.segment = n;\n }\n\n /**\n * The end of the input: an open comment is kept, an open quote is fatal, an open record\n * (no trailing line break) is completed.\n * @returns true when a last record is ready\n */\n finish(): boolean {\n const { state } = this;\n if (state === COMMENT) {\n this.reader.leadingComments.push(this.comment);\n return false;\n }\n if (state === QUOTED) {\n this.reader.unclosedQuote(this.startLine);\n }\n if (state !== START || this.count > 0) {\n this.push();\n this.recordLine = this.startLine;\n return true;\n }\n return false;\n }\n\n /**\n * Count the line breaks of a run of quoted text (a CRLF counts once, like the record loop).\n * @param chunk - the chunk\n * @param from - the first index of the run\n * @param to - the index after the run\n */\n private countLineBreaks(chunk: string, from: number, to: number): void {\n let { line, lastWasCr } = this;\n for (let i = from; i < to; i++) {\n const c = chunk.charCodeAt(i);\n if (c === LF) {\n if (!lastWasCr) {\n line++;\n }\n lastWasCr = false;\n } else if (c === CR) {\n line++;\n lastWasCr = true;\n } else {\n lastWasCr = false;\n }\n }\n this.line = line;\n this.lastWasCr = lastWasCr;\n }\n\n /** Store the field in progress as the next cell. */\n private push(): void {\n const { reader, count } = this;\n reader.cells[count] = this.field;\n reader.quoted[count] = this.fieldQuoted;\n this.count = count + 1;\n this.field = \"\";\n this.fieldQuoted = false;\n }\n\n /**\n * Count a line break that ends a record or a comment line.\n * @param c - the LF or CR\n */\n private endLine(c: number): void {\n if (!(c === LF && this.lastWasCr)) {\n this.line++;\n }\n this.startLine = this.line;\n this.lastWasCr = c === CR;\n }\n}\n\n/** What the CSV importer's reader needs besides the input. */\nexport interface CsvReaderOptions extends ReadOptions {\n /** The delimiter; null or undefined sniffs it from the preview. */\n readonly delimiter?: string | null | undefined;\n /** The characters opening a leading comment line (RecordSyntax.comments); none by default. */\n readonly comments?: readonly string[] | undefined;\n /** RecordSyntax.skipQuoteErrors; false by default. */\n readonly skipQuoteErrors?: boolean | undefined;\n}\n\n/**\n * Records of a CSV input, one string array per row, streamed: iterate with\n * `for await (const row of reader)` and read `reader.line` for the 1-based line the row starts\n * on. The delimiter is known after the first row (`reader.delimiter`). Rows come out as fresh\n * arrays of cell texts exactly as written (no trimming, no typing); `reader.quoted` tells, for\n * the row just yielded, which cells were quoted (a quoted empty cell is the empty string, an\n * unquoted one is \"not set\"). Blank lines and whitespace-only single-cell lines are skipped.\n */\nexport class CsvRecordReader implements AsyncIterable<string[]> {\n private readonly inner: RecordReader;\n\n /**\n * Create a reader; nothing is read until iteration starts.\n * @param input - the input\n * @param report - the report parse errors are recorded in\n * @param options - the delimiter, cancellation and progress\n */\n constructor(input: ImportInput, report: ImportReportBuilder, options: CsvReaderOptions = {}) {\n this.inner = new RecordReader(\n input,\n report,\n {\n delimiter: options.delimiter ?? null,\n quote: '\"',\n comments: options.comments,\n skipQuoteErrors: options.skipQuoteErrors,\n },\n { signal: options.signal, onProgress: options.onProgress, encoding: options.encoding },\n );\n }\n\n /**\n * The leading comment lines the reader skipped (SNAP `#` lines, the KONECT `%` header), in order.\n * @returns the lines without their line breaks\n */\n get leadingComments(): readonly string[] {\n return this.inner.leadingComments;\n }\n\n /**\n * The 1-based line the most recently yielded row starts on.\n * @returns the line; 0 before the first row\n */\n get line(): number {\n return this.inner.line;\n }\n\n /**\n * The delimiter in use.\n * @returns the given or sniffed delimiter; null before the sniff ran\n */\n get delimiter(): string | null {\n return this.inner.delimiter;\n }\n\n /**\n * Whether each cell of the row most recently yielded was quoted.\n * @returns the flags (valid for the first `row.length` entries)\n */\n get quoted(): readonly boolean[] {\n return this.inner.quoted;\n }\n\n /**\n * Iterate the rows.\n * @yields one row at a time as its cell texts\n * @returns nothing\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<string[], void, undefined> {\n const { inner } = this;\n for await (const count of inner) {\n if (count === 1 && !inner.quoted[0] && inner.cells[0].trim().length === 0) {\n continue;\n }\n yield inner.cells.slice(0, count);\n }\n }\n}\n"],"names":[],"mappings":"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"}