@graphty/graph-io 0.3.18 → 0.3.19

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 (118) hide show
  1. package/README.md +37 -2
  2. package/dist/chunks/{escape-D-gZWO26.js → escape-CWExcecC.js} +3 -3
  3. package/dist/chunks/{escape-D-gZWO26.js.map → escape-CWExcecC.js.map} +1 -1
  4. package/dist/chunks/exporter-CbMZyVt-.js +735 -0
  5. package/dist/chunks/exporter-CbMZyVt-.js.map +1 -0
  6. package/dist/chunks/importer-BAP4PrxR.js +2194 -0
  7. package/dist/chunks/importer-BAP4PrxR.js.map +1 -0
  8. package/dist/chunks/{importer-Br_QeAeE.js → importer-BNFuV1-K.js} +5 -4
  9. package/dist/chunks/{importer-Br_QeAeE.js.map → importer-BNFuV1-K.js.map} +1 -1
  10. package/dist/chunks/{importer-d0uQxFp6.js → importer-BVGtU1NA.js} +5 -4
  11. package/dist/chunks/{importer-d0uQxFp6.js.map → importer-BVGtU1NA.js.map} +1 -1
  12. package/dist/chunks/{importer-DHagxvDD.js → importer-BW-Ft2ps.js} +5 -4
  13. package/dist/chunks/{importer-DHagxvDD.js.map → importer-BW-Ft2ps.js.map} +1 -1
  14. package/dist/chunks/{importer-Du5crN9l.js → importer-Bm1_5vPS.js} +275 -82
  15. package/dist/chunks/importer-Bm1_5vPS.js.map +1 -0
  16. package/dist/chunks/{importer-aNJfe0qu.js → importer-ByPGO-09.js} +2 -2
  17. package/dist/chunks/{importer-aNJfe0qu.js.map → importer-ByPGO-09.js.map} +1 -1
  18. package/dist/chunks/importer-DOepkgnG.js +1713 -0
  19. package/dist/chunks/importer-DOepkgnG.js.map +1 -0
  20. package/dist/chunks/json-elements-CZY1wiZh.js +779 -0
  21. package/dist/chunks/json-elements-CZY1wiZh.js.map +1 -0
  22. package/dist/chunks/{records-Bk9jgodz.js → records-BzNicMsf.js} +2 -2
  23. package/dist/chunks/{records-Bk9jgodz.js.map → records-BzNicMsf.js.map} +1 -1
  24. package/dist/chunks/{report-BOk0p5y8.js → report-BcWboivV.js} +95 -95
  25. package/dist/chunks/{report-BOk0p5y8.js.map → report-BcWboivV.js.map} +1 -1
  26. package/dist/chunks/weights-CwISIpCP.js +176 -0
  27. package/dist/chunks/weights-CwISIpCP.js.map +1 -0
  28. package/dist/chunks/{writer-GAdltGmC.js → writer-DQiKgQJc.js} +8 -179
  29. package/dist/chunks/writer-DQiKgQJc.js.map +1 -0
  30. package/dist/csv.js +5 -4
  31. package/dist/csv.js.map +1 -1
  32. package/dist/cx.d.ts +1 -0
  33. package/dist/cx.js +6 -0
  34. package/dist/cx.js.map +1 -0
  35. package/dist/cx2.d.ts +1 -0
  36. package/dist/cx2.js +10 -0
  37. package/dist/cx2.js.map +1 -0
  38. package/dist/dot.js +1 -1
  39. package/dist/gexf.js +15 -4
  40. package/dist/gexf.js.map +1 -1
  41. package/dist/gml.js +5 -4
  42. package/dist/gml.js.map +1 -1
  43. package/dist/graph-io.js +145 -389
  44. package/dist/graph-io.js.map +1 -1
  45. package/dist/graphml.js +1 -1
  46. package/dist/json.js +1 -1
  47. package/dist/neo4j.js +5 -4
  48. package/dist/neo4j.js.map +1 -1
  49. package/dist/obo.js +1 -1
  50. package/dist/pajek.js +1 -1
  51. package/dist/src/common/json-elements.d.ts +286 -0
  52. package/dist/src/common/json-elements.d.ts.map +1 -0
  53. package/dist/src/common/json-elements.js +926 -0
  54. package/dist/src/common/json-elements.js.map +1 -0
  55. package/dist/src/formats/cx/importer.d.ts +133 -0
  56. package/dist/src/formats/cx/importer.d.ts.map +1 -0
  57. package/dist/src/formats/cx/importer.js +2220 -0
  58. package/dist/src/formats/cx/importer.js.map +1 -0
  59. package/dist/src/formats/cx/index.d.ts +7 -0
  60. package/dist/src/formats/cx/index.d.ts.map +1 -0
  61. package/dist/src/formats/cx/index.js +7 -0
  62. package/dist/src/formats/cx/index.js.map +1 -0
  63. package/dist/src/formats/cx2/exporter.d.ts +59 -0
  64. package/dist/src/formats/cx2/exporter.d.ts.map +1 -0
  65. package/dist/src/formats/cx2/exporter.js +864 -0
  66. package/dist/src/formats/cx2/exporter.js.map +1 -0
  67. package/dist/src/formats/cx2/importer.d.ts +169 -0
  68. package/dist/src/formats/cx2/importer.d.ts.map +1 -0
  69. package/dist/src/formats/cx2/importer.js +1652 -0
  70. package/dist/src/formats/cx2/importer.js.map +1 -0
  71. package/dist/src/formats/cx2/index.d.ts +7 -0
  72. package/dist/src/formats/cx2/index.d.ts.map +1 -0
  73. package/dist/src/formats/cx2/index.js +7 -0
  74. package/dist/src/formats/cx2/index.js.map +1 -0
  75. package/dist/src/formats/gexf/exporter.d.ts +2 -0
  76. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  77. package/dist/src/formats/gexf/exporter.js +6 -0
  78. package/dist/src/formats/gexf/exporter.js.map +1 -1
  79. package/dist/src/formats/gexf/importer.d.ts +2 -4
  80. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  81. package/dist/src/formats/gexf/importer.js +2 -4
  82. package/dist/src/formats/gexf/importer.js.map +1 -1
  83. package/dist/src/formats/gexf/index.d.ts +1 -1
  84. package/dist/src/formats/gexf/index.js +2 -2
  85. package/dist/src/formats/gexf/index.js.map +1 -1
  86. package/dist/src/formats/gml/exporter.js +1 -1
  87. package/dist/src/formats/gml/exporter.js.map +1 -1
  88. package/dist/src/formats/json/importer.d.ts.map +1 -1
  89. package/dist/src/formats/json/importer.js +6 -104
  90. package/dist/src/formats/json/importer.js.map +1 -1
  91. package/dist/src/index.d.ts +2 -0
  92. package/dist/src/index.d.ts.map +1 -1
  93. package/dist/src/index.js +2 -0
  94. package/dist/src/index.js.map +1 -1
  95. package/dist/src/registry.d.ts.map +1 -1
  96. package/dist/src/registry.js +5 -0
  97. package/dist/src/registry.js.map +1 -1
  98. package/dist/src/sniff.d.ts +1 -1
  99. package/dist/src/sniff.d.ts.map +1 -1
  100. package/dist/src/sniff.js +2 -0
  101. package/dist/src/sniff.js.map +1 -1
  102. package/package.json +11 -1
  103. package/src/common/json-elements.ts +1147 -0
  104. package/src/formats/cx/importer.ts +2733 -0
  105. package/src/formats/cx/index.ts +7 -0
  106. package/src/formats/cx2/exporter.ts +1036 -0
  107. package/src/formats/cx2/importer.ts +2187 -0
  108. package/src/formats/cx2/index.ts +7 -0
  109. package/src/formats/gexf/exporter.ts +11 -0
  110. package/src/formats/gexf/importer.ts +2 -2
  111. package/src/formats/gexf/index.ts +1 -1
  112. package/src/formats/gml/exporter.ts +1 -1
  113. package/src/formats/json/importer.ts +6 -105
  114. package/src/index.ts +10 -0
  115. package/src/registry.ts +5 -0
  116. package/src/sniff.ts +14 -1
  117. package/dist/chunks/importer-Du5crN9l.js.map +0 -1
  118. package/dist/chunks/writer-GAdltGmC.js.map +0 -1
package/dist/csv.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"csv.js","sources":["../src/formats/csv/header.ts","../src/formats/csv/values.ts","../src/formats/csv/importer.ts","../src/formats/csv/exporter.ts","../src/formats/csv/index.ts"],"sourcesContent":["/**\n * Header handling of the CSV importer: whether the first row is a header, which columns hold the\n * endpoints, the weight, the Gephi per-row `Type`, the edge id and the label (by name lists and\n * by position), and the synthesised names of a headerless file (`column1`, `column2`, ...).\n *\n * Name resolution is exact first, then case-insensitive, over a candidate list in priority order,\n * so `source` / `Source` / `SOURCE`, `src`, `from` all resolve without configuration; the Gephi\n * dialect (exact `Source` and `Target` headers) additionally makes an exact `Type` column the\n * per-row direction. A caller may name any column explicitly or give its 0-based position.\n */\n\nimport { GraphFormatError } from \"@graphty/graph-format\";\n\nimport { inferTextDtype } from \"../../common/text.js\";\n\n/** A column named by header text or by 0-based position. */\nexport type CsvColumnRef = string | number;\n\n/** Header candidates of the source endpoint, in priority order (matched exactly, then case-insensitively). */\nexport const SOURCE_NAMES: readonly string[] = Object.freeze([\n \"source\",\n \"src\",\n \"from\",\n \"start\",\n \"source_id\",\n \"sourceid\",\n \"fromnodeid\",\n \"start_id\",\n \":start_id\",\n]);\n\n/** Header candidates of the target endpoint. */\nexport const TARGET_NAMES: readonly string[] = Object.freeze([\n \"target\",\n \"dst\",\n \"dest\",\n \"to\",\n \"end\",\n \"target_id\",\n \"targetid\",\n \"tonodeid\",\n \"end_id\",\n \":end_id\",\n]);\n\n/** Header candidates of a node table's id column. */\nexport const ID_NAMES: readonly string[] = Object.freeze([\"id\", \"node\", \"name\", \"key\"]);\n\n/** Header candidates of a label column (node or edge). */\nexport const LABEL_NAMES: readonly string[] = Object.freeze([\"label\"]);\n\n/** Header candidates of an edge id column. */\nexport const EDGE_ID_NAMES: readonly string[] = Object.freeze([\"id\"]);\n\n/** The Gephi per-row direction column. */\nexport const TYPE_NAME = \"Type\";\n\n/** Every name that marks a first row as a header when the caller leaves detection to the importer. */\nconst HEADER_MARKERS: ReadonlySet<string> = new Set([\n ...SOURCE_NAMES,\n ...TARGET_NAMES,\n ...ID_NAMES,\n ...LABEL_NAMES,\n \"weight\",\n \"type\",\n \"value\",\n]);\n\n/**\n * Find a column by name: candidates are tried in priority order, each first exactly and then\n * case-insensitively, so `Source` (a case variant of the first candidate) beats an exact `from`.\n * @param names - the header names\n * @param candidates - the names to look for, in priority order\n * @returns the column index, or -1\n */\nexport function findColumn(names: readonly string[], candidates: readonly string[]): number {\n for (const candidate of candidates) {\n const exact = names.indexOf(candidate);\n if (exact >= 0) {\n return exact;\n }\n const lower = candidate.toLowerCase();\n const loose = names.findIndex((name) => name.toLowerCase() === lower);\n if (loose >= 0) {\n return loose;\n }\n }\n return -1;\n}\n\n/**\n * Resolve an explicit column option: a 0-based position (checked against the width) or a header\n * name (exact, then case-insensitive). An option naming a column the file does not have is\n * E_UNSUPPORTED (details.option, details.found): the file cannot be read as requested.\n * @param names - the header names\n * @param ref - the option value\n * @param option - the option name, for the error\n * @returns the column index\n */\nexport function resolveColumnRef(names: readonly string[], ref: CsvColumnRef, option: string): number {\n if (typeof ref === \"number\") {\n if (!Number.isInteger(ref) || ref < 0 || ref >= names.length) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option ${option}: column ${ref} does not exist (the file has ${names.length} column(s))`,\n { option, found: ref, columns: names.length },\n );\n }\n return ref;\n }\n const index = findColumn(names, [ref]);\n if (index < 0) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${option}: no column named ${JSON.stringify(ref)}`, {\n option,\n found: ref,\n columns: [...names],\n });\n }\n return index;\n}\n\n/**\n * Decide whether the first row of a file is a header when the caller left it to the importer: it\n * is when any cell is a known column name (source, target, id, label, weight, type...), or when\n * every cell is non-numeric text while the second row has a numeric cell (`u,v` over `1,2`);\n * a first row with a numeric cell is data, and so is one that looks exactly like the rows below it.\n * @param first - the first row\n * @param second - the second row, or null when the file has one row\n * @returns true when the first row is a header\n */\nexport function looksLikeHeader(first: readonly string[], second: readonly string[] | null): boolean {\n let allText = true;\n for (const cell of first) {\n const text = cell.trim();\n if (HEADER_MARKERS.has(text.toLowerCase())) {\n return true;\n }\n if (text.length === 0 || inferTextDtype(text) !== \"string\") {\n allText = false;\n }\n }\n if (!allText || second === null) {\n return false;\n }\n return second.some((cell) => cell.trim().length > 0 && inferTextDtype(cell.trim()) !== \"string\");\n}\n\n/**\n * The synthesised header of a headerless file: `column1` ... `columnN`.\n * @param width - the number of cells of the first row\n * @returns the names\n */\nexport function positionalNames(width: number): string[] {\n const names: string[] = [];\n for (let i = 1; i <= width; i++) {\n names.push(`column${i}`);\n }\n return names;\n}\n\n/**\n * Header cells as column names: trimmed, a BOM-free first cell (the reader strips it), and an\n * empty cell named by its position so every column has a name.\n * @param cells - the header row\n * @returns the names\n */\nexport function headerNames(cells: readonly string[]): string[] {\n return cells.map((cell, i) => {\n const name = cell.trim();\n return name.length === 0 ? `column${i + 1}` : name;\n });\n}\n","/**\n * The untyped attribute columns of the CSV importer (design sections 5.1 and 5.4): every cell is\n * parsed by the fixed lexical grammar and pushed as a scalar, the sink's per-column inference\n * widens `bool -> i32 -> f64 -> string`, and a column whose sampled values are all text becomes a\n * `dict` when its cardinality stays below half the sampled rows (the 1024-row heuristic of\n * design section 5.4). The sample is the only buffering: at most DICT_SAMPLE_ROWS (row, text)\n * pairs per column, flushed on the decision.\n *\n * An unquoted empty cell is unset (never written); a quoted empty cell is the empty string. A\n * column whose every cell is unset is declared as a nullable string column at the end so the\n * header survives a round trip. Inferred columns go through the shared TextCellWriter, which keeps\n * the column's dtype the one the 5.1 grammar implies and the lexical form of numeric-looking cells\n * that a widening to string would otherwise rewrite.\n */\n\nimport { type ColumnHandle, GraphFormatError, type GraphSink, INVALID_INDEX } from \"@graphty/graph-format\";\n\nimport { DICT_SAMPLE_ROWS, DictHeuristic } from \"../../common/attributes.js\";\nimport { type ImportReportBuilder } from \"../../common/report.js\";\nimport { inferTextDtype, parseTextCell, TextCellWriter } from \"../../common/text.js\";\n\n/**\n * One inferred attribute column of a node or edge table, written cell by cell.\n */\nexport class InferredColumn {\n /** The column name in the sink. */\n readonly name: string;\n\n private readonly domain: \"node\" | \"edge\";\n\n private readonly sink: GraphSink;\n\n private readonly report: ImportReportBuilder;\n\n private handle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n /** Whether values are pushed as text: a dict column, or a caller's string / dict column of the name. */\n private text = false;\n\n /** Whether the column was declared as a dict by this writer. */\n private dict = false;\n\n /** Whether the dtype decision was made; until then values are sampled. */\n private decided = false;\n\n /** Whether every sampled value so far is text under the lexical grammar (a dict candidate). */\n private candidate = true;\n\n private readonly heuristic: DictHeuristic;\n\n /** The inferred-column writer, once the column was decided to be inferred (neither dict nor a caller's). */\n private writer: TextCellWriter | null = null;\n\n private readonly pendingRows: number[] = [];\n\n private readonly pendingTexts: string[] = [];\n\n /**\n * Create a column writer; nothing is declared until the first value or finish().\n * @param name - the column name\n * @param domain - node or edge\n * @param sink - the sink\n * @param report - the report a refused sampled value is recorded in (the flush has no line numbers)\n * @param sampleRows - rows sampled before the dict decision; DICT_SAMPLE_ROWS by default\n */\n constructor(\n name: string,\n domain: \"node\" | \"edge\",\n sink: GraphSink,\n report: ImportReportBuilder,\n sampleRows: number = DICT_SAMPLE_ROWS,\n ) {\n this.name = name;\n this.domain = domain;\n this.sink = sink;\n this.report = report;\n this.heuristic = new DictHeuristic(sampleRows);\n }\n\n /**\n * Whether the column was declared as a dict.\n * @returns true after a dict decision\n */\n get isDict(): boolean {\n return this.dict;\n }\n\n /**\n * Write one set cell.\n * @param row - the node or edge index\n * @param text - the cell text (the empty string for a quoted empty cell)\n */\n write(row: number, text: string): void {\n if (this.decided) {\n this.push(row, text);\n return;\n }\n this.pendingRows.push(row);\n this.pendingTexts.push(text);\n if (this.candidate && inferTextDtype(text) !== \"string\") {\n this.candidate = false;\n }\n const full = this.heuristic.observe(text);\n if (full || !this.candidate) {\n this.decide();\n }\n }\n\n /**\n * Decide an undecided column from what was sampled (or declare an all-empty column as string)\n * and flush the sample. Call once at the end of the input.\n */\n finish(): void {\n if (!this.decided) {\n this.decide();\n }\n if (this.handle === INVALID_INDEX) {\n this.handle = this.lookup();\n if (this.handle === INVALID_INDEX) {\n this.handle = this.declare(\"string\");\n }\n }\n }\n\n /**\n * Make the dtype decision: a dict when every sampled value is text and the cardinality is low\n * and no column of the name exists yet; inference otherwise. A column the sink already holds\n * (a caller's) receives the cell text when it is a string or dict column and the parsed value\n * otherwise. The sample is flushed either way; a sampled value the sink refuses (a caller's\n * typed column of the name) is recorded as an issue of this column and the flush continues.\n */\n private decide(): void {\n this.decided = true;\n const existing = this.lookup();\n if (existing !== INVALID_INDEX) {\n this.handle = existing;\n this.text = this.holdsText();\n } else if (this.candidate && this.heuristic.decide() === \"dict\") {\n this.dict = true;\n this.text = true;\n this.handle = this.declare(\"dict\");\n } else {\n this.writer = new TextCellWriter(this.name, this.domain, this.sink, this.report);\n }\n const rows = this.pendingRows;\n const texts = this.pendingTexts;\n for (let i = 0; i < rows.length; i++) {\n try {\n this.push(rows[i], texts[i]);\n } catch (err) {\n this.report.recordError(err, { element: this.name });\n }\n }\n rows.length = 0;\n texts.length = 0;\n }\n\n /**\n * Push one value: text into a dict or a caller's text column, the parsed scalar into a caller's\n * typed column, and everything else through the inferred-column writer.\n * @param row - the node or edge index\n * @param text - the cell text\n */\n private push(row: number, text: string): void {\n if (this.writer !== null) {\n this.writer.write(row, text);\n this.handle = this.writer.column;\n return;\n }\n const value: unknown = this.text ? text : parseTextCell(text);\n this.set(this.handle, row, value);\n }\n\n /**\n * Set a cell in the sink's table for this domain.\n * @param column - the handle or name\n * @param row - the row\n * @param value - the value\n */\n private set(column: ColumnHandle | string, row: number, value: unknown): void {\n if (this.domain === \"node\") {\n this.sink.setNodeValue(column, row, value);\n } else {\n this.sink.setEdgeValue(column, row, value);\n }\n }\n\n /**\n * The sink's handle for this column's name.\n * @returns the handle, or INVALID_INDEX when the sink has no such column yet\n */\n private lookup(): ColumnHandle {\n return this.domain === \"node\" ? this.sink.nodeColumn(this.name) : this.sink.edgeColumn(this.name);\n }\n\n /**\n * Whether the sink's existing column of this name is a string or dict column, probed through\n * the sink's declare (the same shape returns the existing handle, another is E_COLUMN_EXISTS).\n * @returns true when cell text is what the column holds\n */\n private holdsText(): boolean {\n for (const dtype of [\"string\", \"dict\"] as const) {\n try {\n this.declare(dtype);\n return true;\n } catch (err) {\n if (!(err instanceof GraphFormatError) || err.code !== \"E_COLUMN_EXISTS\") {\n throw err;\n }\n }\n }\n return false;\n }\n\n /**\n * Declare the column with a fixed dtype.\n * @param dtype - dict or string\n * @returns the handle\n */\n private declare(dtype: \"dict\" | \"string\"): ColumnHandle {\n const decl = { name: this.name, dtype, nullable: true, origin: { format: \"csv\" } };\n return this.domain === \"node\" ? this.sink.declareNodeColumn(decl) : this.sink.declareEdgeColumn(decl);\n }\n}\n","/**\n * The CSV / TSV importer (design sections 8.4 and 8.6; research note 07 section 2.6): a streaming\n * edge-list reader for the generic (`source,target[,weight,...]`), Gephi (`Source,Target,Type,Id,\n * Label,Weight,...`) and headerless (`u v [w]`) dialects, with an optional node table merged by id,\n * and, with `table: \"adjacency\"`, an adjacency table (`node,neighbour[:weight],...`).\n *\n * - The delimiter is sniffed from a preview unless given; LF, CRLF and lone-CR files all read.\n * - The first row is a header when it holds a known column name or when it is all text over a\n * numeric second row (`header: \"auto\"`); a headerless file is positional: source, target,\n * weight (when `weightFrom` is not null), then `column4`... as attributes.\n * - Endpoint and node ids are text cells coerced by ONE rule per import call (`ids`, default\n * \"canonical\": `\"1\"` becomes the number 1, `\"01\"` stays a string) over the node table and the\n * edge table alike, so they agree across the two files.\n * - Direction is per row in the Gephi dialect (`Type` = Directed / Undirected / Mutual, blank =\n * `defaultDirected`) and `defaultDirected` (true) otherwise; the first edge row sets the sink's\n * direction and later rows that differ go through `onMixedDirection` (expansion by default).\n * - The weight column (`weightFrom`, default \"weight\", matched case-insensitively) is parsed per\n * row; a blank cell means \"no weight given\" and the edge is pushed without one.\n * - Every other column is an attribute: cells are parsed by the fixed lexical grammar of design\n * section 5.1 and the sink infers the column dtype (widening per column, never per cell); an\n * all-text column of low cardinality becomes a dict (design section 5.4); an `id` column of the\n * edge table is the edge id (role id, unique); a `label` column is the label (role label).\n * - An adjacency table has no header (unless `header: true`, which skips the first row): each row\n * is a node followed by its neighbours, one edge per neighbour in row order. A neighbour cell\n * `id:weight` carries the edge's weight when the text after its LAST colon is a number; a cell\n * ending in a bare colon (`a:1:`) is the id before it with no weight; any other cell is the id\n * as written (`http://x`). A row holding only its node adds an isolated node. An empty adjacency\n * table is the empty graph. The delimiter sniff skips a candidate under which a closing quote is\n * followed by other text, since the field counts it otherwise relies on vary by row.\n * - A node table without an id column is refused unless `rowNumberIds` is set: then each data row's\n * 0-based number is its id, coerced by `ids` like any other id cell. The option applies to the\n * node table (the `nodes` input, or the input itself when nothing is paired with it), whose first\n * row it makes a header under `header: \"auto\"`; a paired edge table is read as without it.\n * - Per-row problems (wrong field count, blank endpoint, invalid weight, bad Type, refused id)\n * are recorded and the row skipped; the import aborts with ImportError once `errorLimit` is\n * exceeded, on a malformed or unterminated quoted field, on an empty input and on a header\n * without endpoint (or id) columns.\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, RENAMED_CODE, uniqueColumnName } from \"../../common/attributes.js\";\nimport {\n DUPLICATE_EDGE_ID_CODE as SHARED_DUPLICATE_EDGE_ID_CODE,\n DUPLICATE_NODE_CODE as SHARED_DUPLICATE_NODE_CODE,\n EMPTY_INPUT_CODE as SHARED_EMPTY_INPUT_CODE,\n ID_MERGED_CODE as SHARED_ID_MERGED_CODE,\n MISSING_ENDPOINT_CODE as SHARED_MISSING_ENDPOINT_CODE,\n MISSING_ID_CODE as SHARED_MISSING_ID_CODE,\n ROLE_TAKEN_CODE as SHARED_ROLE_TAKEN_CODE,\n} from \"../../common/codes.js\";\nimport { DirectionResolver, type EdgeKind } from \"../../common/direction.js\";\nimport { IdCoercer } from \"../../common/ids.js\";\nimport { throwIfAborted } from \"../../common/input.js\";\nimport {\n reportSinkOptions,\n reportUnusedOptions,\n type ResolvedImportOptions,\n resolveImportOptions,\n} from \"../../common/options.js\";\nimport { ImportReportBuilder } from \"../../common/report.js\";\nimport { parseWeightText } from \"../../common/weights.js\";\nimport { type CommonImportOptions, type GraphImporter, type ImportInput, type ImportReport } from \"../../types.js\";\nimport {\n type CsvColumnRef,\n EDGE_ID_NAMES,\n findColumn,\n headerNames,\n ID_NAMES,\n LABEL_NAMES,\n looksLikeHeader,\n positionalNames,\n resolveColumnRef,\n SOURCE_NAMES,\n TARGET_NAMES,\n TYPE_NAME,\n} from \"./header.js\";\nimport { type CsvReaderOptions, CsvRecordReader, sniffDelimiter, sniffNewline } from \"./records.js\";\nimport { InferredColumn } from \"./values.js\";\n\n/** The format-specific options of the CSV importer. */\nexport interface CsvImportOptions {\n /** The field delimiter; sniffed from the first rows when omitted (`,`, tab, `;`, `|`, space). */\n delimiter?: string | undefined;\n /** Whether the first row is a header; \"auto\" (default) decides from its content. */\n header?: boolean | \"auto\" | undefined;\n /**\n * What the input is: an edge table, a node table, an adjacency table (`node,neighbour[:weight],...`\n * per row, no header by default), or \"auto\" (default): an edge table when source and target\n * columns resolve, a node table when only an id column does. An adjacency table is never\n * guessed: nothing in its rows tells it from an edge list.\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\" | undefined;\n /** The source column, by name or 0-based position; resolved from the header by default. */\n sourceColumn?: CsvColumnRef | undefined;\n /** The target column, by name or 0-based position; resolved from the header by default. */\n targetColumn?: CsvColumnRef | undefined;\n /**\n * The per-row direction column (Directed / Undirected / Mutual); by default the exact `Type`\n * column of a Gephi table (exact `Source` and `Target` headers); null reads no such column.\n */\n typeColumn?: CsvColumnRef | null | undefined;\n /** The id column of a node table, by name or position; resolved from the header by default. */\n idColumn?: CsvColumnRef | undefined;\n /** A node table read before the edges: its ids become nodes and its other columns node attributes. */\n nodes?: ImportInput | undefined;\n /**\n * A node table whose header has no id column gets its ids from the row numbers (0 for the first\n * data row), coerced by `ids`, instead of failing with E_CSV_NO_ID_COLUMN. The node table is the\n * `nodes` input when one is given (the edge table is then read as without this option), else\n * the input itself; its first row is a header even under `header: \"auto\"`. False by default.\n */\n rowNumberIds?: boolean | undefined;\n}\n\n/** Issue code: the input holds no header row at all. */\nexport const EMPTY_INPUT_CODE = SHARED_EMPTY_INPUT_CODE;\n/** Issue code: the header names no source / target (or, for a node table, no id) column. */\nexport const NO_ENDPOINT_COLUMNS_CODE = \"E_CSV_NO_ENDPOINT_COLUMNS\";\n/** Issue code: a node table without an id column. */\nexport const NO_ID_COLUMN_CODE = \"E_CSV_NO_ID_COLUMN\";\n/** Issue code: a row with a different number of fields than the header. */\nexport const FIELD_COUNT_CODE = \"E_CSV_FIELD_COUNT\";\n/** Issue code: an edge row with a blank source or target cell. */\nexport const MISSING_ENDPOINT_CODE = SHARED_MISSING_ENDPOINT_CODE;\n/** Issue code: a node row with a blank id cell. */\nexport const MISSING_ID_CODE = SHARED_MISSING_ID_CODE;\n/** Issue code: a Type cell that is not Directed, Undirected or Mutual. */\nexport const BAD_TYPE_CODE = \"E_CSV_BAD_TYPE\";\n/** Issue code: the table has a header and no data rows. */\nexport const NO_DATA_ROWS_CODE = \"W_CSV_NO_DATA_ROWS\";\n/** Issue code: a node table row repeats an id; its attributes overwrite the earlier row's. */\nexport const DUPLICATE_NODE_CODE = SHARED_DUPLICATE_NODE_CODE;\n/** Issue code: two distinct id cells became one id under `ids: \"number\"` (design section 4.1). */\nexport const ID_MERGED_CODE = SHARED_ID_MERGED_CODE;\n/** Issue code: an explicitly named weight column the file does not have. */\nexport const COLUMN_MISSING_CODE = \"W_CSV_COLUMN_MISSING\";\n/** Issue code: a column whose role (id, label) is already held by another column of the sink. */\nexport const ROLE_TAKEN_CODE = SHARED_ROLE_TAKEN_CODE;\n/** Issue code: a repeated edge id (the column is unique); the edge is skipped. */\nexport const DUPLICATE_EDGE_ID_CODE = SHARED_DUPLICATE_EDGE_ID_CODE;\n\nconst TABLE_MODES: ReadonlySet<string> = new Set([\"edges\", \"nodes\", \"adjacency\", \"auto\"]);\n\n/** The common options an edge-table import reads (the rest is reported by reportUnusedOptions). */\nconst USED_OPTIONS: ReadonlySet<keyof CommonImportOptions> = new Set<keyof CommonImportOptions>([\n \"ids\",\n \"addMissingNodes\",\n \"duplicateEdges\",\n \"selfLoops\",\n \"onMixedDirection\",\n \"defaultDirected\",\n \"weightFrom\",\n \"weightDtype\",\n \"errorLimit\",\n \"signal\",\n \"onProgress\",\n]);\n\n/** The common options an import with a node table reads: nodeIdFrom applies to the node table. */\nconst USED_OPTIONS_WITH_NODES: ReadonlySet<keyof CommonImportOptions> = new Set<keyof CommonImportOptions>([\n ...USED_OPTIONS,\n \"nodeIdFrom\",\n]);\nconst BAD_DELIMITERS: ReadonlySet<string> = new Set(['\"', \"\\n\", \"\\r\"]);\n\n/** The CSV options with defaults applied. */\ninterface ResolvedCsvOptions {\n readonly delimiter: string | null;\n readonly header: boolean | \"auto\";\n readonly table: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\";\n readonly sourceColumn: CsvColumnRef | null;\n readonly targetColumn: CsvColumnRef | null;\n /** The direction column reference; null for none; undefined for the Gephi rule. */\n readonly typeColumn: CsvColumnRef | null | undefined;\n readonly idColumn: CsvColumnRef | null;\n readonly nodes: ImportInput | null;\n readonly rowNumberIds: boolean;\n}\n\n/** The columns of an edge table, by index. */\ninterface EdgePlan {\n readonly kind: \"edges\";\n readonly names: readonly string[];\n readonly width: number;\n readonly source: number;\n readonly target: number;\n readonly weight: number;\n readonly type: number;\n readonly id: number;\n readonly label: number;\n readonly attributes: readonly number[];\n}\n\n/** The columns of a node table, by index. */\ninterface NodePlan {\n readonly kind: \"nodes\";\n readonly names: readonly string[];\n readonly width: number;\n /** The column ids are read from, or -1 under nodeIdFrom \"index\" or rowNumberIds. */\n readonly id: number;\n /** Whether a row's id is its row number coerced by `ids` (rowNumberIds); else the raw ordinal under -1. */\n readonly rowNumber: boolean;\n readonly label: number;\n readonly attributes: readonly number[];\n}\n\n/** An adjacency table: no columns, a node and its neighbours per row. */\ninterface AdjacencyPlan {\n readonly kind: \"adjacency\";\n readonly names: readonly string[];\n}\n\n/** Everything one import call shares between its tables. */\ninterface ImportState {\n readonly sink: GraphSink;\n readonly report: ImportReportBuilder;\n readonly common: ResolvedImportOptions;\n readonly csv: ResolvedCsvOptions;\n readonly weightFromExplicit: boolean;\n readonly coercer: IdCoercer;\n readonly resolver: DirectionResolver;\n headerSet: boolean;\n /**\n * The file-level direction a SNAP (`# Directed graph` / `# Undirected graph`) or KONECT\n * (`% asym` / `% sym` / `% bip`) comment header declares; null when the file declares none\n * (the `defaultDirected` option applies).\n */\n commentDirected: boolean | null;\n}\n\n/** The comment characters of the SNAP (`#`) and KONECT (`%`) headers (research note 07 section 2.6). */\nconst COMMENT_CHARS: readonly string[] = Object.freeze([\"#\", \"%\"]);\n\n/**\n * The direction a SNAP or KONECT comment header declares (research note 07 section 2.6): SNAP\n * pages write `# Directed graph` / `# Undirected graph`, KONECT's first line is `% sym` (undirected),\n * `% asym` (directed) or `% bip` (bipartite, undirected).\n * @param comments - the leading comment lines\n * @returns true / false when a line declares the direction, null otherwise\n */\nfunction commentDirection(comments: readonly string[]): boolean | null {\n for (const comment of comments) {\n const text = comment.slice(1).trim().toLowerCase();\n if (text.startsWith(\"directed graph\") || text.startsWith(\"asym\")) {\n return true;\n }\n if (text.startsWith(\"undirected graph\") || text.startsWith(\"sym\") || text.startsWith(\"bip\")) {\n return false;\n }\n }\n return null;\n}\n\n/**\n * Apply the defaults of the CSV options and check every value.\n * @param options - the caller's options\n * @returns the resolved options; E_UNSUPPORTED for a value outside its set\n */\nfunction resolveCsvOptions(options: (CsvImportOptions & CommonImportOptions) | undefined): ResolvedCsvOptions {\n const o: CsvImportOptions = options ?? {};\n if (o.delimiter !== undefined && (typeof o.delimiter !== \"string\" || o.delimiter.length === 0)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option delimiter: expected a non-empty string\", {\n option: \"delimiter\",\n found: o.delimiter,\n });\n }\n if (o.delimiter !== undefined && BAD_DELIMITERS.has(o.delimiter)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option delimiter: a quote or a line break cannot delimit\", {\n option: \"delimiter\",\n found: o.delimiter,\n });\n }\n if (o.header !== undefined && o.header !== \"auto\" && typeof o.header !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option header: expected true, false or \"auto\"', {\n option: \"header\",\n found: o.header,\n });\n }\n if (o.table !== undefined && !TABLE_MODES.has(o.table)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option table: expected \"edges\", \"nodes\", \"adjacency\" or \"auto\"', {\n option: \"table\",\n found: o.table,\n });\n }\n for (const name of [\"sourceColumn\", \"targetColumn\", \"idColumn\"] as const) {\n checkColumnRef(name, o[name]);\n }\n if (o.typeColumn !== null) {\n checkColumnRef(\"typeColumn\", o.typeColumn);\n }\n if (o.table === \"adjacency\") {\n for (const name of [\"sourceColumn\", \"targetColumn\", \"typeColumn\", \"idColumn\"] as const) {\n if (o[name] !== undefined) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${name}: an adjacency table has no columns`, {\n option: name,\n found: o[name],\n });\n }\n }\n if (o.rowNumberIds === true) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option rowNumberIds: an adjacency table names its nodes\", {\n option: \"rowNumberIds\",\n found: o.rowNumberIds,\n });\n }\n }\n if (o.rowNumberIds !== undefined && typeof o.rowNumberIds !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option rowNumberIds: expected a boolean\", {\n option: \"rowNumberIds\",\n found: o.rowNumberIds,\n });\n }\n return {\n delimiter: o.delimiter ?? null,\n header: o.header ?? \"auto\",\n table: o.table ?? \"auto\",\n sourceColumn: o.sourceColumn ?? null,\n targetColumn: o.targetColumn ?? null,\n typeColumn: o.typeColumn,\n idColumn: o.idColumn ?? null,\n nodes: o.nodes ?? null,\n rowNumberIds: o.rowNumberIds ?? false,\n };\n}\n\n/**\n * Check a column reference option: a string name or a non-negative integer position.\n * @param name - the option name\n * @param value - the value\n */\nfunction checkColumnRef(name: string, value: unknown): void {\n if (value === undefined) {\n return;\n }\n if (typeof value === \"string\" && value.length > 0) {\n return;\n }\n if (typeof value === \"number\" && Number.isInteger(value) && value >= 0) {\n return;\n }\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${name}: expected a column name or a 0-based position`, {\n option: name,\n found: value,\n });\n}\n\n/**\n * Whether a cell is empty (nothing or whitespace only): an unset value, a missing endpoint.\n * @param text - the cell text\n * @returns true when blank\n */\nfunction isBlank(text: string): boolean {\n return text.length === 0 || text.trim().length === 0;\n}\n\n/**\n * Whether a cell is unset: blank and not quoted. A quoted blank cell (`\"\"`, `\" \"`) is a set value\n * (the empty string is a legal id and a legal value, design section 4.1); an unquoted one is\n * nothing at all.\n * @param text - the cell text\n * @param quoted - whether the cell was quoted\n * @returns true when the cell carries no value\n */\nfunction isUnset(text: string, quoted: boolean | undefined): boolean {\n return quoted !== true && isBlank(text);\n}\n\n/** Rows between two checks of the cancellation signal on an in-memory input. */\nconst ABORT_CHECK_INTERVAL = 64;\n\n/**\n * Header names made unique within the table: a repeated name becomes `<name>#<position>` (1-based,\n * the CSV analogue of the `#<origin.id>` rule of design section 5.6) and is reported.\n * @param names - the header names\n * @param report - the report\n * @param line - the header line\n * @returns the unique names\n */\nfunction uniqueNames(names: readonly string[], report: ImportReportBuilder, line: number): string[] {\n const seen = new Set<string>();\n return names.map((name, i) => {\n const unique = uniqueColumnName(name, String(i + 1), (n) => seen.has(n));\n seen.add(unique);\n if (unique !== name) {\n report.warning(\n \"coercion\",\n RENAMED_CODE,\n `column ${i + 1} \"${name}\" renamed to \"${unique}\": the header repeats the name`,\n { line, element: name },\n );\n }\n return unique;\n });\n}\n\n/**\n * Find a column by candidates, skipping indices already claimed by another role.\n * @param names - the header names\n * @param candidates - the names to look for\n * @param claimed - indices taken\n * @returns the index, or -1\n */\nfunction findFree(names: readonly string[], candidates: readonly string[], claimed: ReadonlySet<number>): number {\n const masked = names.map((name, i) => (claimed.has(i) ? \"\" : name));\n return findColumn(masked, candidates);\n}\n\n/**\n * Declare a role column (edge id, label) on the sink through the shared design section 5.6 rule:\n * a name already declared differently is renamed `<name>#<position>` (reported); a role already\n * held by another column is dropped from this declaration (reported), the values are kept under\n * the name.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the declaration\n * @param position - the 1-based column position, for the rename\n * @param report - the report\n * @param line - the header line\n * @returns the handle\n */\nfunction declareRoleColumn(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n decl: ColumnDecl,\n position: number,\n report: ImportReportBuilder,\n line: number,\n): ColumnHandle {\n const withOrigin: ColumnDecl = { ...decl, origin: { ...decl.origin, id: String(position) } };\n const resolved = declareResolved(sink, domain, withOrigin, report, { line, element: decl.name });\n if (resolved.roleDropped) {\n // a unique constraint belongs to the id role; without the role the column is plain text\n return resolved.handle;\n }\n return resolved.handle;\n}\n\n/**\n * Parse a Gephi Type cell.\n * @param text - the cell text\n * @returns the kind; undefined for a blank cell (the default applies); null for an unknown word\n */\nfunction parseKind(text: string): EdgeKind | null | undefined {\n if (isBlank(text)) {\n return undefined;\n }\n switch (text.trim().toLowerCase()) {\n case \"directed\":\n return \"directed\";\n case \"undirected\":\n return \"undirected\";\n case \"mutual\":\n return \"mutual\";\n default:\n return null;\n }\n}\n\n/**\n * Split an adjacency neighbour cell into its id and weight: the text after the LAST colon is the\n * weight when it is a number, nothing when it is empty (`a:1:` is the id `a:1`, unweighted, the\n * form the exporter writes for an id containing a colon), and part of the id otherwise.\n * @param text - the cell text\n * @returns the id text and the weight (undefined when the cell has none)\n */\nexport function splitNeighbour(text: string): { readonly id: string; readonly weight: number | undefined } {\n const colon = text.lastIndexOf(\":\");\n if (colon < 0) {\n return { id: text, weight: undefined };\n }\n const suffix = text.slice(colon + 1);\n if (suffix.length === 0) {\n return { id: text.slice(0, colon), weight: undefined };\n }\n const weight = numberOrNull(suffix);\n return weight === null ? { id: text, weight: undefined } : { id: text.slice(0, colon), weight };\n}\n\n/**\n * A weight suffix as a number, or null when the text is not one.\n * @param text - the suffix\n * @returns the number, or null\n */\nfunction numberOrNull(text: string): number | null {\n try {\n return parseWeightText(text) ?? null;\n } catch {\n // not a number: the colon belongs to the id\n return null;\n }\n}\n\n/**\n * Read one CSV table into the sink.\n */\nclass TableReader {\n private readonly state: ImportState;\n\n private readonly reader: CsvRecordReader;\n\n private readonly kind: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\";\n\n /**\n * Whether rowNumberIds applies to this table: a node table, or an \"auto\" table with no paired\n * node table. Never the edge table of a paired import, which must still fail loudly when its\n * header names no endpoints.\n */\n private readonly rowNumbers: boolean;\n\n private plan: EdgePlan | NodePlan | AdjacencyPlan | null = null;\n\n private writers: (InferredColumn | null)[] = [];\n\n private idHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n private labelHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n private dataRows = 0;\n\n private nodeOrdinal = 0;\n\n /** The edge ids seen so far (the id column is unique; a repeat is skipped with an issue). */\n private readonly edgeIds = new Set<string>();\n\n private readonly where: { line: number | null; element: string | null } = { line: null, element: null };\n\n /**\n * Create a table reader.\n * @param state - the import state\n * @param input - the table's input\n * @param kind - what the table is, or \"auto\"\n * @param progress - whether this table reports byte progress\n */\n constructor(\n state: ImportState,\n input: ImportInput,\n kind: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\",\n progress: boolean,\n ) {\n this.state = state;\n this.kind = kind;\n this.rowNumbers = state.csv.rowNumberIds && (kind === \"nodes\" || (kind === \"auto\" && state.csv.nodes === null));\n const readerOptions: CsvReaderOptions = {\n delimiter: state.csv.delimiter,\n comments: COMMENT_CHARS,\n // field counts say nothing about an adjacency table, whose rows vary in width\n skipQuoteErrors: kind === \"adjacency\",\n signal: state.common.signal,\n onProgress: progress ? state.common.onProgress : null,\n encoding: state.common.encoding,\n };\n this.reader = new CsvRecordReader(input, state.report, readerOptions);\n }\n\n /**\n * Read every row; the reader is closed (and a stream cancelled) when the import aborts midway.\n */\n async read(): Promise<void> {\n const iterator = this.reader[Symbol.asyncIterator]();\n try {\n await this.readRows(iterator);\n } finally {\n await iterator.return(undefined);\n }\n }\n\n /**\n * Read the header (or decide there is none), resolve the plan, then push every row.\n * @param iterator - the record iterator\n */\n private async readRows(iterator: AsyncGenerator<string[], void, undefined>): Promise<void> {\n const { report } = this.state;\n const first = await iterator.next();\n if (first.done && this.kind === \"adjacency\") {\n // an adjacency table has no header: an empty one is the empty graph\n return;\n }\n const firstRow: string[] = first.done\n ? report.fail(EMPTY_INPUT_CODE, \"the input is empty: no header row and no records\")\n : first.value;\n const firstLine = this.reader.line;\n const firstQuoted = this.reader.quoted.slice(0, firstRow.length);\n const pending: { row: string[]; quoted: readonly boolean[]; line: number }[] = [];\n let header: boolean;\n // an adjacency table has no header unless the caller says so: its rows vary in width; a\n // table numbered by rowNumberIds has one, since a headerless table takes its ids from column 0\n let mode = this.state.csv.header;\n if (mode === \"auto\" && this.kind === \"adjacency\") {\n mode = false;\n } else if (mode === \"auto\" && this.rowNumbers) {\n mode = true;\n }\n if (mode === \"auto\") {\n const second = await iterator.next();\n const secondRow: string[] | null = second.done ? null : second.value;\n const secondLine = this.reader.line;\n const secondQuoted = this.reader.quoted.slice(0, secondRow?.length ?? 0);\n header = looksLikeHeader(firstRow, secondRow);\n if (!header) {\n pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });\n }\n if (secondRow !== null) {\n pending.push({ row: secondRow, quoted: secondQuoted, line: secondLine });\n }\n } else {\n header = mode;\n if (!header) {\n pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });\n }\n }\n const names = header ? uniqueNames(headerNames(firstRow), report, firstLine) : positionalNames(firstRow.length);\n if (this.kind !== \"nodes\" && this.state.commentDirected === null) {\n this.state.commentDirected = commentDirection(this.reader.leadingComments);\n }\n this.plan = this.resolvePlan(names, header, firstLine);\n this.prepareColumns(firstLine);\n for (const { row, quoted, line } of pending) {\n this.processRow(row, quoted, line);\n }\n const { signal } = this.state.common;\n let sinceCheck = 0;\n for (;;) {\n const next = await iterator.next();\n if (next.done) {\n break;\n }\n this.processRow(next.value, this.reader.quoted, this.reader.line);\n if (++sinceCheck >= ABORT_CHECK_INTERVAL) {\n sinceCheck = 0;\n throwIfAborted(signal);\n }\n }\n for (const writer of this.writers) {\n writer?.finish();\n }\n if (this.dataRows === 0 && header) {\n report.warning(\"missing-value\", NO_DATA_ROWS_CODE, \"the table has a header and no data rows\", {\n line: firstLine,\n });\n }\n }\n\n /**\n * Decide the table's columns from its header.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param line - the header line\n * @returns the plan; the import aborts when no endpoints (or id) resolve\n */\n private resolvePlan(names: readonly string[], header: boolean, line: number): EdgePlan | NodePlan | AdjacencyPlan {\n const { csv, report } = this.state;\n if (this.kind === \"adjacency\") {\n return { kind: \"adjacency\", names: [] };\n }\n const width = names.length;\n let source = -1;\n let target = -1;\n if (this.kind !== \"nodes\") {\n if (csv.sourceColumn !== null) {\n source = resolveColumnRef(names, csv.sourceColumn, \"sourceColumn\");\n } else if (header) {\n source = findColumn(names, SOURCE_NAMES);\n } else {\n source = width >= 2 ? 0 : -1;\n }\n if (csv.targetColumn !== null) {\n target = resolveColumnRef(names, csv.targetColumn, \"targetColumn\");\n } else if (header) {\n target = findColumn(names, TARGET_NAMES);\n } else {\n target = width >= 2 ? 1 : -1;\n }\n if (source >= 0 && target >= 0 && source === target) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"sourceColumn and targetColumn name the same column\", {\n option: \"targetColumn\",\n found: names[target],\n });\n }\n }\n if (source >= 0 && target >= 0) {\n return this.edgePlan(names, header, source, target);\n }\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n if (\n this.kind === \"edges\" ||\n (this.kind === \"auto\" && (csv.sourceColumn !== null || csv.targetColumn !== null))\n ) {\n report.fail(\n NO_ENDPOINT_COLUMNS_CODE,\n `no source / target columns in the header (${shown}); a node table goes in the nodes option`,\n { line },\n { columns: [...names] },\n );\n }\n const idResolves = header ? findColumn(names, ID_NAMES) >= 0 : width >= 1;\n if (this.kind === \"auto\" && csv.idColumn === null && !idResolves && !this.rowNumbers) {\n report.fail(\n NO_ENDPOINT_COLUMNS_CODE,\n `no source / target columns and no id column in the header (${shown}); the input is neither an edge table nor a node table`,\n { line },\n { columns: [...names] },\n );\n }\n return this.nodePlan(names, header, line);\n }\n\n /**\n * The columns of an edge table.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param source - the source column\n * @param target - the target column\n * @returns the plan\n */\n private edgePlan(names: readonly string[], header: boolean, source: number, target: number): EdgePlan {\n const { csv, common, report } = this.state;\n const claimed = new Set<number>([source, target]);\n let weight = -1;\n if (common.weightFrom !== null) {\n if (header) {\n weight = findFree(names, [common.weightFrom], claimed);\n if (weight < 0 && this.state.weightFromExplicit) {\n report.warning(\n \"missing-value\",\n COLUMN_MISSING_CODE,\n `weight column ${JSON.stringify(common.weightFrom)} not found; edges are unweighted`,\n { line: this.reader.line, element: common.weightFrom },\n );\n }\n } else if (names.length >= 3 && !claimed.has(2)) {\n weight = 2;\n }\n }\n if (weight >= 0) {\n claimed.add(weight);\n }\n let type = -1;\n if (csv.typeColumn === undefined) {\n if (header && names[source] === \"Source\" && names[target] === \"Target\") {\n type = findFree(names, [TYPE_NAME], claimed);\n if (type >= 0 && names[type] !== TYPE_NAME) {\n type = -1;\n }\n }\n } else if (csv.typeColumn !== null) {\n type = resolveColumnRef(names, csv.typeColumn, \"typeColumn\");\n if (claimed.has(type)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"typeColumn names an endpoint or weight column\", {\n option: \"typeColumn\",\n found: names[type],\n });\n }\n }\n if (type >= 0) {\n claimed.add(type);\n }\n const id = header ? findFree(names, EDGE_ID_NAMES, claimed) : -1;\n if (id >= 0) {\n claimed.add(id);\n }\n const label = header ? findFree(names, LABEL_NAMES, claimed) : -1;\n if (label >= 0) {\n claimed.add(label);\n }\n const attributes: number[] = [];\n for (let i = 0; i < names.length; i++) {\n if (!claimed.has(i)) {\n attributes.push(i);\n }\n }\n return { kind: \"edges\", names, width: names.length, source, target, weight, type, id, label, attributes };\n }\n\n /**\n * The columns of a node table.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param line - the header line\n * @returns the plan; the import aborts when no id column resolves\n */\n private nodePlan(names: readonly string[], header: boolean, line: number): NodePlan {\n const { csv, common, report } = this.state;\n const claimed = new Set<number>();\n let idColumn = -1;\n if (csv.idColumn !== null) {\n idColumn = resolveColumnRef(names, csv.idColumn, \"idColumn\");\n } else if (header) {\n idColumn = findColumn(names, ID_NAMES);\n } else if (names.length >= 1) {\n idColumn = 0;\n }\n const label = header ? findFree(names, LABEL_NAMES, new Set(idColumn >= 0 ? [idColumn] : [])) : -1;\n let id: number;\n switch (common.nodeIdFrom) {\n case \"label\":\n if (label < 0) {\n report.fail(NO_ID_COLUMN_CODE, 'nodeIdFrom is \"label\" but the node table has no label column', {\n line,\n });\n }\n id = label;\n break;\n case \"index\":\n id = -1;\n break;\n default:\n if (idColumn < 0 && this.rowNumbers) {\n id = -1;\n break;\n }\n if (idColumn < 0) {\n report.fail(\n NO_ID_COLUMN_CODE,\n `no id column in the node table header (${names.map((n) => JSON.stringify(n)).join(\", \")})`,\n { line },\n { columns: [...names] },\n );\n }\n id = idColumn;\n claimed.add(idColumn);\n break;\n }\n if (label >= 0) {\n claimed.add(label);\n }\n const attributes: number[] = [];\n for (let i = 0; i < names.length; i++) {\n if (!claimed.has(i)) {\n attributes.push(i);\n }\n }\n const rowNumber = id < 0 && common.nodeIdFrom !== \"index\";\n return { kind: \"nodes\", names, width: names.length, id, rowNumber, label, attributes };\n }\n\n /**\n * Declare the role columns and create a writer per attribute column.\n * @param line - the header line\n */\n private prepareColumns(line: number): void {\n const plan = this.requirePlan();\n if (plan.kind === \"adjacency\") {\n return;\n }\n const { sink, report } = this.state;\n const domain = plan.kind === \"edges\" ? \"edge\" : \"node\";\n const origin = { format: \"csv\" };\n if (plan.kind === \"edges\" && plan.id >= 0) {\n this.idHandle = declareRoleColumn(\n sink,\n \"edge\",\n { name: plan.names[plan.id], dtype: \"string\", nullable: true, role: \"id\", unique: true, origin },\n plan.id + 1,\n report,\n line,\n );\n }\n if (plan.label >= 0) {\n this.labelHandle = declareRoleColumn(\n sink,\n domain,\n { name: plan.names[plan.label], dtype: \"string\", nullable: true, role: \"label\", origin },\n plan.label + 1,\n report,\n line,\n );\n }\n this.writers = plan.names.map(() => null);\n for (const index of plan.attributes) {\n this.writers[index] = new InferredColumn(plan.names[index], domain, sink, report);\n }\n }\n\n /**\n * The plan, which exists once the header was read.\n * @returns the plan\n */\n private requirePlan(): EdgePlan | NodePlan | AdjacencyPlan {\n if (this.plan === null) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"the header has not been read\", { reason: \"no plan\" });\n }\n return this.plan;\n }\n\n /**\n * Push one data row.\n * @param row - the cells\n * @param quoted - whether each cell was quoted (a quoted empty cell is the empty string)\n * @param line - the row's line\n */\n private processRow(row: string[], quoted: readonly boolean[], line: number): void {\n const plan = this.requirePlan();\n this.dataRows++;\n if (plan.kind === \"edges\") {\n this.processEdgeRow(plan, row, quoted, line);\n } else if (plan.kind === \"nodes\") {\n this.processNodeRow(plan, row, quoted, line);\n } else {\n this.processAdjacencyRow(row, quoted, line);\n }\n }\n\n /**\n * Push one adjacency row: the node, then one edge per set neighbour cell, in row order.\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processAdjacencyRow(row: string[], quoted: readonly boolean[], line: number): void {\n const { report, sink, resolver, common } = this.state;\n const { counts } = report;\n const { where } = this;\n where.line = line;\n where.element = null;\n let neighbours = 0;\n for (let k = 1; k < row.length; k++) {\n if (!isUnset(row[k], quoted[k])) {\n neighbours++;\n }\n }\n if (isUnset(row[0], quoted[0])) {\n report.error(\"missing-value\", MISSING_ID_CODE, `line ${line}: blank node cell`, { line });\n counts.skippedNodes++;\n counts.skippedEdges += neighbours;\n return;\n }\n const kind: EdgeKind = (this.state.commentDirected ?? common.defaultDirected) ? \"directed\" : \"undirected\";\n if (!this.state.headerSet) {\n this.state.headerSet = true;\n resolver.setHeader(kind !== \"undirected\", where);\n }\n let source: NodeId;\n try {\n where.element = row[0];\n source = this.coerce(row[0]);\n if (sink.indexOf(source) === INVALID_INDEX) {\n counts.nodes++;\n }\n sink.addNode(source);\n } catch (err) {\n report.recordError(err, where);\n counts.skippedNodes++;\n counts.skippedEdges += neighbours;\n return;\n }\n for (let k = 1; k < row.length; k++) {\n if (isUnset(row[k], quoted[k])) {\n continue;\n }\n where.element = row[k];\n try {\n const cell = splitNeighbour(row[k]);\n const target = this.coerce(cell.id);\n const targetNew = sink.indexOf(target) === INVALID_INDEX;\n const before = sink.edgeCount;\n resolver.addEdge(source, target, kind, common.weightFrom === null ? undefined : cell.weight, where);\n counts.edges += sink.edgeCount - before;\n counts.nodes += targetNew ? 1 : 0;\n } catch (err) {\n report.recordError(err, where);\n counts.skippedEdges++;\n }\n }\n }\n\n /**\n * Push one edge row: endpoints, weight, direction, then the attribute cells.\n * @param plan - the edge plan\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processEdgeRow(plan: EdgePlan, row: string[], quoted: readonly boolean[], line: number): void {\n const { report, sink, resolver, common } = this.state;\n const { counts } = report;\n if (row.length !== plan.width) {\n report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `line ${line}: ${row.length} field(s), the header has ${plan.width}`,\n { line },\n );\n counts.skippedEdges++;\n return;\n }\n const sourceText = row[plan.source];\n const targetText = row[plan.target];\n const sourceMissing = isUnset(sourceText, quoted[plan.source]);\n if (sourceMissing || isUnset(targetText, quoted[plan.target])) {\n report.error(\n \"missing-value\",\n MISSING_ENDPOINT_CODE,\n `line ${line}: blank ${sourceMissing ? \"source\" : \"target\"} cell`,\n { line },\n );\n counts.skippedEdges++;\n return;\n }\n let kind: EdgeKind = (this.state.commentDirected ?? common.defaultDirected) ? \"directed\" : \"undirected\";\n if (plan.type >= 0) {\n const parsed = parseKind(row[plan.type]);\n if (parsed === null) {\n report.error(\n \"validation-error\",\n BAD_TYPE_CODE,\n `line ${line}: Type ${JSON.stringify(row[plan.type])} is not Directed, Undirected or Mutual`,\n { line },\n );\n counts.skippedEdges++;\n return;\n }\n if (parsed !== undefined) {\n kind = parsed;\n }\n }\n const { where } = this;\n where.line = line;\n where.element = null;\n const idText = plan.id >= 0 && !isUnset(row[plan.id], quoted[plan.id]) ? row[plan.id] : null;\n if (idText !== null) {\n if (this.edgeIds.has(idText)) {\n report.error(\n \"validation-error\",\n DUPLICATE_EDGE_ID_CODE,\n `line ${line}: edge id ${JSON.stringify(idText)} repeats an earlier row's; the row is skipped`,\n { line, element: idText },\n );\n counts.skippedEdges++;\n return;\n }\n this.edgeIds.add(idText);\n }\n let edge: number;\n try {\n const source = this.coerce(sourceText);\n const target = this.coerce(targetText);\n const weight = plan.weight >= 0 ? parseWeightText(row[plan.weight]) : undefined;\n if (!this.state.headerSet) {\n this.state.headerSet = true;\n resolver.setHeader(kind !== \"undirected\", where);\n }\n const sourceNew = sink.indexOf(source) === INVALID_INDEX;\n const targetNew = source !== target && sink.indexOf(target) === INVALID_INDEX;\n const before = sink.edgeCount;\n edge = resolver.addEdge(source, target, kind, weight, where);\n counts.edges += sink.edgeCount - before;\n counts.nodes += (sourceNew ? 1 : 0) + (targetNew ? 1 : 0);\n } catch (err) {\n where.element = idText ?? `${sourceText}->${targetText}`;\n report.recordError(err, where);\n counts.skippedEdges++;\n return;\n }\n if (idText !== null) {\n this.writeRole(this.idHandle, \"edge\", edge, idText, plan.names[plan.id], line);\n }\n if (plan.label >= 0 && !isUnset(row[plan.label], quoted[plan.label])) {\n this.writeRole(this.labelHandle, \"edge\", edge, row[plan.label], plan.names[plan.label], line);\n }\n this.writeAttributes(plan, row, quoted, edge, line);\n }\n\n /**\n * Push one node row: the id, then the label and attribute cells.\n * @param plan - the node plan\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processNodeRow(plan: NodePlan, row: string[], quoted: readonly boolean[], line: number): void {\n const { report, sink } = this.state;\n const { counts } = report;\n const ordinal = this.nodeOrdinal++;\n if (row.length !== plan.width) {\n report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `line ${line}: ${row.length} field(s), the header has ${plan.width}`,\n { line },\n );\n counts.skippedNodes++;\n return;\n }\n const idText = plan.id >= 0 ? row[plan.id] : String(ordinal);\n if (plan.id >= 0 && isUnset(idText, quoted[plan.id])) {\n report.error(\"missing-value\", MISSING_ID_CODE, `line ${line}: blank id cell`, { line });\n counts.skippedNodes++;\n return;\n }\n const { where } = this;\n where.line = line;\n where.element = idText;\n let index: number;\n try {\n const id = plan.id >= 0 || plan.rowNumber ? this.coerce(idText) : ordinal;\n if (sink.indexOf(id) !== INVALID_INDEX) {\n report.warning(\n \"merged\",\n DUPLICATE_NODE_CODE,\n `line ${line}: node ${JSON.stringify(id)} already exists; its attributes are overwritten`,\n where,\n );\n } else {\n counts.nodes++;\n }\n index = sink.addNode(id);\n } catch (err) {\n report.recordError(err, where);\n counts.skippedNodes++;\n return;\n }\n if (plan.label >= 0 && !isUnset(row[plan.label], quoted[plan.label])) {\n this.writeRole(this.labelHandle, \"node\", index, row[plan.label], plan.names[plan.label], line);\n }\n this.writeAttributes(plan, row, quoted, index, line);\n }\n\n /**\n * Coerce an id cell, reporting a merge under `ids: \"number\"`.\n * @param text - the cell text\n * @returns the id\n */\n private coerce(text: string): NodeId {\n const id = this.state.coercer.text(text);\n const merge = this.state.coercer.lastMerge;\n if (merge !== null) {\n this.state.report.warnOnce(\n \"coercion\",\n ID_MERGED_CODE,\n `id ${JSON.stringify(merge.text)} merged with ${JSON.stringify(merge.previousText)} as ${merge.id} under ids: \"number\"`,\n this.where,\n );\n }\n return id;\n }\n\n /**\n * Write a role column cell (edge id, label).\n * @param handle - the column handle\n * @param domain - node or edge\n * @param row - the node or edge index\n * @param text - the cell text\n * @param name - the column name, for issues\n * @param line - the row's line\n */\n private writeRole(\n handle: ColumnHandle,\n domain: \"node\" | \"edge\",\n row: number,\n text: string,\n name: string,\n line: number,\n ): void {\n try {\n if (domain === \"node\") {\n this.state.sink.setNodeValue(handle, row, text);\n } else {\n this.state.sink.setEdgeValue(handle, row, text);\n }\n } catch (err) {\n this.state.report.recordError(err, { line, element: name });\n }\n }\n\n /**\n * Write the attribute cells of a row: an unquoted blank cell is unset, a quoted one (`\"\"`,\n * `\" \"`) is the text it holds.\n * @param plan - the plan\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param index - the node or edge index\n * @param line - the row's line\n */\n private writeAttributes(\n plan: EdgePlan | NodePlan,\n row: readonly string[],\n quoted: readonly boolean[],\n index: number,\n line: number,\n ): void {\n for (const k of plan.attributes) {\n const text = row[k];\n if (isUnset(text, quoted[k])) {\n continue;\n }\n const writer = this.writers[k];\n if (writer === null) {\n continue;\n }\n try {\n writer.write(index, text);\n } catch (err) {\n this.state.report.recordError(err, { line, element: writer.name });\n }\n }\n }\n}\n\nconst HEAD_BYTES = 4096;\nconst OTHER_FORMAT = /^\\s*(<|[[{]|(strict\\s+)?(di)?graph(\\s+\\S+)?\\s*\\{|\\*vertices|creator\\b|graph\\s*\\[)/i;\n\n/**\n * Sniff confidence for the registry: 0 for XML, JSON, GML, DOT and Pajek openings; otherwise a\n * delimited first row with endpoint headers is 0.9, with an id header 0.6, any consistently\n * delimited rows 0.3, a single column 0.\n * @param head - the first bytes of the input\n * @returns a confidence in 0..1\n */\nfunction sniff(head: Uint8Array): number {\n const text = new TextDecoder(\"utf-8\").decode(head.subarray(0, HEAD_BYTES));\n const body = text.startsWith(String.fromCharCode(0xfeff)) ? text.slice(1) : text;\n if (body.trim().length === 0 || OTHER_FORMAT.test(body)) {\n return 0;\n }\n const newline = sniffNewline(body);\n const delimiter = sniffDelimiter(body, newline);\n if (delimiter === null) {\n return 0;\n }\n const end = body.indexOf(newline);\n const firstLine = end < 0 ? body : body.slice(0, end);\n const names = headerNames(firstLine.replace(/\\r$/, \"\").split(delimiter));\n if (findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0) {\n return 0.9;\n }\n if (findColumn(names, ID_NAMES) >= 0) {\n return 0.6;\n }\n return 0.3;\n}\n\n/**\n * Import a CSV edge table (and an optional node table) into a sink.\n * @param input - the edge table (or, with `table: \"nodes\"` or a header without endpoints, a node table)\n * @param sink - the sink\n * @param options - CSV and common options\n * @returns the report; ImportError with the partial report when the import aborts\n */\nasync function importCsv(\n input: ImportInput,\n sink: GraphSink,\n options?: CsvImportOptions & CommonImportOptions,\n): Promise<ImportReport> {\n const common = resolveImportOptions(options, { ids: \"canonical\", defaultDirected: true, weightFrom: \"weight\" });\n const csv = resolveCsvOptions(options);\n const report = new ImportReportBuilder(\"csv\", common.errorLimit);\n reportSinkOptions(sink, options, report);\n reportUnusedOptions(\n options,\n report,\n csv.table === \"nodes\" || csv.nodes !== null ? USED_OPTIONS_WITH_NODES : USED_OPTIONS,\n );\n const state: ImportState = {\n sink,\n report,\n common,\n csv,\n weightFromExplicit: typeof options?.weightFrom === \"string\",\n coercer: new IdCoercer(common.ids),\n resolver: new DirectionResolver(sink, report, common.onMixedDirection),\n headerSet: false,\n commentDirected: null,\n };\n if (csv.nodes !== null) {\n await new TableReader(state, csv.nodes, \"nodes\", false).read();\n }\n await new TableReader(state, input, csv.table, true).read();\n if (state.coercer.mergeCount > 1) {\n report.warning(\n \"coercion\",\n ID_MERGED_CODE,\n `${state.coercer.mergeCount} id cell(s) merged into ids other cells already produced under ids: \"number\"`,\n );\n }\n throwIfAborted(common.signal);\n return report.finish();\n}\n\n/** The CSV / TSV importer plugin (subpath `@graphty/graph-io/csv`). */\nexport const csvImporter: GraphImporter<CsvImportOptions> = Object.freeze({\n format: \"csv\",\n extensions: Object.freeze([\".csv\", \".tsv\", \".edges\", \".edgelist\"]),\n mimeTypes: Object.freeze([\"text/csv\", \"text/tab-separated-values\", \"text/plain\"]),\n sniff,\n import: importCsv,\n});\n","/**\n * The CSV / TSV exporter (design section 8.5; research note 07 section 9): writes the edge table\n * (`Source,Target,Type,Id,Label,Weight,<attributes>` in the Gephi dialect, `source,target,weight,\n * <attributes>` in the generic one) or, with `table: \"nodes\"`, the node table (`Id,Label,\n * <attributes>`), RFC 4180 quoted, one row per logical edge with expanded pairs folded back\n * through the `pair` role column; or, with `table: \"adjacency\"`, an adjacency table: a node and its\n * neighbours per row (`id:weight` for an explicit weight), no header, no attribute columns.\n *\n * What survives a re-import exactly: ids (as text under the canonical rule), topology and\n * orientation, explicit weights (blank cells for defaulted ones), the per-row direction of the\n * Gephi dialect, edge ids and labels (string), bool / i32 / f64 / string columns (an f64 value is\n * written with a decimal point so it reads back as f64), low-cardinality dicts, and set empty\n * strings (the quoted empty cell; an unset cell is written as nothing). check() reports everything\n * else: the generic dialect drops direction, mutual pairs become two directed rows, lists and json\n * values are written as text, non-finite numbers do not read back, attribute names that collide\n * with the reserved headers are not written, node attributes are written by the node table only,\n * and the edge table carries neither isolated nodes nor the node order.\n *\n * The adjacency table keeps ids, the node order, isolated nodes, the edge order, orientation and\n * explicit weights: consecutive edges with the same source share a row, a node the rows would\n * otherwise introduce out of index order gets a row of its own first, and a neighbour id holding a\n * colon is written `id:` when it has no weight so the importer does not read its tail as one.\n */\n\nimport { type Column, GraphFormatError, type GraphSnapshot, type NodeId } from \"@graphty/graph-format\";\n\nimport { DICT_SAMPLE_ROWS, DictHeuristic } from \"../../common/attributes.js\";\nimport { type PairFolding, pairFolding } from \"../../common/direction.js\";\nimport { quoteCsvCell } from \"../../common/escape.js\";\nimport { capabilities, checkCapabilities, countMixedEdges, LOSS } from \"../../common/export.js\";\nimport { formatDecimal, formatInteger } from \"../../common/format.js\";\nimport { canonicalId } from \"../../common/ids.js\";\nimport { joinListText } from \"../../common/lists.js\";\nimport { type ResolvedExportOptions, resolveExportOptions } from \"../../common/options.js\";\nimport { inferTextDtype, type TextDtype } from \"../../common/text.js\";\nimport { type ExplicitWeights, explicitWeights } from \"../../common/weights.js\";\nimport { encodeChunks, joinText } from \"../../common/writer.js\";\nimport { type CommonExportOptions, type ExportCapabilities, type GraphExporter, type LossNote } from \"../../types.js\";\nimport { EDGE_ID_NAMES, findColumn, LABEL_NAMES } from \"./header.js\";\nimport { DELIMITER_CANDIDATES } from \"./records.js\";\n\n/** The format-specific options of the CSV exporter. */\nexport interface CsvExportOptions {\n /**\n * The header spelling: \"gephi\" (default) writes `Source,Target,Type,...,Weight` with the per-row\n * direction; \"generic\" writes `source,target,...,weight` and no direction column.\n */\n dialect?: \"gephi\" | \"generic\" | undefined;\n /**\n * Which table to write: the edge table (default), the node table, or an adjacency table (a node\n * and its neighbours per row; read back with the importer's `table: \"adjacency\"`).\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | undefined;\n /** The field delimiter; \",\" by default. */\n delimiter?: string | undefined;\n /** The line terminator; \"\\n\" by default. */\n newline?: \"\\n\" | \"\\r\\n\" | undefined;\n /** Whether to write the header row; true by default (an adjacency table never has one). */\n header?: boolean | undefined;\n}\n\n/** The CSV loss-note codes of check(); the shared ones are LOSS's. */\nexport const CSV_LOSS = Object.freeze({\n /** Two node ids share one text (a number and a string); export() throws E_INVALID_ID. */\n ID_TEXT_COLLISION: LOSS.ID_TEXT_COLLISION,\n /** Ids whose text reads back as the other type under the canonical rule. */\n ID_TEXT_TYPE: LOSS.ID_TEXT_TYPE,\n /**\n * The generic dialect has no direction column; an undirected or mixed graph reads back as\n * directed. The Gephi dialect loses the direction of an undirected graph without edges (no\n * row carries a Type cell).\n */\n DIRECTION_DROPPED: \"W_CSV_DIRECTION_DROPPED\",\n /** Mutual pairs are written as two directed rows. */\n MUTUAL_EXPANDED: LOSS.MUTUAL_EXPANDED,\n /** An attribute column named like a reserved header is not written. */\n RESERVED_NAME: \"W_CSV_RESERVED_NAME\",\n /** A column without a role that the importer gives one back by its name. */\n ROLE_ASSUMED: LOSS.ROLE_ASSUMED,\n /** A role column (id, label) whose name the importer does not recognise; the role is lost. */\n ROLE_NAME: \"W_CSV_ROLE_NAME\",\n /** A role column (id, label) that is not string / dict reads back as string. */\n TEXT_ROLE: \"W_CSV_TEXT_ROLE\",\n /** NaN / Infinity in a numeric column read back as text. */\n NONFINITE: \"W_CSV_NONFINITE\",\n /** A text column whose every value reads back as a number or boolean. */\n TEXT_INFERRED: LOSS.TEXT_INFERRED,\n /** A dict column whose cardinality makes the importer read it back as string, or the reverse. */\n STORAGE_CLASS_CHANGED: LOSS.STORAGE_CLASS,\n /** Node attributes are written by a `table: \"nodes\"` export only. */\n NODE_TABLE: \"W_CSV_NODE_TABLE\",\n /** The edge table carries no node without an edge: isolated nodes vanish on re-import. */\n ISOLATED_NODES: \"W_CSV_ISOLATED_NODES\",\n /** The edge table lists nodes by first appearance; the node order (and indices) change on re-import. */\n NODE_ORDER: \"W_CSV_NODE_ORDER\",\n /** An adjacency table holds no edge column but the weight: edge ids, labels and attributes are not written. */\n EDGE_COLUMNS: \"W_CSV_EDGE_COLUMNS\",\n});\n\n/** What the CSV format keeps as declared. */\nexport const CSV_CAPABILITIES: ExportCapabilities = capabilities({\n mixedDirection: true,\n multiEdges: true,\n selfLoops: true,\n edgeIds: \"optional\",\n idCharset: \"any\",\n dtypes: [\"bool\", \"i32\", \"f64\", \"string\", \"dict\"],\n});\n\n/** The dialect header spellings. */\ninterface Dialect {\n readonly source: string;\n readonly target: string;\n readonly type: string | null;\n readonly id: string;\n readonly label: string;\n readonly weight: string;\n}\n\nconst DIALECTS: Readonly<Record<\"gephi\" | \"generic\", Dialect>> = {\n gephi: { source: \"Source\", target: \"Target\", type: \"Type\", id: \"Id\", label: \"Label\", weight: \"Weight\" },\n generic: { source: \"source\", target: \"target\", type: null, id: \"id\", label: \"label\", weight: \"weight\" },\n};\n\n/** The roles the tables have a slot for (the id and label headers); every other role is reported. */\nconst KEPT_ROLES: ReadonlySet<string> = new Set([\"id\", \"label\"]);\n\n/** Roles the edge and node tables never write as attributes. */\nconst SKIPPED_ROLES: ReadonlySet<string> = new Set([\n \"directed\",\n \"pair\",\n \"mutual\",\n \"weight\",\n \"timeText\",\n \"position\",\n \"color\",\n \"size\",\n \"shape\",\n \"thickness\",\n \"parent\",\n \"parents\",\n \"start\",\n \"end\",\n \"timestamp\",\n \"timestamps\",\n \"spells\",\n \"open\",\n \"spellsOpen\",\n]);\n\n/** The CSV options with defaults applied. */\ninterface ResolvedCsvExportOptions {\n readonly dialect: Dialect;\n readonly table: \"edges\" | \"nodes\" | \"adjacency\";\n readonly delimiter: string;\n readonly newline: string;\n readonly header: boolean;\n}\n\n/** A column written under a header. */\ninterface ColumnOut {\n readonly column: Column;\n readonly header: string;\n}\n\n/** The plan of one export call: what is written, and the notes. */\ninterface Plan {\n readonly csv: ResolvedCsvExportOptions;\n readonly common: ResolvedExportOptions;\n readonly notes: LossNote[];\n /** The rows of the edge table (logical edge indices; mirrors of undirected pairs left out). */\n readonly edgeRows: number[];\n readonly folding: PairFolding;\n readonly edgeId: Column | null;\n readonly edgeLabel: Column | null;\n readonly weights: ExplicitWeights;\n readonly edgeColumns: ColumnOut[];\n readonly nodeLabel: Column | null;\n readonly nodeColumns: ColumnOut[];\n /** The id text of every node. */\n readonly idText: (index: number) => string;\n}\n\n/**\n * Apply the defaults of the CSV export options and check every value.\n * @param options - the caller's options\n * @returns the resolved options; E_UNSUPPORTED for a value outside its set\n */\nfunction resolveCsvExportOptions(\n options: (CsvExportOptions & CommonExportOptions) | undefined,\n): ResolvedCsvExportOptions {\n const o: CsvExportOptions = options ?? {};\n if (o.dialect !== undefined && o.dialect !== \"gephi\" && o.dialect !== \"generic\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option dialect: expected \"gephi\" or \"generic\"', {\n option: \"dialect\",\n found: o.dialect,\n });\n }\n if (o.table !== undefined && o.table !== \"edges\" && o.table !== \"nodes\" && o.table !== \"adjacency\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option table: expected \"edges\", \"nodes\" or \"adjacency\"', {\n option: \"table\",\n found: o.table,\n });\n }\n if (\n o.delimiter !== undefined &&\n (typeof o.delimiter !== \"string\" ||\n o.delimiter.length !== 1 ||\n o.delimiter === '\"' ||\n o.delimiter === \"\\n\" ||\n o.delimiter === \"\\r\")\n ) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n \"option delimiter: expected one character other than a quote or a line break\",\n { option: \"delimiter\", found: o.delimiter },\n );\n }\n if (o.newline !== undefined && o.newline !== \"\\n\" && o.newline !== \"\\r\\n\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option newline: expected LF or CRLF\", {\n option: \"newline\",\n found: o.newline,\n });\n }\n if (o.table === \"adjacency\" && o.header === true) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option header: an adjacency table has no header row\", {\n option: \"header\",\n found: o.header,\n });\n }\n if (o.header !== undefined && typeof o.header !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option header: expected a boolean\", {\n option: \"header\",\n found: o.header,\n });\n }\n return {\n dialect: DIALECTS[o.dialect ?? \"gephi\"],\n table: o.table ?? \"edges\",\n delimiter: o.delimiter ?? \",\",\n newline: o.newline ?? \"\\n\",\n header: o.header ?? true,\n };\n}\n\n/**\n * One scalar (or one list item) as text by its dtype.\n * @param dtype - the scalar dtype\n * @param value - the value\n * @returns the text\n */\nfunction scalarText(dtype: string, value: unknown): string {\n switch (dtype) {\n case \"bool\":\n return value === true ? \"true\" : \"false\";\n case \"f32\":\n case \"f64\":\n return typeof value === \"number\" ? formatDecimal(value, dtype) : String(value);\n case \"i32\":\n case \"u32\":\n case \"u8\":\n return typeof value === \"number\" ? formatInteger(value) : String(value);\n case \"string\":\n case \"dict\":\n return typeof value === \"string\" ? value : String(value);\n case \"json\":\n return JSON.stringify(value) ?? \"\";\n default:\n return String(value);\n }\n}\n\n/**\n * The text of one cell: null for an unset row (written as nothing between the delimiters);\n * components and list items joined by `;`.\n * @param column - the column\n * @param row - the row\n * @returns the cell text, or null\n */\nfunction cellText(column: Column, row: number): string | null {\n if (!column.isSet(row)) {\n return null;\n }\n switch (column.dtype) {\n case \"list\": {\n const items = column.sliceOf(row);\n const { dtype } = column.child;\n return joinListText(\n items.map((item) => scalarText(dtype, item)),\n \"semicolon\",\n );\n }\n case \"json\":\n return JSON.stringify(column.values[row]) ?? \"\";\n case \"bool\":\n case \"string\":\n case \"dict\":\n return scalarText(column.dtype, column.value(row));\n default: {\n const { components } = column.meta;\n const { data } = column;\n if (components === 1) {\n return scalarText(column.dtype, data[row]);\n }\n const parts: string[] = [];\n for (let k = 0; k < components; k++) {\n parts.push(scalarText(column.dtype, data[row * components + k]));\n }\n return parts.join(\";\");\n }\n }\n}\n\n/**\n * Build the plan of an export: resolve options, choose the rows and columns, and collect the\n * CSV-specific loss notes (check() adds the generic ones). The value-level notes need a pass over\n * every written cell and are collected for check() only; export() needs the plan and the fatal\n * id-collision note alone.\n * @param snapshot - the snapshot\n * @param options - the caller's options\n * @param values - whether to scan the values for notes\n * @returns the plan\n */\n/**\n * The id notes: text collisions are fatal (export() throws E_INVALID_ID); type changes under the\n * canonical re-read are reported.\n * @param snapshot - the snapshot\n * @param values - whether the values are inspected (false for a plan that writes nothing)\n * @param note - the note recorder\n */\nfunction idNotes(\n snapshot: GraphSnapshot,\n values: boolean,\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n): void {\n const { ids } = snapshot;\n if (ids.kind === \"mixed\") {\n const seen = new Set<string>();\n let collisions = 0;\n for (let i = 0; i < ids.size; i++) {\n const text = String(ids.idOf(i));\n if (seen.has(text)) {\n collisions++;\n } else {\n seen.add(text);\n }\n }\n if (collisions > 0) {\n note(\n CSV_LOSS.ID_TEXT_COLLISION,\n `${collisions} node id(s) share their text with another id (a number and a string); export() will throw E_INVALID_ID`,\n null,\n collisions,\n );\n }\n }\n if (values && ids.kind !== \"identity\" && ids.kind !== \"dense\") {\n let changed = 0;\n for (let i = 0; i < ids.size; i++) {\n const id: NodeId = ids.idOf(i);\n if (typeof canonicalId(String(id)) !== typeof id) {\n changed++;\n }\n }\n if (changed > 0) {\n note(\n CSV_LOSS.ID_TEXT_TYPE,\n `${changed} node id(s) read back as the other type under ids: \"canonical\" (a string \"1\" becomes 1, a number 1.5 becomes \"1.5\")`,\n null,\n changed,\n );\n }\n }\n}\n\nfunction planExport(\n snapshot: GraphSnapshot,\n options: (CsvExportOptions & CommonExportOptions) | undefined,\n values: boolean,\n): Plan {\n const common = resolveExportOptions(options);\n const csv = resolveCsvExportOptions(options);\n const notes: LossNote[] = [];\n const note = (code: string, message: string, column: string | null = null, count: number | null = null): void => {\n notes.push(Object.freeze({ code, message, column, count }));\n };\n const { ids } = snapshot;\n const idText = (index: number): string => String(ids.idOf(index));\n idNotes(snapshot, values, note);\n\n // edge rows: fold the mirrors of undirected pairs; mutual pairs stay two directed rows\n const folding = pairFolding(snapshot);\n const edgeRows: number[] = [];\n for (let e = 0; e < snapshot.edgeCount; e++) {\n if (!folding.folded(e)) {\n edgeRows.push(e);\n }\n }\n if (csv.dialect.type === null || csv.table === \"adjacency\") {\n const where = csv.table === \"adjacency\" ? \"an adjacency table\" : \"the generic dialect\";\n const mixed = countMixedEdges(snapshot);\n if (!snapshot.directed) {\n note(\n CSV_LOSS.DIRECTION_DROPPED,\n `${where} has no direction column; ${snapshot.edgeCount} undirected edge(s) read back as directed unless the importer is told otherwise`,\n null,\n snapshot.edgeCount,\n );\n } else if (mixed > 0) {\n note(\n CSV_LOSS.DIRECTION_DROPPED,\n `${where} has no direction column; ${mixed} undirected edge(s) of a mixed graph read back as one directed edge each`,\n null,\n mixed,\n );\n }\n } else if (csv.table === \"edges\" && !snapshot.directed && edgeRows.length === 0) {\n note(\n CSV_LOSS.DIRECTION_DROPPED,\n \"the direction lives in the Type cell of each edge row; an undirected graph without edges reads back as directed\",\n null,\n 0,\n );\n }\n if (folding.mutualCount > 0) {\n note(\n CSV_LOSS.MUTUAL_EXPANDED,\n `${folding.mutualCount} mutual pair(s) are written as two directed rows; the mutual mark is lost`,\n null,\n folding.mutualCount,\n );\n }\n if (csv.table === \"edges\") {\n noteNodeCoverage(snapshot, edgeRows, note);\n }\n\n // weights\n const weights = explicitWeights(snapshot);\n\n // the edge table's columns\n const edgeId = snapshot.edges.byRole(\"id\");\n const edgeLabel = snapshot.edges.byRole(\"label\");\n const edgeReserved = new Set<string>([csv.dialect.source, csv.dialect.target, csv.dialect.weight].map(lower));\n if (csv.dialect.type !== null) {\n edgeReserved.add(lower(csv.dialect.type));\n }\n const edgeColumns = attributeColumns(\n snapshot.edges,\n \"edge\",\n edgeReserved,\n edgeId,\n edgeLabel,\n csv.table === \"edges\" ? note : null,\n );\n if (csv.table === \"adjacency\") {\n const dropped = edgeColumns.length + (edgeId === null ? 0 : 1) + (edgeLabel === null ? 0 : 1);\n if (dropped > 0) {\n note(\n CSV_LOSS.EDGE_COLUMNS,\n `${dropped} edge column(s) are not written: an adjacency table holds the weight only`,\n null,\n dropped,\n );\n }\n }\n if (csv.table === \"edges\" && values) {\n checkRoleColumn(edgeId, \"id\", EDGE_ID_NAMES, \"edge\", edgeRows, note);\n checkRoleColumn(edgeLabel, \"label\", LABEL_NAMES, \"edge\", edgeRows, note);\n for (const { column } of edgeColumns) {\n checkValues(column, \"edge\", edgeRows, note);\n }\n }\n\n // the node table's columns\n const nodeLabel = snapshot.nodes.byRole(\"label\");\n const nodeReserved = new Set<string>([lower(csv.dialect.id)]);\n const nodeRows: number[] = [];\n if (csv.table === \"nodes\" && values) {\n for (let i = 0; i < snapshot.nodeCount; i++) {\n nodeRows.push(i);\n }\n }\n const nodeColumns = attributeColumns(\n snapshot.nodes,\n \"node\",\n nodeReserved,\n null,\n nodeLabel,\n csv.table === \"nodes\" ? note : null,\n );\n if (csv.table === \"nodes\") {\n if (values) {\n checkRoleColumn(nodeLabel, \"label\", LABEL_NAMES, \"node\", nodeRows, note);\n for (const { column } of nodeColumns) {\n checkValues(column, \"node\", nodeRows, note);\n }\n }\n } else {\n const written = nodeColumns.length + (nodeLabel === null ? 0 : 1);\n if (written > 0) {\n note(\n CSV_LOSS.NODE_TABLE,\n `${written} node column(s) are written by a table: \"nodes\" export only`,\n null,\n written,\n );\n }\n }\n\n return {\n csv,\n common,\n notes,\n edgeRows,\n folding,\n edgeId,\n edgeLabel,\n weights,\n edgeColumns,\n nodeLabel,\n nodeColumns,\n idText,\n };\n}\n\n/**\n * The edge table is not a node carrier (research note 07 section 2.6: node attributes and node\n * presence come from a separate node table): a node without an edge is not written at all, and\n * the importer creates nodes in the order the edge rows first mention them. Both are reported so\n * check() predicts the ids and the order a re-import gives back (design section 8.5).\n * @param snapshot - the snapshot\n * @param edgeRows - the edge rows written\n * @param note - the recorder\n */\nfunction noteNodeCoverage(\n snapshot: GraphSnapshot,\n edgeRows: readonly number[],\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n): void {\n const { nodeCount } = snapshot;\n if (nodeCount === 0) {\n return;\n }\n const list = snapshot.edgeList();\n const seen = new Uint8Array(nodeCount);\n let next = 0;\n let reordered = 0;\n const mention = (index: number): void => {\n if (seen[index] === 1) {\n return;\n }\n seen[index] = 1;\n if (index !== next) {\n reordered++;\n }\n next++;\n };\n for (const e of edgeRows) {\n mention(list.src[e]);\n mention(list.dst[e]);\n }\n const isolated = nodeCount - next;\n if (isolated > 0) {\n note(\n CSV_LOSS.ISOLATED_NODES,\n `${isolated} node(s) have no edge and cannot be written by the edge table; write the node table (table: \"nodes\") to keep them`,\n null,\n isolated,\n );\n }\n if (reordered > 0) {\n note(\n CSV_LOSS.NODE_ORDER,\n `${reordered} node(s) are first mentioned by an edge row out of index order; a re-import numbers nodes by first appearance`,\n null,\n reordered,\n );\n }\n}\n\n/**\n * Lower-case a header name for reserved-name comparisons.\n * @param name - the name\n * @returns the lower-case name\n */\nfunction lower(name: string): string {\n return name.toLowerCase();\n}\n\n/**\n * The attribute columns of a table that are written, in declaration order, skipping the role\n * columns handled elsewhere, the roles the format cannot hold, and names that collide with the\n * reserved headers (reported).\n * @param table - the node or edge table\n * @param domain - node or edge, for messages\n * @param reserved - lower-case header names the dialect writes for structure\n * @param idColumn - the table's id role column, or null\n * @param labelColumn - the table's label role column, or null\n * @param note - the recorder, or null when this table's notes are not wanted\n * @returns the columns and their headers\n */\nfunction attributeColumns(\n table: Iterable<Column>,\n domain: \"node\" | \"edge\",\n reserved: ReadonlySet<string>,\n idColumn: Column | null,\n labelColumn: Column | null,\n note: ((code: string, message: string, column?: string | null, count?: number | null) => void) | null,\n): ColumnOut[] {\n const out: ColumnOut[] = [];\n for (const column of table) {\n const { name, role } = column.meta;\n if (column === idColumn || column === labelColumn) {\n continue;\n }\n if (role !== null && SKIPPED_ROLES.has(role)) {\n continue;\n }\n const low = lower(name);\n const set = column.length - column.nullCount;\n const isIdName = domain === \"edge\" ? findColumn([name], EDGE_ID_NAMES) >= 0 : false;\n if (reserved.has(low) || isIdName || (idColumn !== null && lower(idColumn.meta.name) === low)) {\n note?.(\n CSV_LOSS.RESERVED_NAME,\n `${domain} column \"${name}\" is not written: the name is reserved for the ${isIdName ? \"edge id\" : low} column`,\n name,\n set,\n );\n continue;\n }\n if (findColumn([name], LABEL_NAMES) >= 0) {\n if (labelColumn !== null) {\n note?.(\n CSV_LOSS.RESERVED_NAME,\n `${domain} column \"${name}\" is not written: the name is reserved for the label column`,\n name,\n set,\n );\n continue;\n }\n note?.(\n CSV_LOSS.ROLE_ASSUMED,\n `${domain} column \"${name}\" reads back with the label role (string)`,\n name,\n set,\n );\n }\n out.push({ column, header: name });\n }\n return out;\n}\n\n/**\n * Notes about a role column (id or label): a name the importer does not map to the role, and a\n * dtype other than string / dict (read back as string).\n * @param column - the role column, or null\n * @param role - the role\n * @param names - the header names the importer maps to the role\n * @param domain - node or edge\n * @param rows - the rows written\n * @param note - the recorder\n */\nfunction checkRoleColumn(\n column: Column | null,\n role: \"id\" | \"label\",\n names: readonly string[],\n domain: \"node\" | \"edge\",\n rows: readonly number[],\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n): void {\n if (column === null) {\n return;\n }\n const { name } = column.meta;\n if (findColumn([name], names) < 0) {\n note(\n CSV_LOSS.ROLE_NAME,\n `${domain} column \"${name}\" (${role}) is written under its name, which the importer does not map to the ${role} role`,\n name,\n countSet(column, rows),\n );\n }\n if (column.dtype !== \"string\" && column.dtype !== \"dict\") {\n note(\n CSV_LOSS.TEXT_ROLE,\n `${domain} column \"${name}\" (${role}) is ${column.dtype}; it reads back as string`,\n name,\n countSet(column, rows),\n );\n }\n checkValues(column, domain, rows, note, true);\n}\n\n/**\n * Set rows among the rows written.\n * @param column - the column\n * @param rows - the rows written\n * @returns the count\n */\nfunction countSet(column: Column, rows: readonly number[]): number {\n let count = 0;\n for (const row of rows) {\n if (column.isSet(row)) {\n count++;\n }\n }\n return count;\n}\n\nconst WIDENING: Readonly<Record<TextDtype, number>> = { bool: 0, i32: 1, f64: 2, string: 3 };\nconst TEXT_DTYPES: readonly TextDtype[] = [\"bool\", \"i32\", \"f64\", \"string\"];\n\n/**\n * Value-level notes of one written column over the rows written: non-finite numbers (text on\n * re-import), a text column whose values all read back as numbers or booleans, and the dict\n * heuristic's verdict when it differs from the dtype. A set empty string is written as the quoted\n * empty cell and reads back exactly.\n * @param column - the column\n * @param domain - node or edge\n * @param rows - the rows written\n * @param note - the recorder\n * @param roleColumn - whether the column is declared string on re-import (no inference)\n */\nfunction checkValues(\n column: Column,\n domain: \"node\" | \"edge\",\n rows: readonly number[],\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n roleColumn = false,\n): void {\n const { name, dtype } = column.meta;\n const label = `${domain} column \"${name}\"`;\n if (column.dtype === \"f32\" || column.dtype === \"f64\") {\n let nonFinite = 0;\n const { components } = column.meta;\n const { data } = column;\n for (const row of rows) {\n if (!column.isSet(row)) {\n continue;\n }\n for (let k = 0; k < components; k++) {\n if (!Number.isFinite(data[row * components + k])) {\n nonFinite++;\n break;\n }\n }\n }\n if (nonFinite > 0) {\n note(CSV_LOSS.NONFINITE, `${label}: ${nonFinite} non-finite value(s) read back as text`, name, nonFinite);\n }\n return;\n }\n if (dtype !== \"string\" && dtype !== \"dict\") {\n return;\n }\n let widest = -1;\n let candidate = true;\n let set = 0;\n const heuristic = new DictHeuristic(DICT_SAMPLE_ROWS);\n for (const row of rows) {\n if (!column.isSet(row)) {\n continue;\n }\n const text = column.value(row);\n if (typeof text !== \"string\") {\n continue;\n }\n set++;\n const kind = inferTextDtype(text);\n widest = Math.max(widest, WIDENING[kind]);\n if (!heuristic.decided) {\n if (kind !== \"string\") {\n candidate = false;\n }\n heuristic.observe(text);\n }\n }\n if (roleColumn || widest < 0) {\n return;\n }\n const readsAsDict = candidate && heuristic.decide() === \"dict\";\n if (widest < WIDENING.string && !readsAsDict) {\n note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[widest]}`, name, set);\n return;\n }\n if (dtype === \"dict\" && !readsAsDict) {\n note(\n CSV_LOSS.STORAGE_CLASS_CHANGED,\n `${label}: reads back as string (cardinality too high for a dict)`,\n name,\n null,\n );\n } else if (dtype === \"string\" && readsAsDict) {\n note(CSV_LOSS.STORAGE_CLASS_CHANGED, `${label}: reads back as dict (low cardinality)`, name, null);\n }\n}\n\n/**\n * The Type cell of an edge row.\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @param e - the edge\n * @returns \"Directed\" or \"Undirected\"\n */\nfunction typeText(snapshot: GraphSnapshot, plan: Plan, e: number): string {\n if (!snapshot.directed || !plan.folding.sourceDirected(e)) {\n return \"Undirected\";\n }\n return \"Directed\";\n}\n\n/**\n * A neighbour cell of the adjacency table: `id:weight` for an explicit weight; `id:` for an id\n * holding a colon without one, so the importer does not read the id's tail as a weight; `id`\n * otherwise.\n * @param id - the neighbour's id text\n * @param weight - the weight text, or null\n * @returns the cell text (unquoted)\n */\nfunction neighbourCell(id: string, weight: string | null): string {\n if (weight !== null) {\n return `${id}:${weight}`;\n }\n return id.includes(\":\") ? `${id}:` : id;\n}\n\n/**\n * The rows of the adjacency table (design section 8.5: the order survives the importer's first-\n * appearance numbering). Consecutive edges with one source share a row, so the edge order is kept;\n * a row that would introduce nodes out of index order is preceded by one-cell rows for every node\n * up to its highest new index, and the nodes no edge names close the table the same way, so the\n * node order and the isolated nodes are kept.\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @yields one line at a time\n * @returns nothing\n */\nfunction* adjacencyLines(snapshot: GraphSnapshot, plan: Plan): Generator<string, void, undefined> {\n const { delimiter, newline } = plan.csv;\n const forced = (text: string): string => `\"${text.replace(/\"/g, '\"\"')}\"`;\n // rows vary in width by design, so a bare cell holding any delimiter the importer sniffs for\n // (a space in \"New York\") can win the sniff over the real delimiter: such a cell is quoted\n const quote = (text: string): string =>\n DELIMITER_CANDIDATES.some((c) => text.includes(c)) ? forced(text) : quoteCsvCell(text, delimiter);\n // a leading `#` or `%` would open a comment line on the first row\n const first = (text: string): string => (/^[#%]/.test(text) ? forced(text) : quote(text));\n const list = snapshot.edgeList();\n const { edgeRows, weights } = plan;\n const seen = new Uint8Array(snapshot.nodeCount);\n let next = 0;\n const advance = (): void => {\n while (next < seen.length && seen[next] === 1) {\n next++;\n }\n };\n function* singles(upTo: number): Generator<string, void, undefined> {\n for (let i = next; i <= upTo; i++) {\n if (seen[i] === 0) {\n seen[i] = 1;\n yield first(plan.idText(i)) + newline;\n }\n }\n advance();\n }\n let r = 0;\n while (r < edgeRows.length) {\n const source = list.src[edgeRows[r]];\n let end = r;\n while (end < edgeRows.length && list.src[edgeRows[end]] === source) {\n end++;\n }\n // the nodes this row introduces, in the order the importer meets them; in index order from\n // `next` they need nothing more\n const fresh: number[] = [];\n let highest = -1;\n let inOrder = true;\n const mark = (v: number): void => {\n if (seen[v] === 0) {\n seen[v] = 1;\n inOrder &&= v === next + fresh.length;\n fresh.push(v);\n highest = Math.max(highest, v);\n }\n };\n mark(source);\n for (let k = r; k < end; k++) {\n mark(list.dst[edgeRows[k]]);\n }\n if (!inOrder) {\n for (const v of fresh) {\n seen[v] = 0;\n }\n yield* singles(highest);\n }\n advance();\n const cells = [first(plan.idText(source))];\n for (let k = r; k < end; k++) {\n const e = edgeRows[k];\n cells.push(quote(neighbourCell(plan.idText(list.dst[e]), weights.weighted ? weights.text(e) : null)));\n }\n yield cells.join(delimiter) + newline;\n r = end;\n }\n yield* singles(snapshot.nodeCount - 1);\n}\n\n/**\n * The lines of the export, one string per row (terminator included).\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @yields one line at a time\n * @returns nothing\n */\nfunction* lines(snapshot: GraphSnapshot, plan: Plan): Generator<string, void, undefined> {\n const { csv } = plan;\n if (csv.table === \"adjacency\") {\n yield* adjacencyLines(snapshot, plan);\n return;\n }\n const { delimiter, newline } = csv;\n const quote = (text: string): string => quoteCsvCell(text, delimiter);\n const cell = (column: Column, row: number): string => {\n const text = cellText(column, row);\n return text === null ? \"\" : quote(text);\n };\n if (csv.table === \"nodes\") {\n const headers = [csv.dialect.id];\n if (plan.nodeLabel !== null) {\n headers.push(plan.nodeLabel.meta.name);\n }\n for (const { header } of plan.nodeColumns) {\n headers.push(header);\n }\n if (csv.header) {\n yield headers.map(quote).join(delimiter) + newline;\n }\n for (let i = 0; i < snapshot.nodeCount; i++) {\n const cells = [quote(plan.idText(i))];\n if (plan.nodeLabel !== null) {\n cells.push(cell(plan.nodeLabel, i));\n }\n for (const { column } of plan.nodeColumns) {\n cells.push(cell(column, i));\n }\n yield cells.join(delimiter) + newline;\n }\n return;\n }\n const headers = [csv.dialect.source, csv.dialect.target];\n if (csv.dialect.type !== null) {\n headers.push(csv.dialect.type);\n }\n if (plan.edgeId !== null) {\n headers.push(plan.edgeId.meta.name);\n }\n if (plan.edgeLabel !== null) {\n headers.push(plan.edgeLabel.meta.name);\n }\n if (plan.weights.weighted) {\n headers.push(csv.dialect.weight);\n }\n for (const { header } of plan.edgeColumns) {\n headers.push(header);\n }\n if (csv.header) {\n yield headers.map(quote).join(delimiter) + newline;\n }\n const list = snapshot.edgeList();\n for (const e of plan.edgeRows) {\n const cells = [quote(plan.idText(list.src[e])), quote(plan.idText(list.dst[e]))];\n if (csv.dialect.type !== null) {\n cells.push(typeText(snapshot, plan, e));\n }\n if (plan.edgeId !== null) {\n cells.push(cell(plan.edgeId, e));\n }\n if (plan.edgeLabel !== null) {\n cells.push(cell(plan.edgeLabel, e));\n }\n if (plan.weights.weighted) {\n cells.push(plan.weights.text(e) ?? \"\");\n }\n for (const { column } of plan.edgeColumns) {\n cells.push(cell(column, e));\n }\n yield cells.join(delimiter) + newline;\n }\n}\n\n/**\n * Plan an export and refuse it when a fatal note is present (E_INVALID_ID for id text collisions),\n * before anything is written.\n * @param snapshot - the snapshot\n * @param options - the caller's options\n * @returns the plan\n */\nfunction prepare(snapshot: GraphSnapshot, options: (CsvExportOptions & CommonExportOptions) | undefined): Plan {\n const plan = planExport(snapshot, options, false);\n const collision = plan.notes.find((n) => n.code === CSV_LOSS.ID_TEXT_COLLISION);\n if (collision !== undefined) {\n throw new GraphFormatError(\"E_INVALID_ID\", collision.message, {\n reason: \"collision\",\n count: collision.count,\n });\n }\n return plan;\n}\n\n/**\n * Pre-flight: what a CSV export would lose. The generic notes describe the snapshot as a whole\n * against the format; the CSV notes concern the table selected by `table`.\n * @param snapshot - the snapshot\n * @param options - CSV and common options\n * @returns the notes, empty when the export is exact\n */\nfunction check(snapshot: GraphSnapshot, options?: CsvExportOptions & CommonExportOptions): readonly LossNote[] {\n const plan = planExport(snapshot, options, true);\n return Object.freeze([\n ...checkCapabilities(snapshot, CSV_CAPABILITIES, plan.common, { roles: KEPT_ROLES }),\n ...plan.notes,\n ]);\n}\n\n/** The CSV / TSV exporter plugin (subpath `@graphty/graph-io/csv`). */\nexport const csvExporter: GraphExporter<CsvExportOptions> = Object.freeze({\n format: \"csv\",\n capabilities: CSV_CAPABILITIES,\n check,\n export(snapshot: GraphSnapshot, options?: CsvExportOptions & CommonExportOptions): AsyncIterable<Uint8Array> {\n return encodeChunks(lines(snapshot, prepare(snapshot, options)));\n },\n exportToString(snapshot: GraphSnapshot, options?: CsvExportOptions & CommonExportOptions): Promise<string> {\n return joinText(lines(snapshot, prepare(snapshot, options)));\n },\n});\n","/**\n * The CSV / TSV importer and exporter of @graphty/graph-io: the subpath entry `@graphty/graph-io/csv`.\n */\n\nimport {\n COLUMN_RENAMED_CODE,\n DIRECTION_FORCED_CODE,\n DIRECTION_REFUSED_CODE,\n ENCODING_FALLBACK_CODE,\n INVALID_ENCODING_CODE,\n INVALID_UTF8_CODE,\n MIXED_DIRECTION_CODE,\n OPTION_IGNORED_CODE,\n SINK_OPTION_CODE,\n UNKNOWN_ENCODING_CODE,\n} from \"../../common/codes.js\";\nimport { WIDENING_UNSUPPORTED_CODE } from \"../../common/text.js\";\nimport {\n BAD_TYPE_CODE,\n COLUMN_MISSING_CODE,\n DUPLICATE_EDGE_ID_CODE,\n DUPLICATE_NODE_CODE,\n EMPTY_INPUT_CODE,\n FIELD_COUNT_CODE,\n ID_MERGED_CODE,\n MISSING_ENDPOINT_CODE,\n MISSING_ID_CODE,\n NO_DATA_ROWS_CODE,\n NO_ENDPOINT_COLUMNS_CODE,\n NO_ID_COLUMN_CODE,\n ROLE_TAKEN_CODE,\n} from \"./importer.js\";\nimport { BAD_QUOTE_CODE, UNCLOSED_QUOTE_CODE } from \"./records.js\";\n\nexport { CSV_CAPABILITIES, CSV_LOSS, csvExporter, type CsvExportOptions } from \"./exporter.js\";\nexport { type CsvColumnRef } from \"./header.js\";\nexport { csvImporter, type CsvImportOptions } from \"./importer.js\";\n\n/**\n * The issue codes the CSV importer records (design section 8.6), by name: the codes shared with\n * the other importers (src/common/codes.ts) and the CSV-specific ones. A key is the code without\n * its severity and format prefixes.\n */\nexport const CSV_ISSUE = Object.freeze({\n /** The input is empty (fatal). */\n EMPTY_INPUT: EMPTY_INPUT_CODE,\n /** The input holds invalid UTF-8 (fatal). */\n INVALID_UTF8: INVALID_UTF8_CODE,\n /** Invalid bytes in the encoding a BOM, a declaration or the encoding option chose (fatal). */\n INVALID_ENCODING: INVALID_ENCODING_CODE,\n /** Bytes that are not UTF-8 and declare no encoding were read as windows-1252. */\n ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,\n /** A declared encoding the platform cannot decode was ignored. */\n UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,\n /** The header names neither endpoint columns nor an id column (fatal). */\n NO_ENDPOINT_COLUMNS: NO_ENDPOINT_COLUMNS_CODE,\n /** A node table without an id column (fatal). */\n NO_ID_COLUMN: NO_ID_COLUMN_CODE,\n /** A row with a different field count than the header. */\n FIELD_COUNT: FIELD_COUNT_CODE,\n /** An edge row with a blank source or target. */\n MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,\n /** A node row with a blank id. */\n MISSING_ID: MISSING_ID_CODE,\n /** A Type cell outside Directed / Undirected / Mutual. */\n BAD_TYPE: BAD_TYPE_CODE,\n /** An unterminated quoted field (fatal). */\n UNCLOSED_QUOTE: UNCLOSED_QUOTE_CODE,\n /** Text after a closing quote (fatal). */\n QUOTE: BAD_QUOTE_CODE,\n /** A header and no data rows. */\n NO_DATA_ROWS: NO_DATA_ROWS_CODE,\n /** A node table row repeating an id. */\n DUPLICATE_NODE: DUPLICATE_NODE_CODE,\n /** An edge row repeating an edge id (skipped). */\n DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,\n /** Two id cells merged into one number under ids \"number\". */\n ID_MERGED: ID_MERGED_CODE,\n /** An explicitly named weight column the file does not have. */\n COLUMN_MISSING: COLUMN_MISSING_CODE,\n /** A column whose role another column of the sink already holds. */\n ROLE_TAKEN: ROLE_TAKEN_CODE,\n /** A column renamed `<name>#<position>` (a repeated header, or a name the sink holds with another shape). */\n COLUMN_RENAMED: COLUMN_RENAMED_CODE,\n /** A common option the importer has no use for was given. */\n OPTION_IGNORED: OPTION_IGNORED_CODE,\n /** A builder-policy option the sink does not honour. */\n SINK_OPTION: SINK_OPTION_CODE,\n /** The sink refused the file's direction. */\n DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,\n /** Edges forced to the policy's direction. */\n DIRECTION_FORCED: DIRECTION_FORCED_CODE,\n /** A mixed file under onMixedDirection \"error\" (fatal). */\n MIXED_DIRECTION: MIXED_DIRECTION_CODE,\n /** A text column the sink could not widen to the dtype its cells imply. */\n WIDENING_UNSUPPORTED: WIDENING_UNSUPPORTED_CODE,\n});\n"],"names":["lower","SHARED_EMPTY_INPUT_CODE","SHARED_MISSING_ENDPOINT_CODE","SHARED_MISSING_ID_CODE","SHARED_DUPLICATE_NODE_CODE","SHARED_ID_MERGED_CODE","SHARED_ROLE_TAKEN_CODE","SHARED_DUPLICATE_EDGE_ID_CODE","headers"],"mappings":";;;;;;AAmBO,MAAM,eAAkC,OAAO,OAAO;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAGM,MAAM,eAAkC,OAAO,OAAO;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAGM,MAAM,WAA8B,OAAO,OAAO,CAAC,MAAM,QAAQ,QAAQ,KAAK,CAAC;AAG/E,MAAM,cAAiC,OAAO,OAAO,CAAC,OAAO,CAAC;AAG9D,MAAM,gBAAmC,OAAO,OAAO,CAAC,IAAI,CAAC;AAG7D,MAAM,YAAY;AAGzB,MAAM,qCAA0C,IAAI;AAAA,EAChD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AASM,SAAS,WAAW,OAA0B,YAAuC;AACxF,aAAW,aAAa,YAAY;AAChC,UAAM,QAAQ,MAAM,QAAQ,SAAS;AACrC,QAAI,SAAS,GAAG;AACZ,aAAO;AAAA,IACX;AACA,UAAMA,SAAQ,UAAU,YAAA;AACxB,UAAM,QAAQ,MAAM,UAAU,CAAC,SAAS,KAAK,YAAA,MAAkBA,MAAK;AACpE,QAAI,SAAS,GAAG;AACZ,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAWO,SAAS,iBAAiB,OAA0B,KAAmB,QAAwB;AAClG,MAAI,OAAO,QAAQ,UAAU;AACzB,QAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,OAAO,MAAM,QAAQ;AAC1D,YAAM,IAAI;AAAA,QACN;AAAA,QACA,UAAU,MAAM,YAAY,GAAG,iCAAiC,MAAM,MAAM;AAAA,QAC5E,EAAE,QAAQ,OAAO,KAAK,SAAS,MAAM,OAAA;AAAA,MAAO;AAAA,IAEpD;AACA,WAAO;AAAA,EACX;AACA,QAAM,QAAQ,WAAW,OAAO,CAAC,GAAG,CAAC;AACrC,MAAI,QAAQ,GAAG;AACX,UAAM,IAAI,iBAAiB,iBAAiB,UAAU,MAAM,qBAAqB,KAAK,UAAU,GAAG,CAAC,IAAI;AAAA,MACpG;AAAA,MACA,OAAO;AAAA,MACP,SAAS,CAAC,GAAG,KAAK;AAAA,IAAA,CACrB;AAAA,EACL;AACA,SAAO;AACX;AAWO,SAAS,gBAAgB,OAA0B,QAA2C;AACjG,MAAI,UAAU;AACd,aAAW,QAAQ,OAAO;AACtB,UAAM,OAAO,KAAK,KAAA;AAClB,QAAI,eAAe,IAAI,KAAK,YAAA,CAAa,GAAG;AACxC,aAAO;AAAA,IACX;AACA,QAAI,KAAK,WAAW,KAAK,eAAe,IAAI,MAAM,UAAU;AACxD,gBAAU;AAAA,IACd;AAAA,EACJ;AACA,MAAI,CAAC,WAAW,WAAW,MAAM;AAC7B,WAAO;AAAA,EACX;AACA,SAAO,OAAO,KAAK,CAAC,SAAS,KAAK,KAAA,EAAO,SAAS,KAAK,eAAe,KAAK,KAAA,CAAM,MAAM,QAAQ;AACnG;AAOO,SAAS,gBAAgB,OAAyB;AACrD,QAAM,QAAkB,CAAA;AACxB,WAAS,IAAI,GAAG,KAAK,OAAO,KAAK;AAC7B,UAAM,KAAK,SAAS,CAAC,EAAE;AAAA,EAC3B;AACA,SAAO;AACX;AAQO,SAAS,YAAY,OAAoC;AAC5D,SAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC1B,UAAM,OAAO,KAAK,KAAA;AAClB,WAAO,KAAK,WAAW,IAAI,SAAS,IAAI,CAAC,KAAK;AAAA,EAClD,CAAC;AACL;ACnJO,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyCxB,YACI,MACA,QACA,MACA,QACA,aAAqB,kBACvB;AArCF,SAAQ,SAAuB;AAG/B,SAAQ,OAAO;AAGf,SAAQ,OAAO;AAGf,SAAQ,UAAU;AAGlB,SAAQ,YAAY;AAKpB,SAAQ,SAAgC;AAExC,SAAiB,cAAwB,CAAA;AAEzC,SAAiB,eAAyB,CAAA;AAiBtC,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,YAAY,IAAI,cAAc,UAAU;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAAkB;AAClB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAa,MAAoB;AACnC,QAAI,KAAK,SAAS;AACd,WAAK,KAAK,KAAK,IAAI;AACnB;AAAA,IACJ;AACA,SAAK,YAAY,KAAK,GAAG;AACzB,SAAK,aAAa,KAAK,IAAI;AAC3B,QAAI,KAAK,aAAa,eAAe,IAAI,MAAM,UAAU;AACrD,WAAK,YAAY;AAAA,IACrB;AACA,UAAM,OAAO,KAAK,UAAU,QAAQ,IAAI;AACxC,QAAI,QAAQ,CAAC,KAAK,WAAW;AACzB,WAAK,OAAA;AAAA,IACT;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAe;AACX,QAAI,CAAC,KAAK,SAAS;AACf,WAAK,OAAA;AAAA,IACT;AACA,QAAI,KAAK,WAAW,eAAe;AAC/B,WAAK,SAAS,KAAK,OAAA;AACnB,UAAI,KAAK,WAAW,eAAe;AAC/B,aAAK,SAAS,KAAK,QAAQ,QAAQ;AAAA,MACvC;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,SAAe;AACnB,SAAK,UAAU;AACf,UAAM,WAAW,KAAK,OAAA;AACtB,QAAI,aAAa,eAAe;AAC5B,WAAK,SAAS;AACd,WAAK,OAAO,KAAK,UAAA;AAAA,IACrB,WAAW,KAAK,aAAa,KAAK,UAAU,OAAA,MAAa,QAAQ;AAC7D,WAAK,OAAO;AACZ,WAAK,OAAO;AACZ,WAAK,SAAS,KAAK,QAAQ,MAAM;AAAA,IACrC,OAAO;AACH,WAAK,SAAS,IAAI,eAAe,KAAK,MAAM,KAAK,QAAQ,KAAK,MAAM,KAAK,MAAM;AAAA,IACnF;AACA,UAAM,OAAO,KAAK;AAClB,UAAM,QAAQ,KAAK;AACnB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAI;AACA,aAAK,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,MAC/B,SAAS,KAAK;AACV,aAAK,OAAO,YAAY,KAAK,EAAE,SAAS,KAAK,MAAM;AAAA,MACvD;AAAA,IACJ;AACA,SAAK,SAAS;AACd,UAAM,SAAS;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,KAAK,KAAa,MAAoB;AAC1C,QAAI,KAAK,WAAW,MAAM;AACtB,WAAK,OAAO,MAAM,KAAK,IAAI;AAC3B,WAAK,SAAS,KAAK,OAAO;AAC1B;AAAA,IACJ;AACA,UAAM,QAAiB,KAAK,OAAO,OAAO,cAAc,IAAI;AAC5D,SAAK,IAAI,KAAK,QAAQ,KAAK,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,IAAI,QAA+B,KAAa,OAAsB;AAC1E,QAAI,KAAK,WAAW,QAAQ;AACxB,WAAK,KAAK,aAAa,QAAQ,KAAK,KAAK;AAAA,IAC7C,OAAO;AACH,WAAK,KAAK,aAAa,QAAQ,KAAK,KAAK;AAAA,IAC7C;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,SAAuB;AAC3B,WAAO,KAAK,WAAW,SAAS,KAAK,KAAK,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK,WAAW,KAAK,IAAI;AAAA,EACpG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,YAAqB;AACzB,eAAW,SAAS,CAAC,UAAU,MAAM,GAAY;AAC7C,UAAI;AACA,aAAK,QAAQ,KAAK;AAClB,eAAO;AAAA,MACX,SAAS,KAAK;AACV,YAAI,EAAE,eAAe,qBAAqB,IAAI,SAAS,mBAAmB;AACtE,gBAAM;AAAA,QACV;AAAA,MACJ;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,QAAQ,OAAwC;AACpD,UAAM,OAAO,EAAE,MAAM,KAAK,MAAM,OAAO,UAAU,MAAM,QAAQ,EAAE,QAAQ,MAAA,EAAM;AAC/E,WAAO,KAAK,WAAW,SAAS,KAAK,KAAK,kBAAkB,IAAI,IAAI,KAAK,KAAK,kBAAkB,IAAI;AAAA,EACxG;AACJ;ACpGO,MAAM,mBAAmBC;AAEzB,MAAM,2BAA2B;AAEjC,MAAM,oBAAoB;AAE1B,MAAM,mBAAmB;AAEzB,MAAM,wBAAwBC;AAE9B,MAAM,kBAAkBC;AAExB,MAAM,gBAAgB;AAEtB,MAAM,oBAAoB;AAE1B,MAAM,sBAAsBC;AAE5B,MAAM,iBAAiBC;AAEvB,MAAM,sBAAsB;AAE5B,MAAM,kBAAkBC;AAExB,MAAM,yBAAyBC;AAEtC,MAAM,kCAAuC,IAAI,CAAC,SAAS,SAAS,aAAa,MAAM,CAAC;AAGxF,MAAM,mCAA2D,IAA+B;AAAA,EAC5F;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAGD,MAAM,8CAAsE,IAA+B;AAAA,EACvG,GAAG;AAAA,EACH;AACJ,CAAC;AACD,MAAM,iBAAsC,oBAAI,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC;AAoErE,MAAM,gBAAmC,OAAO,OAAO,CAAC,KAAK,GAAG,CAAC;AASjE,SAAS,iBAAiB,UAA6C;AACnE,aAAW,WAAW,UAAU;AAC5B,UAAM,OAAO,QAAQ,MAAM,CAAC,EAAE,KAAA,EAAO,YAAA;AACrC,QAAI,KAAK,WAAW,gBAAgB,KAAK,KAAK,WAAW,MAAM,GAAG;AAC9D,aAAO;AAAA,IACX;AACA,QAAI,KAAK,WAAW,kBAAkB,KAAK,KAAK,WAAW,KAAK,KAAK,KAAK,WAAW,KAAK,GAAG;AACzF,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAOA,SAAS,kBAAkB,SAAmF;AAC1G,QAAM,IAAsB,WAAW,CAAA;AACvC,MAAI,EAAE,cAAc,WAAc,OAAO,EAAE,cAAc,YAAY,EAAE,UAAU,WAAW,IAAI;AAC5F,UAAM,IAAI,iBAAiB,iBAAiB,iDAAiD;AAAA,MACzF,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MAAI,EAAE,cAAc,UAAa,eAAe,IAAI,EAAE,SAAS,GAAG;AAC9D,UAAM,IAAI,iBAAiB,iBAAiB,4DAA4D;AAAA,MACpG,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MAAI,EAAE,WAAW,UAAa,EAAE,WAAW,UAAU,OAAO,EAAE,WAAW,WAAW;AAChF,UAAM,IAAI,iBAAiB,iBAAiB,iDAAiD;AAAA,MACzF,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MAAI,EAAE,UAAU,UAAa,CAAC,YAAY,IAAI,EAAE,KAAK,GAAG;AACpD,UAAM,IAAI,iBAAiB,iBAAiB,kEAAkE;AAAA,MAC1G,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,aAAW,QAAQ,CAAC,gBAAgB,gBAAgB,UAAU,GAAY;AACtE,mBAAe,MAAM,EAAE,IAAI,CAAC;AAAA,EAChC;AACA,MAAI,EAAE,eAAe,MAAM;AACvB,mBAAe,cAAc,EAAE,UAAU;AAAA,EAC7C;AACA,MAAI,EAAE,UAAU,aAAa;AACzB,eAAW,QAAQ,CAAC,gBAAgB,gBAAgB,cAAc,UAAU,GAAY;AACpF,UAAI,EAAE,IAAI,MAAM,QAAW;AACvB,cAAM,IAAI,iBAAiB,iBAAiB,UAAU,IAAI,uCAAuC;AAAA,UAC7F,QAAQ;AAAA,UACR,OAAO,EAAE,IAAI;AAAA,QAAA,CAChB;AAAA,MACL;AAAA,IACJ;AACA,QAAI,EAAE,iBAAiB,MAAM;AACzB,YAAM,IAAI,iBAAiB,iBAAiB,2DAA2D;AAAA,QACnG,QAAQ;AAAA,QACR,OAAO,EAAE;AAAA,MAAA,CACZ;AAAA,IACL;AAAA,EACJ;AACA,MAAI,EAAE,iBAAiB,UAAa,OAAO,EAAE,iBAAiB,WAAW;AACrE,UAAM,IAAI,iBAAiB,iBAAiB,2CAA2C;AAAA,MACnF,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,SAAO;AAAA,IACH,WAAW,EAAE,aAAa;AAAA,IAC1B,QAAQ,EAAE,UAAU;AAAA,IACpB,OAAO,EAAE,SAAS;AAAA,IAClB,cAAc,EAAE,gBAAgB;AAAA,IAChC,cAAc,EAAE,gBAAgB;AAAA,IAChC,YAAY,EAAE;AAAA,IACd,UAAU,EAAE,YAAY;AAAA,IACxB,OAAO,EAAE,SAAS;AAAA,IAClB,cAAc,EAAE,gBAAgB;AAAA,EAAA;AAExC;AAOA,SAAS,eAAe,MAAc,OAAsB;AACxD,MAAI,UAAU,QAAW;AACrB;AAAA,EACJ;AACA,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AAC/C;AAAA,EACJ;AACA,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS,GAAG;AACpE;AAAA,EACJ;AACA,QAAM,IAAI,iBAAiB,iBAAiB,UAAU,IAAI,kDAAkD;AAAA,IACxG,QAAQ;AAAA,IACR,OAAO;AAAA,EAAA,CACV;AACL;AAOA,SAAS,QAAQ,MAAuB;AACpC,SAAO,KAAK,WAAW,KAAK,KAAK,KAAA,EAAO,WAAW;AACvD;AAUA,SAAS,QAAQ,MAAc,QAAsC;AACjE,SAAO,WAAW,QAAQ,QAAQ,IAAI;AAC1C;AAGA,MAAM,uBAAuB;AAU7B,SAAS,YAAY,OAA0B,QAA6B,MAAwB;AAChG,QAAM,2BAAW,IAAA;AACjB,SAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC1B,UAAM,SAAS,iBAAiB,MAAM,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;AACvE,SAAK,IAAI,MAAM;AACf,QAAI,WAAW,MAAM;AACjB,aAAO;AAAA,QACH;AAAA,QACA;AAAA,QACA,UAAU,IAAI,CAAC,KAAK,IAAI,iBAAiB,MAAM;AAAA,QAC/C,EAAE,MAAM,SAAS,KAAA;AAAA,MAAK;AAAA,IAE9B;AACA,WAAO;AAAA,EACX,CAAC;AACL;AASA,SAAS,SAAS,OAA0B,YAA+B,SAAsC;AAC7G,QAAM,SAAS,MAAM,IAAI,CAAC,MAAM,MAAO,QAAQ,IAAI,CAAC,IAAI,KAAK,IAAK;AAClE,SAAO,WAAW,QAAQ,UAAU;AACxC;AAeA,SAAS,kBACL,MACA,QACA,MACA,UACA,QACA,MACY;AACZ,QAAM,aAAyB,EAAE,GAAG,MAAM,QAAQ,EAAE,GAAG,KAAK,QAAQ,IAAI,OAAO,QAAQ,IAAE;AACzF,QAAM,WAAW,gBAAgB,MAAM,QAAQ,YAAY,QAAQ,EAAE,MAAM,SAAS,KAAK,KAAA,CAAM;AAC/F,MAAI,SAAS,aAAa;AAEtB,WAAO,SAAS;AAAA,EACpB;AACA,SAAO,SAAS;AACpB;AAOA,SAAS,UAAU,MAA2C;AAC1D,MAAI,QAAQ,IAAI,GAAG;AACf,WAAO;AAAA,EACX;AACA,UAAQ,KAAK,KAAA,EAAO,YAAA,GAAY;AAAA,IAC5B,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB;AASO,SAAS,eAAe,MAA4E;AACvG,QAAM,QAAQ,KAAK,YAAY,GAAG;AAClC,MAAI,QAAQ,GAAG;AACX,WAAO,EAAE,IAAI,MAAM,QAAQ,OAAA;AAAA,EAC/B;AACA,QAAM,SAAS,KAAK,MAAM,QAAQ,CAAC;AACnC,MAAI,OAAO,WAAW,GAAG;AACrB,WAAO,EAAE,IAAI,KAAK,MAAM,GAAG,KAAK,GAAG,QAAQ,OAAA;AAAA,EAC/C;AACA,QAAM,SAAS,aAAa,MAAM;AAClC,SAAO,WAAW,OAAO,EAAE,IAAI,MAAM,QAAQ,OAAA,IAAc,EAAE,IAAI,KAAK,MAAM,GAAG,KAAK,GAAG,OAAA;AAC3F;AAOA,SAAS,aAAa,MAA6B;AAC/C,MAAI;AACA,WAAO,gBAAgB,IAAI,KAAK;AAAA,EACpC,QAAQ;AAEJ,WAAO;AAAA,EACX;AACJ;AAKA,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCd,YACI,OACA,OACA,MACA,UACF;AA7BF,SAAQ,OAAmD;AAE3D,SAAQ,UAAqC,CAAA;AAE7C,SAAQ,WAAyB;AAEjC,SAAQ,cAA4B;AAEpC,SAAQ,WAAW;AAEnB,SAAQ,cAAc;AAGtB,SAAiB,8BAAc,IAAA;AAE/B,SAAiB,QAAyD,EAAE,MAAM,MAAM,SAAS,KAAA;AAe7F,SAAK,QAAQ;AACb,SAAK,OAAO;AACZ,SAAK,aAAa,MAAM,IAAI,iBAAiB,SAAS,WAAY,SAAS,UAAU,MAAM,IAAI,UAAU;AACzG,UAAM,gBAAkC;AAAA,MACpC,WAAW,MAAM,IAAI;AAAA,MACrB,UAAU;AAAA;AAAA,MAEV,iBAAiB,SAAS;AAAA,MAC1B,QAAQ,MAAM,OAAO;AAAA,MACrB,YAAY,WAAW,MAAM,OAAO,aAAa;AAAA,MACjD,UAAU,MAAM,OAAO;AAAA,IAAA;AAE3B,SAAK,SAAS,IAAI,gBAAgB,OAAO,MAAM,QAAQ,aAAa;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AACxB,UAAM,WAAW,KAAK,OAAO,OAAO,aAAa,EAAA;AACjD,QAAI;AACA,YAAM,KAAK,SAAS,QAAQ;AAAA,IAChC,UAAA;AACI,YAAM,SAAS,OAAO,MAAS;AAAA,IACnC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,SAAS,UAAoE;AACvF,UAAM,EAAE,WAAW,KAAK;AACxB,UAAM,QAAQ,MAAM,SAAS,KAAA;AAC7B,QAAI,MAAM,QAAQ,KAAK,SAAS,aAAa;AAEzC;AAAA,IACJ;AACA,UAAM,WAAqB,MAAM,OAC3B,OAAO,KAAK,kBAAkB,kDAAkD,IAChF,MAAM;AACZ,UAAM,YAAY,KAAK,OAAO;AAC9B,UAAM,cAAc,KAAK,OAAO,OAAO,MAAM,GAAG,SAAS,MAAM;AAC/D,UAAM,UAAyE,CAAA;AAC/E,QAAI;AAGJ,QAAI,OAAO,KAAK,MAAM,IAAI;AAC1B,QAAI,SAAS,UAAU,KAAK,SAAS,aAAa;AAC9C,aAAO;AAAA,IACX,WAAW,SAAS,UAAU,KAAK,YAAY;AAC3C,aAAO;AAAA,IACX;AACA,QAAI,SAAS,QAAQ;AACjB,YAAM,SAAS,MAAM,SAAS,KAAA;AAC9B,YAAM,YAA6B,OAAO,OAAO,OAAO,OAAO;AAC/D,YAAM,aAAa,KAAK,OAAO;AAC/B,YAAM,eAAe,KAAK,OAAO,OAAO,MAAM,GAAG,WAAW,UAAU,CAAC;AACvE,eAAS,gBAAgB,UAAU,SAAS;AAC5C,UAAI,CAAC,QAAQ;AACT,gBAAQ,KAAK,EAAE,KAAK,UAAU,QAAQ,aAAa,MAAM,WAAW;AAAA,MACxE;AACA,UAAI,cAAc,MAAM;AACpB,gBAAQ,KAAK,EAAE,KAAK,WAAW,QAAQ,cAAc,MAAM,YAAY;AAAA,MAC3E;AAAA,IACJ,OAAO;AACH,eAAS;AACT,UAAI,CAAC,QAAQ;AACT,gBAAQ,KAAK,EAAE,KAAK,UAAU,QAAQ,aAAa,MAAM,WAAW;AAAA,MACxE;AAAA,IACJ;AACA,UAAM,QAAQ,SAAS,YAAY,YAAY,QAAQ,GAAG,QAAQ,SAAS,IAAI,gBAAgB,SAAS,MAAM;AAC9G,QAAI,KAAK,SAAS,WAAW,KAAK,MAAM,oBAAoB,MAAM;AAC9D,WAAK,MAAM,kBAAkB,iBAAiB,KAAK,OAAO,eAAe;AAAA,IAC7E;AACA,SAAK,OAAO,KAAK,YAAY,OAAO,QAAQ,SAAS;AACrD,SAAK,eAAe,SAAS;AAC7B,eAAW,EAAE,KAAK,QAAQ,KAAA,KAAU,SAAS;AACzC,WAAK,WAAW,KAAK,QAAQ,IAAI;AAAA,IACrC;AACA,UAAM,EAAE,OAAA,IAAW,KAAK,MAAM;AAC9B,QAAI,aAAa;AACjB,eAAS;AACL,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,UAAI,KAAK,MAAM;AACX;AAAA,MACJ;AACA,WAAK,WAAW,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO,IAAI;AAChE,UAAI,EAAE,cAAc,sBAAsB;AACtC,qBAAa;AACb,uBAAe,MAAM;AAAA,MACzB;AAAA,IACJ;AACA,eAAW,UAAU,KAAK,SAAS;AAC/B,cAAQ,OAAA;AAAA,IACZ;AACA,QAAI,KAAK,aAAa,KAAK,QAAQ;AAC/B,aAAO,QAAQ,iBAAiB,mBAAmB,2CAA2C;AAAA,QAC1F,MAAM;AAAA,MAAA,CACT;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,YAAY,OAA0B,QAAiB,MAAmD;AAC9G,UAAM,EAAE,KAAK,OAAA,IAAW,KAAK;AAC7B,QAAI,KAAK,SAAS,aAAa;AAC3B,aAAO,EAAE,MAAM,aAAa,OAAO,CAAA,EAAC;AAAA,IACxC;AACA,UAAM,QAAQ,MAAM;AACpB,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,KAAK,SAAS,SAAS;AACvB,UAAI,IAAI,iBAAiB,MAAM;AAC3B,iBAAS,iBAAiB,OAAO,IAAI,cAAc,cAAc;AAAA,MACrE,WAAW,QAAQ;AACf,iBAAS,WAAW,OAAO,YAAY;AAAA,MAC3C,OAAO;AACH,iBAAS,SAAS,IAAI,IAAI;AAAA,MAC9B;AACA,UAAI,IAAI,iBAAiB,MAAM;AAC3B,iBAAS,iBAAiB,OAAO,IAAI,cAAc,cAAc;AAAA,MACrE,WAAW,QAAQ;AACf,iBAAS,WAAW,OAAO,YAAY;AAAA,MAC3C,OAAO;AACH,iBAAS,SAAS,IAAI,IAAI;AAAA,MAC9B;AACA,UAAI,UAAU,KAAK,UAAU,KAAK,WAAW,QAAQ;AACjD,cAAM,IAAI,iBAAiB,iBAAiB,sDAAsD;AAAA,UAC9F,QAAQ;AAAA,UACR,OAAO,MAAM,MAAM;AAAA,QAAA,CACtB;AAAA,MACL;AAAA,IACJ;AACA,QAAI,UAAU,KAAK,UAAU,GAAG;AAC5B,aAAO,KAAK,SAAS,OAAO,QAAQ,QAAQ,MAAM;AAAA,IACtD;AACA,UAAM,QAAQ,MAAM,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI;AAC3D,QACI,KAAK,SAAS,WACb,KAAK,SAAS,WAAW,IAAI,iBAAiB,QAAQ,IAAI,iBAAiB,OAC9E;AACE,aAAO;AAAA,QACH;AAAA,QACA,6CAA6C,KAAK;AAAA,QAClD,EAAE,KAAA;AAAA,QACF,EAAE,SAAS,CAAC,GAAG,KAAK,EAAA;AAAA,MAAE;AAAA,IAE9B;AACA,UAAM,aAAa,SAAS,WAAW,OAAO,QAAQ,KAAK,IAAI,SAAS;AACxE,QAAI,KAAK,SAAS,UAAU,IAAI,aAAa,QAAQ,CAAC,cAAc,CAAC,KAAK,YAAY;AAClF,aAAO;AAAA,QACH;AAAA,QACA,8DAA8D,KAAK;AAAA,QACnE,EAAE,KAAA;AAAA,QACF,EAAE,SAAS,CAAC,GAAG,KAAK,EAAA;AAAA,MAAE;AAAA,IAE9B;AACA,WAAO,KAAK,SAAS,OAAO,QAAQ,IAAI;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,SAAS,OAA0B,QAAiB,QAAgB,QAA0B;AAClG,UAAM,EAAE,KAAK,QAAQ,OAAA,IAAW,KAAK;AACrC,UAAM,UAAU,oBAAI,IAAY,CAAC,QAAQ,MAAM,CAAC;AAChD,QAAI,SAAS;AACb,QAAI,OAAO,eAAe,MAAM;AAC5B,UAAI,QAAQ;AACR,iBAAS,SAAS,OAAO,CAAC,OAAO,UAAU,GAAG,OAAO;AACrD,YAAI,SAAS,KAAK,KAAK,MAAM,oBAAoB;AAC7C,iBAAO;AAAA,YACH;AAAA,YACA;AAAA,YACA,iBAAiB,KAAK,UAAU,OAAO,UAAU,CAAC;AAAA,YAClD,EAAE,MAAM,KAAK,OAAO,MAAM,SAAS,OAAO,WAAA;AAAA,UAAW;AAAA,QAE7D;AAAA,MACJ,WAAW,MAAM,UAAU,KAAK,CAAC,QAAQ,IAAI,CAAC,GAAG;AAC7C,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,QAAI,UAAU,GAAG;AACb,cAAQ,IAAI,MAAM;AAAA,IACtB;AACA,QAAI,OAAO;AACX,QAAI,IAAI,eAAe,QAAW;AAC9B,UAAI,UAAU,MAAM,MAAM,MAAM,YAAY,MAAM,MAAM,MAAM,UAAU;AACpE,eAAO,SAAS,OAAO,CAAC,SAAS,GAAG,OAAO;AAC3C,YAAI,QAAQ,KAAK,MAAM,IAAI,MAAM,WAAW;AACxC,iBAAO;AAAA,QACX;AAAA,MACJ;AAAA,IACJ,WAAW,IAAI,eAAe,MAAM;AAChC,aAAO,iBAAiB,OAAO,IAAI,YAAY,YAAY;AAC3D,UAAI,QAAQ,IAAI,IAAI,GAAG;AACnB,cAAM,IAAI,iBAAiB,iBAAiB,iDAAiD;AAAA,UACzF,QAAQ;AAAA,UACR,OAAO,MAAM,IAAI;AAAA,QAAA,CACpB;AAAA,MACL;AAAA,IACJ;AACA,QAAI,QAAQ,GAAG;AACX,cAAQ,IAAI,IAAI;AAAA,IACpB;AACA,UAAM,KAAK,SAAS,SAAS,OAAO,eAAe,OAAO,IAAI;AAC9D,QAAI,MAAM,GAAG;AACT,cAAQ,IAAI,EAAE;AAAA,IAClB;AACA,UAAM,QAAQ,SAAS,SAAS,OAAO,aAAa,OAAO,IAAI;AAC/D,QAAI,SAAS,GAAG;AACZ,cAAQ,IAAI,KAAK;AAAA,IACrB;AACA,UAAM,aAAuB,CAAA;AAC7B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,UAAI,CAAC,QAAQ,IAAI,CAAC,GAAG;AACjB,mBAAW,KAAK,CAAC;AAAA,MACrB;AAAA,IACJ;AACA,WAAO,EAAE,MAAM,SAAS,OAAO,OAAO,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,IAAI,OAAO,WAAA;AAAA,EACjG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,SAAS,OAA0B,QAAiB,MAAwB;AAChF,UAAM,EAAE,KAAK,QAAQ,OAAA,IAAW,KAAK;AACrC,UAAM,8BAAc,IAAA;AACpB,QAAI,WAAW;AACf,QAAI,IAAI,aAAa,MAAM;AACvB,iBAAW,iBAAiB,OAAO,IAAI,UAAU,UAAU;AAAA,IAC/D,WAAW,QAAQ;AACf,iBAAW,WAAW,OAAO,QAAQ;AAAA,IACzC,WAAW,MAAM,UAAU,GAAG;AAC1B,iBAAW;AAAA,IACf;AACA,UAAM,QAAQ,SAAS,SAAS,OAAO,aAAa,IAAI,IAAI,YAAY,IAAI,CAAC,QAAQ,IAAI,CAAA,CAAE,CAAC,IAAI;AAChG,QAAI;AACJ,YAAQ,OAAO,YAAA;AAAA,MACX,KAAK;AACD,YAAI,QAAQ,GAAG;AACX,iBAAO,KAAK,mBAAmB,gEAAgE;AAAA,YAC3F;AAAA,UAAA,CACH;AAAA,QACL;AACA,aAAK;AACL;AAAA,MACJ,KAAK;AACD,aAAK;AACL;AAAA,MACJ;AACI,YAAI,WAAW,KAAK,KAAK,YAAY;AACjC,eAAK;AACL;AAAA,QACJ;AACA,YAAI,WAAW,GAAG;AACd,iBAAO;AAAA,YACH;AAAA,YACA,0CAA0C,MAAM,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,YACxF,EAAE,KAAA;AAAA,YACF,EAAE,SAAS,CAAC,GAAG,KAAK,EAAA;AAAA,UAAE;AAAA,QAE9B;AACA,aAAK;AACL,gBAAQ,IAAI,QAAQ;AACpB;AAAA,IAAA;AAER,QAAI,SAAS,GAAG;AACZ,cAAQ,IAAI,KAAK;AAAA,IACrB;AACA,UAAM,aAAuB,CAAA;AAC7B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,UAAI,CAAC,QAAQ,IAAI,CAAC,GAAG;AACjB,mBAAW,KAAK,CAAC;AAAA,MACrB;AAAA,IACJ;AACA,UAAM,YAAY,KAAK,KAAK,OAAO,eAAe;AAClD,WAAO,EAAE,MAAM,SAAS,OAAO,OAAO,MAAM,QAAQ,IAAI,WAAW,OAAO,WAAA;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,MAAoB;AACvC,UAAM,OAAO,KAAK,YAAA;AAClB,QAAI,KAAK,SAAS,aAAa;AAC3B;AAAA,IACJ;AACA,UAAM,EAAE,MAAM,OAAA,IAAW,KAAK;AAC9B,UAAM,SAAS,KAAK,SAAS,UAAU,SAAS;AAChD,UAAM,SAAS,EAAE,QAAQ,MAAA;AACzB,QAAI,KAAK,SAAS,WAAW,KAAK,MAAM,GAAG;AACvC,WAAK,WAAW;AAAA,QACZ;AAAA,QACA;AAAA,QACA,EAAE,MAAM,KAAK,MAAM,KAAK,EAAE,GAAG,OAAO,UAAU,UAAU,MAAM,MAAM,MAAM,QAAQ,MAAM,OAAA;AAAA,QACxF,KAAK,KAAK;AAAA,QACV;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AACA,QAAI,KAAK,SAAS,GAAG;AACjB,WAAK,cAAc;AAAA,QACf;AAAA,QACA;AAAA,QACA,EAAE,MAAM,KAAK,MAAM,KAAK,KAAK,GAAG,OAAO,UAAU,UAAU,MAAM,MAAM,SAAS,OAAA;AAAA,QAChF,KAAK,QAAQ;AAAA,QACb;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AACA,SAAK,UAAU,KAAK,MAAM,IAAI,MAAM,IAAI;AACxC,eAAW,SAAS,KAAK,YAAY;AACjC,WAAK,QAAQ,KAAK,IAAI,IAAI,eAAe,KAAK,MAAM,KAAK,GAAG,QAAQ,MAAM,MAAM;AAAA,IACpF;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAmD;AACvD,QAAI,KAAK,SAAS,MAAM;AACpB,YAAM,IAAI,iBAAiB,iBAAiB,gCAAgC,EAAE,QAAQ,WAAW;AAAA,IACrG;AACA,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAW,KAAe,QAA4B,MAAoB;AAC9E,UAAM,OAAO,KAAK,YAAA;AAClB,SAAK;AACL,QAAI,KAAK,SAAS,SAAS;AACvB,WAAK,eAAe,MAAM,KAAK,QAAQ,IAAI;AAAA,IAC/C,WAAW,KAAK,SAAS,SAAS;AAC9B,WAAK,eAAe,MAAM,KAAK,QAAQ,IAAI;AAAA,IAC/C,OAAO;AACH,WAAK,oBAAoB,KAAK,QAAQ,IAAI;AAAA,IAC9C;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBAAoB,KAAe,QAA4B,MAAoB;AACvF,UAAM,EAAE,QAAQ,MAAM,UAAU,OAAA,IAAW,KAAK;AAChD,UAAM,EAAE,WAAW;AACnB,UAAM,EAAE,UAAU;AAClB,UAAM,OAAO;AACb,UAAM,UAAU;AAChB,QAAI,aAAa;AACjB,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,UAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG;AAC7B;AAAA,MACJ;AAAA,IACJ;AACA,QAAI,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG;AAC5B,aAAO,MAAM,iBAAiB,iBAAiB,QAAQ,IAAI,qBAAqB,EAAE,MAAM;AACxF,aAAO;AACP,aAAO,gBAAgB;AACvB;AAAA,IACJ;AACA,UAAM,OAAkB,KAAK,MAAM,mBAAmB,OAAO,kBAAmB,aAAa;AAC7F,QAAI,CAAC,KAAK,MAAM,WAAW;AACvB,WAAK,MAAM,YAAY;AACvB,eAAS,UAAU,SAAS,cAAc,KAAK;AAAA,IACnD;AACA,QAAI;AACJ,QAAI;AACA,YAAM,UAAU,IAAI,CAAC;AACrB,eAAS,KAAK,OAAO,IAAI,CAAC,CAAC;AAC3B,UAAI,KAAK,QAAQ,MAAM,MAAM,eAAe;AACxC,eAAO;AAAA,MACX;AACA,WAAK,QAAQ,MAAM;AAAA,IACvB,SAAS,KAAK;AACV,aAAO,YAAY,KAAK,KAAK;AAC7B,aAAO;AACP,aAAO,gBAAgB;AACvB;AAAA,IACJ;AACA,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,UAAI,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG;AAC5B;AAAA,MACJ;AACA,YAAM,UAAU,IAAI,CAAC;AACrB,UAAI;AACA,cAAM,OAAO,eAAe,IAAI,CAAC,CAAC;AAClC,cAAM,SAAS,KAAK,OAAO,KAAK,EAAE;AAClC,cAAM,YAAY,KAAK,QAAQ,MAAM,MAAM;AAC3C,cAAM,SAAS,KAAK;AACpB,iBAAS,QAAQ,QAAQ,QAAQ,MAAM,OAAO,eAAe,OAAO,SAAY,KAAK,QAAQ,KAAK;AAClG,eAAO,SAAS,KAAK,YAAY;AACjC,eAAO,SAAS,YAAY,IAAI;AAAA,MACpC,SAAS,KAAK;AACV,eAAO,YAAY,KAAK,KAAK;AAC7B,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,eAAe,MAAgB,KAAe,QAA4B,MAAoB;AAClG,UAAM,EAAE,QAAQ,MAAM,UAAU,OAAA,IAAW,KAAK;AAChD,UAAM,EAAE,WAAW;AACnB,QAAI,IAAI,WAAW,KAAK,OAAO;AAC3B,aAAO;AAAA,QACH;AAAA,QACA;AAAA,QACA,QAAQ,IAAI,KAAK,IAAI,MAAM,6BAA6B,KAAK,KAAK;AAAA,QAClE,EAAE,KAAA;AAAA,MAAK;AAEX,aAAO;AACP;AAAA,IACJ;AACA,UAAM,aAAa,IAAI,KAAK,MAAM;AAClC,UAAM,aAAa,IAAI,KAAK,MAAM;AAClC,UAAM,gBAAgB,QAAQ,YAAY,OAAO,KAAK,MAAM,CAAC;AAC7D,QAAI,iBAAiB,QAAQ,YAAY,OAAO,KAAK,MAAM,CAAC,GAAG;AAC3D,aAAO;AAAA,QACH;AAAA,QACA;AAAA,QACA,QAAQ,IAAI,WAAW,gBAAgB,WAAW,QAAQ;AAAA,QAC1D,EAAE,KAAA;AAAA,MAAK;AAEX,aAAO;AACP;AAAA,IACJ;AACA,QAAI,OAAkB,KAAK,MAAM,mBAAmB,OAAO,kBAAmB,aAAa;AAC3F,QAAI,KAAK,QAAQ,GAAG;AAChB,YAAM,SAAS,UAAU,IAAI,KAAK,IAAI,CAAC;AACvC,UAAI,WAAW,MAAM;AACjB,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA,QAAQ,IAAI,UAAU,KAAK,UAAU,IAAI,KAAK,IAAI,CAAC,CAAC;AAAA,UACpD,EAAE,KAAA;AAAA,QAAK;AAEX,eAAO;AACP;AAAA,MACJ;AACA,UAAI,WAAW,QAAW;AACtB,eAAO;AAAA,MACX;AAAA,IACJ;AACA,UAAM,EAAE,UAAU;AAClB,UAAM,OAAO;AACb,UAAM,UAAU;AAChB,UAAM,SAAS,KAAK,MAAM,KAAK,CAAC,QAAQ,IAAI,KAAK,EAAE,GAAG,OAAO,KAAK,EAAE,CAAC,IAAI,IAAI,KAAK,EAAE,IAAI;AACxF,QAAI,WAAW,MAAM;AACjB,UAAI,KAAK,QAAQ,IAAI,MAAM,GAAG;AAC1B,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA,QAAQ,IAAI,aAAa,KAAK,UAAU,MAAM,CAAC;AAAA,UAC/C,EAAE,MAAM,SAAS,OAAA;AAAA,QAAO;AAE5B,eAAO;AACP;AAAA,MACJ;AACA,WAAK,QAAQ,IAAI,MAAM;AAAA,IAC3B;AACA,QAAI;AACJ,QAAI;AACA,YAAM,SAAS,KAAK,OAAO,UAAU;AACrC,YAAM,SAAS,KAAK,OAAO,UAAU;AACrC,YAAM,SAAS,KAAK,UAAU,IAAI,gBAAgB,IAAI,KAAK,MAAM,CAAC,IAAI;AACtE,UAAI,CAAC,KAAK,MAAM,WAAW;AACvB,aAAK,MAAM,YAAY;AACvB,iBAAS,UAAU,SAAS,cAAc,KAAK;AAAA,MACnD;AACA,YAAM,YAAY,KAAK,QAAQ,MAAM,MAAM;AAC3C,YAAM,YAAY,WAAW,UAAU,KAAK,QAAQ,MAAM,MAAM;AAChE,YAAM,SAAS,KAAK;AACpB,aAAO,SAAS,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,KAAK;AAC3D,aAAO,SAAS,KAAK,YAAY;AACjC,aAAO,UAAU,YAAY,IAAI,MAAM,YAAY,IAAI;AAAA,IAC3D,SAAS,KAAK;AACV,YAAM,UAAU,UAAU,GAAG,UAAU,KAAK,UAAU;AACtD,aAAO,YAAY,KAAK,KAAK;AAC7B,aAAO;AACP;AAAA,IACJ;AACA,QAAI,WAAW,MAAM;AACjB,WAAK,UAAU,KAAK,UAAU,QAAQ,MAAM,QAAQ,KAAK,MAAM,KAAK,EAAE,GAAG,IAAI;AAAA,IACjF;AACA,QAAI,KAAK,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,KAAK,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG;AAClE,WAAK,UAAU,KAAK,aAAa,QAAQ,MAAM,IAAI,KAAK,KAAK,GAAG,KAAK,MAAM,KAAK,KAAK,GAAG,IAAI;AAAA,IAChG;AACA,SAAK,gBAAgB,MAAM,KAAK,QAAQ,MAAM,IAAI;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,eAAe,MAAgB,KAAe,QAA4B,MAAoB;AAClG,UAAM,EAAE,QAAQ,KAAA,IAAS,KAAK;AAC9B,UAAM,EAAE,WAAW;AACnB,UAAM,UAAU,KAAK;AACrB,QAAI,IAAI,WAAW,KAAK,OAAO;AAC3B,aAAO;AAAA,QACH;AAAA,QACA;AAAA,QACA,QAAQ,IAAI,KAAK,IAAI,MAAM,6BAA6B,KAAK,KAAK;AAAA,QAClE,EAAE,KAAA;AAAA,MAAK;AAEX,aAAO;AACP;AAAA,IACJ;AACA,UAAM,SAAS,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,IAAI,OAAO,OAAO;AAC3D,QAAI,KAAK,MAAM,KAAK,QAAQ,QAAQ,OAAO,KAAK,EAAE,CAAC,GAAG;AAClD,aAAO,MAAM,iBAAiB,iBAAiB,QAAQ,IAAI,mBAAmB,EAAE,MAAM;AACtF,aAAO;AACP;AAAA,IACJ;AACA,UAAM,EAAE,UAAU;AAClB,UAAM,OAAO;AACb,UAAM,UAAU;AAChB,QAAI;AACJ,QAAI;AACA,YAAM,KAAK,KAAK,MAAM,KAAK,KAAK,YAAY,KAAK,OAAO,MAAM,IAAI;AAClE,UAAI,KAAK,QAAQ,EAAE,MAAM,eAAe;AACpC,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA,QAAQ,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;AAAA,UACxC;AAAA,QAAA;AAAA,MAER,OAAO;AACH,eAAO;AAAA,MACX;AACA,cAAQ,KAAK,QAAQ,EAAE;AAAA,IAC3B,SAAS,KAAK;AACV,aAAO,YAAY,KAAK,KAAK;AAC7B,aAAO;AACP;AAAA,IACJ;AACA,QAAI,KAAK,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,KAAK,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG;AAClE,WAAK,UAAU,KAAK,aAAa,QAAQ,OAAO,IAAI,KAAK,KAAK,GAAG,KAAK,MAAM,KAAK,KAAK,GAAG,IAAI;AAAA,IACjG;AACA,SAAK,gBAAgB,MAAM,KAAK,QAAQ,OAAO,IAAI;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,OAAO,MAAsB;AACjC,UAAM,KAAK,KAAK,MAAM,QAAQ,KAAK,IAAI;AACvC,UAAM,QAAQ,KAAK,MAAM,QAAQ;AACjC,QAAI,UAAU,MAAM;AAChB,WAAK,MAAM,OAAO;AAAA,QACd;AAAA,QACA;AAAA,QACA,MAAM,KAAK,UAAU,MAAM,IAAI,CAAC,gBAAgB,KAAK,UAAU,MAAM,YAAY,CAAC,OAAO,MAAM,EAAE;AAAA,QACjG,KAAK;AAAA,MAAA;AAAA,IAEb;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,UACJ,QACA,QACA,KACA,MACA,MACA,MACI;AACJ,QAAI;AACA,UAAI,WAAW,QAAQ;AACnB,aAAK,MAAM,KAAK,aAAa,QAAQ,KAAK,IAAI;AAAA,MAClD,OAAO;AACH,aAAK,MAAM,KAAK,aAAa,QAAQ,KAAK,IAAI;AAAA,MAClD;AAAA,IACJ,SAAS,KAAK;AACV,WAAK,MAAM,OAAO,YAAY,KAAK,EAAE,MAAM,SAAS,MAAM;AAAA,IAC9D;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,gBACJ,MACA,KACA,QACA,OACA,MACI;AACJ,eAAW,KAAK,KAAK,YAAY;AAC7B,YAAM,OAAO,IAAI,CAAC;AAClB,UAAI,QAAQ,MAAM,OAAO,CAAC,CAAC,GAAG;AAC1B;AAAA,MACJ;AACA,YAAM,SAAS,KAAK,QAAQ,CAAC;AAC7B,UAAI,WAAW,MAAM;AACjB;AAAA,MACJ;AACA,UAAI;AACA,eAAO,MAAM,OAAO,IAAI;AAAA,MAC5B,SAAS,KAAK;AACV,aAAK,MAAM,OAAO,YAAY,KAAK,EAAE,MAAM,SAAS,OAAO,MAAM;AAAA,MACrE;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,MAAM,aAAa;AACnB,MAAM,eAAe;AASrB,SAAS,MAAM,MAA0B;AACrC,QAAM,OAAO,IAAI,YAAY,OAAO,EAAE,OAAO,KAAK,SAAS,GAAG,UAAU,CAAC;AACzE,QAAM,OAAO,KAAK,WAAW,OAAO,aAAa,KAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;AAC5E,MAAI,KAAK,OAAO,WAAW,KAAK,aAAa,KAAK,IAAI,GAAG;AACrD,WAAO;AAAA,EACX;AACA,QAAM,UAAU,aAAa,IAAI;AACjC,QAAM,YAAY,eAAe,MAAM,OAAO;AAC9C,MAAI,cAAc,MAAM;AACpB,WAAO;AAAA,EACX;AACA,QAAM,MAAM,KAAK,QAAQ,OAAO;AAChC,QAAM,YAAY,MAAM,IAAI,OAAO,KAAK,MAAM,GAAG,GAAG;AACpD,QAAM,QAAQ,YAAY,UAAU,QAAQ,OAAO,EAAE,EAAE,MAAM,SAAS,CAAC;AACvE,MAAI,WAAW,OAAO,YAAY,KAAK,KAAK,WAAW,OAAO,YAAY,KAAK,GAAG;AAC9E,WAAO;AAAA,EACX;AACA,MAAI,WAAW,OAAO,QAAQ,KAAK,GAAG;AAClC,WAAO;AAAA,EACX;AACA,SAAO;AACX;AASA,eAAe,UACX,OACA,MACA,SACqB;AACrB,QAAM,SAAS,qBAAqB,SAAS,EAAE,KAAK,aAAa,iBAAiB,MAAM,YAAY,UAAU;AAC9G,QAAM,MAAM,kBAAkB,OAAO;AACrC,QAAM,SAAS,IAAI,oBAAoB,OAAO,OAAO,UAAU;AAC/D,oBAAkB,MAAM,SAAS,MAAM;AACvC;AAAA,IACI;AAAA,IACA;AAAA,IACA,IAAI,UAAU,WAAW,IAAI,UAAU,OAAO,0BAA0B;AAAA,EAAA;AAE5E,QAAM,QAAqB;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,OAAO,SAAS,eAAe;AAAA,IACnD,SAAS,IAAI,UAAU,OAAO,GAAG;AAAA,IACjC,UAAU,IAAI,kBAAkB,MAAM,QAAQ,OAAO,gBAAgB;AAAA,IACrE,WAAW;AAAA,IACX,iBAAiB;AAAA,EAAA;AAErB,MAAI,IAAI,UAAU,MAAM;AACpB,UAAM,IAAI,YAAY,OAAO,IAAI,OAAO,SAAS,KAAK,EAAE,KAAA;AAAA,EAC5D;AACA,QAAM,IAAI,YAAY,OAAO,OAAO,IAAI,OAAO,IAAI,EAAE,KAAA;AACrD,MAAI,MAAM,QAAQ,aAAa,GAAG;AAC9B,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,GAAG,MAAM,QAAQ,UAAU;AAAA,IAAA;AAAA,EAEnC;AACA,iBAAe,OAAO,MAAM;AAC5B,SAAO,OAAO,OAAA;AAClB;AAGO,MAAM,cAA+C,OAAO,OAAO;AAAA,EACtE,QAAQ;AAAA,EACR,YAAY,OAAO,OAAO,CAAC,QAAQ,QAAQ,UAAU,WAAW,CAAC;AAAA,EACjE,WAAW,OAAO,OAAO,CAAC,YAAY,6BAA6B,YAAY,CAAC;AAAA,EAChF;AAAA,EACA,QAAQ;AACZ,CAAC;AC/sCM,MAAM,WAAW,OAAO,OAAO;AAAA;AAAA,EAElC,mBAAmB,KAAK;AAAA;AAAA,EAExB,cAAc,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMnB,mBAAmB;AAAA;AAAA,EAEnB,iBAAiB,KAAK;AAAA;AAAA,EAEtB,eAAe;AAAA;AAAA,EAEf,cAAc,KAAK;AAAA;AAAA,EAEnB,WAAW;AAAA;AAAA,EAEX,WAAW;AAAA;AAAA,EAEX,WAAW;AAAA;AAAA,EAEX,eAAe,KAAK;AAAA;AAAA,EAEpB,uBAAuB,KAAK;AAAA;AAAA,EAE5B,YAAY;AAAA;AAAA,EAEZ,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,cAAc;AAClB,CAAC;AAGM,MAAM,mBAAuC,aAAa;AAAA,EAC7D,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ,CAAC,QAAQ,OAAO,OAAO,UAAU,MAAM;AACnD,CAAC;AAYD,MAAM,WAA2D;AAAA,EAC7D,OAAO,EAAE,QAAQ,UAAU,QAAQ,UAAU,MAAM,QAAQ,IAAI,MAAM,OAAO,SAAS,QAAQ,SAAA;AAAA,EAC7F,SAAS,EAAE,QAAQ,UAAU,QAAQ,UAAU,MAAM,MAAM,IAAI,MAAM,OAAO,SAAS,QAAQ,SAAA;AACjG;AAGA,MAAM,aAAkC,oBAAI,IAAI,CAAC,MAAM,OAAO,CAAC;AAG/D,MAAM,oCAAyC,IAAI;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAwCD,SAAS,wBACL,SACwB;AACxB,QAAM,IAAsB,WAAW,CAAA;AACvC,MAAI,EAAE,YAAY,UAAa,EAAE,YAAY,WAAW,EAAE,YAAY,WAAW;AAC7E,UAAM,IAAI,iBAAiB,iBAAiB,iDAAiD;AAAA,MACzF,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MAAI,EAAE,UAAU,UAAa,EAAE,UAAU,WAAW,EAAE,UAAU,WAAW,EAAE,UAAU,aAAa;AAChG,UAAM,IAAI,iBAAiB,iBAAiB,0DAA0D;AAAA,MAClG,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MACI,EAAE,cAAc,WACf,OAAO,EAAE,cAAc,YACpB,EAAE,UAAU,WAAW,KACvB,EAAE,cAAc,OAChB,EAAE,cAAc,QAChB,EAAE,cAAc,OACtB;AACE,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,aAAa,OAAO,EAAE,UAAA;AAAA,IAAU;AAAA,EAElD;AACA,MAAI,EAAE,YAAY,UAAa,EAAE,YAAY,QAAQ,EAAE,YAAY,QAAQ;AACvE,UAAM,IAAI,iBAAiB,iBAAiB,uCAAuC;AAAA,MAC/E,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MAAI,EAAE,UAAU,eAAe,EAAE,WAAW,MAAM;AAC9C,UAAM,IAAI,iBAAiB,iBAAiB,uDAAuD;AAAA,MAC/F,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,MAAI,EAAE,WAAW,UAAa,OAAO,EAAE,WAAW,WAAW;AACzD,UAAM,IAAI,iBAAiB,iBAAiB,qCAAqC;AAAA,MAC7E,QAAQ;AAAA,MACR,OAAO,EAAE;AAAA,IAAA,CACZ;AAAA,EACL;AACA,SAAO;AAAA,IACH,SAAS,SAAS,EAAE,WAAW,OAAO;AAAA,IACtC,OAAO,EAAE,SAAS;AAAA,IAClB,WAAW,EAAE,aAAa;AAAA,IAC1B,SAAS,EAAE,WAAW;AAAA,IACtB,QAAQ,EAAE,UAAU;AAAA,EAAA;AAE5B;AAQA,SAAS,WAAW,OAAe,OAAwB;AACvD,UAAQ,OAAA;AAAA,IACJ,KAAK;AACD,aAAO,UAAU,OAAO,SAAS;AAAA,IACrC,KAAK;AAAA,IACL,KAAK;AACD,aAAO,OAAO,UAAU,WAAW,cAAc,OAAO,KAAK,IAAI,OAAO,KAAK;AAAA,IACjF,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,aAAO,OAAO,UAAU,WAAW,cAAc,KAAK,IAAI,OAAO,KAAK;AAAA,IAC1E,KAAK;AAAA,IACL,KAAK;AACD,aAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAAA,IAC3D,KAAK;AACD,aAAO,KAAK,UAAU,KAAK,KAAK;AAAA,IACpC;AACI,aAAO,OAAO,KAAK;AAAA,EAAA;AAE/B;AASA,SAAS,SAAS,QAAgB,KAA4B;AAC1D,MAAI,CAAC,OAAO,MAAM,GAAG,GAAG;AACpB,WAAO;AAAA,EACX;AACA,UAAQ,OAAO,OAAA;AAAA,IACX,KAAK,QAAQ;AACT,YAAM,QAAQ,OAAO,QAAQ,GAAG;AAChC,YAAM,EAAE,UAAU,OAAO;AACzB,aAAO;AAAA,QACH,MAAM,IAAI,CAAC,SAAS,WAAW,OAAO,IAAI,CAAC;AAAA,QAC3C;AAAA,MAAA;AAAA,IAER;AAAA,IACA,KAAK;AACD,aAAO,KAAK,UAAU,OAAO,OAAO,GAAG,CAAC,KAAK;AAAA,IACjD,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,aAAO,WAAW,OAAO,OAAO,OAAO,MAAM,GAAG,CAAC;AAAA,IACrD,SAAS;AACL,YAAM,EAAE,eAAe,OAAO;AAC9B,YAAM,EAAE,SAAS;AACjB,UAAI,eAAe,GAAG;AAClB,eAAO,WAAW,OAAO,OAAO,KAAK,GAAG,CAAC;AAAA,MAC7C;AACA,YAAM,QAAkB,CAAA;AACxB,eAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACjC,cAAM,KAAK,WAAW,OAAO,OAAO,KAAK,MAAM,aAAa,CAAC,CAAC,CAAC;AAAA,MACnE;AACA,aAAO,MAAM,KAAK,GAAG;AAAA,IACzB;AAAA,EAAA;AAER;AAmBA,SAAS,QACL,UACA,QACA,MACI;AACJ,QAAM,EAAE,QAAQ;AAChB,MAAI,IAAI,SAAS,SAAS;AACtB,UAAM,2BAAW,IAAA;AACjB,QAAI,aAAa;AACjB,aAAS,IAAI,GAAG,IAAI,IAAI,MAAM,KAAK;AAC/B,YAAM,OAAO,OAAO,IAAI,KAAK,CAAC,CAAC;AAC/B,UAAI,KAAK,IAAI,IAAI,GAAG;AAChB;AAAA,MACJ,OAAO;AACH,aAAK,IAAI,IAAI;AAAA,MACjB;AAAA,IACJ;AACA,QAAI,aAAa,GAAG;AAChB;AAAA,QACI,SAAS;AAAA,QACT,GAAG,UAAU;AAAA,QACb;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AACA,MAAI,UAAU,IAAI,SAAS,cAAc,IAAI,SAAS,SAAS;AAC3D,QAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,IAAI,MAAM,KAAK;AAC/B,YAAM,KAAa,IAAI,KAAK,CAAC;AAC7B,UAAI,OAAO,YAAY,OAAO,EAAE,CAAC,MAAM,OAAO,IAAI;AAC9C;AAAA,MACJ;AAAA,IACJ;AACA,QAAI,UAAU,GAAG;AACb;AAAA,QACI,SAAS;AAAA,QACT,GAAG,OAAO;AAAA,QACV;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AACJ;AAEA,SAAS,WACL,UACA,SACA,QACI;AACJ,QAAM,SAAS,qBAAqB,OAAO;AAC3C,QAAM,MAAM,wBAAwB,OAAO;AAC3C,QAAM,QAAoB,CAAA;AAC1B,QAAM,OAAO,CAAC,MAAc,SAAiB,SAAwB,MAAM,QAAuB,SAAe;AAC7G,UAAM,KAAK,OAAO,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAA,CAAO,CAAC;AAAA,EAC9D;AACA,QAAM,EAAE,QAAQ;AAChB,QAAM,SAAS,CAAC,UAA0B,OAAO,IAAI,KAAK,KAAK,CAAC;AAChE,UAAQ,UAAU,QAAQ,IAAI;AAG9B,QAAM,UAAU,YAAY,QAAQ;AACpC,QAAM,WAAqB,CAAA;AAC3B,WAAS,IAAI,GAAG,IAAI,SAAS,WAAW,KAAK;AACzC,QAAI,CAAC,QAAQ,OAAO,CAAC,GAAG;AACpB,eAAS,KAAK,CAAC;AAAA,IACnB;AAAA,EACJ;AACA,MAAI,IAAI,QAAQ,SAAS,QAAQ,IAAI,UAAU,aAAa;AACxD,UAAM,QAAQ,IAAI,UAAU,cAAc,uBAAuB;AACjE,UAAM,QAAQ,gBAAgB,QAAQ;AACtC,QAAI,CAAC,SAAS,UAAU;AACpB;AAAA,QACI,SAAS;AAAA,QACT,GAAG,KAAK,6BAA6B,SAAS,SAAS;AAAA,QACvD;AAAA,QACA,SAAS;AAAA,MAAA;AAAA,IAEjB,WAAW,QAAQ,GAAG;AAClB;AAAA,QACI,SAAS;AAAA,QACT,GAAG,KAAK,6BAA6B,KAAK;AAAA,QAC1C;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ,WAAW,IAAI,UAAU,WAAW,CAAC,SAAS,YAAY,SAAS,WAAW,GAAG;AAC7E;AAAA,MACI,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAER;AACA,MAAI,QAAQ,cAAc,GAAG;AACzB;AAAA,MACI,SAAS;AAAA,MACT,GAAG,QAAQ,WAAW;AAAA,MACtB;AAAA,MACA,QAAQ;AAAA,IAAA;AAAA,EAEhB;AACA,MAAI,IAAI,UAAU,SAAS;AACvB,qBAAiB,UAAU,UAAU,IAAI;AAAA,EAC7C;AAGA,QAAM,UAAU,gBAAgB,QAAQ;AAGxC,QAAM,SAAS,SAAS,MAAM,OAAO,IAAI;AACzC,QAAM,YAAY,SAAS,MAAM,OAAO,OAAO;AAC/C,QAAM,eAAe,IAAI,IAAY,CAAC,IAAI,QAAQ,QAAQ,IAAI,QAAQ,QAAQ,IAAI,QAAQ,MAAM,EAAE,IAAI,KAAK,CAAC;AAC5G,MAAI,IAAI,QAAQ,SAAS,MAAM;AAC3B,iBAAa,IAAI,MAAM,IAAI,QAAQ,IAAI,CAAC;AAAA,EAC5C;AACA,QAAM,cAAc;AAAA,IAChB,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,UAAU,UAAU,OAAO;AAAA,EAAA;AAEnC,MAAI,IAAI,UAAU,aAAa;AAC3B,UAAM,UAAU,YAAY,UAAU,WAAW,OAAO,IAAI,MAAM,cAAc,OAAO,IAAI;AAC3F,QAAI,UAAU,GAAG;AACb;AAAA,QACI,SAAS;AAAA,QACT,GAAG,OAAO;AAAA,QACV;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AACA,MAAI,IAAI,UAAU,WAAW,QAAQ;AACjC,oBAAgB,QAAQ,MAAM,eAAe,QAAQ,UAAU,IAAI;AACnE,oBAAgB,WAAW,SAAS,aAAa,QAAQ,UAAU,IAAI;AACvE,eAAW,EAAE,OAAA,KAAY,aAAa;AAClC,kBAAY,QAAQ,QAAQ,UAAU,IAAI;AAAA,IAC9C;AAAA,EACJ;AAGA,QAAM,YAAY,SAAS,MAAM,OAAO,OAAO;AAC/C,QAAM,mCAAmB,IAAY,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC;AAC5D,QAAM,WAAqB,CAAA;AAC3B,MAAI,IAAI,UAAU,WAAW,QAAQ;AACjC,aAAS,IAAI,GAAG,IAAI,SAAS,WAAW,KAAK;AACzC,eAAS,KAAK,CAAC;AAAA,IACnB;AAAA,EACJ;AACA,QAAM,cAAc;AAAA,IAChB,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,UAAU,UAAU,OAAO;AAAA,EAAA;AAEnC,MAAI,IAAI,UAAU,SAAS;AACvB,QAAI,QAAQ;AACR,sBAAgB,WAAW,SAAS,aAAa,QAAQ,UAAU,IAAI;AACvE,iBAAW,EAAE,OAAA,KAAY,aAAa;AAClC,oBAAY,QAAQ,QAAQ,UAAU,IAAI;AAAA,MAC9C;AAAA,IACJ;AAAA,EACJ,OAAO;AACH,UAAM,UAAU,YAAY,UAAU,cAAc,OAAO,IAAI;AAC/D,QAAI,UAAU,GAAG;AACb;AAAA,QACI,SAAS;AAAA,QACT,GAAG,OAAO;AAAA,QACV;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAER;AAWA,SAAS,iBACL,UACA,UACA,MACI;AACJ,QAAM,EAAE,cAAc;AACtB,MAAI,cAAc,GAAG;AACjB;AAAA,EACJ;AACA,QAAM,OAAO,SAAS,SAAA;AACtB,QAAM,OAAO,IAAI,WAAW,SAAS;AACrC,MAAI,OAAO;AACX,MAAI,YAAY;AAChB,QAAM,UAAU,CAAC,UAAwB;AACrC,QAAI,KAAK,KAAK,MAAM,GAAG;AACnB;AAAA,IACJ;AACA,SAAK,KAAK,IAAI;AACd,QAAI,UAAU,MAAM;AAChB;AAAA,IACJ;AACA;AAAA,EACJ;AACA,aAAW,KAAK,UAAU;AACtB,YAAQ,KAAK,IAAI,CAAC,CAAC;AACnB,YAAQ,KAAK,IAAI,CAAC,CAAC;AAAA,EACvB;AACA,QAAM,WAAW,YAAY;AAC7B,MAAI,WAAW,GAAG;AACd;AAAA,MACI,SAAS;AAAA,MACT,GAAG,QAAQ;AAAA,MACX;AAAA,MACA;AAAA,IAAA;AAAA,EAER;AACA,MAAI,YAAY,GAAG;AACf;AAAA,MACI,SAAS;AAAA,MACT,GAAG,SAAS;AAAA,MACZ;AAAA,MACA;AAAA,IAAA;AAAA,EAER;AACJ;AAOA,SAAS,MAAM,MAAsB;AACjC,SAAO,KAAK,YAAA;AAChB;AAcA,SAAS,iBACL,OACA,QACA,UACA,UACA,aACA,MACW;AACX,QAAM,MAAmB,CAAA;AACzB,aAAW,UAAU,OAAO;AACxB,UAAM,EAAE,MAAM,KAAA,IAAS,OAAO;AAC9B,QAAI,WAAW,YAAY,WAAW,aAAa;AAC/C;AAAA,IACJ;AACA,QAAI,SAAS,QAAQ,cAAc,IAAI,IAAI,GAAG;AAC1C;AAAA,IACJ;AACA,UAAM,MAAM,MAAM,IAAI;AACtB,UAAM,MAAM,OAAO,SAAS,OAAO;AACnC,UAAM,WAAW,WAAW,SAAS,WAAW,CAAC,IAAI,GAAG,aAAa,KAAK,IAAI;AAC9E,QAAI,SAAS,IAAI,GAAG,KAAK,YAAa,aAAa,QAAQ,MAAM,SAAS,KAAK,IAAI,MAAM,KAAM;AAC3F;AAAA,QACI,SAAS;AAAA,QACT,GAAG,MAAM,YAAY,IAAI,kDAAkD,WAAW,YAAY,GAAG;AAAA,QACrG;AAAA,QACA;AAAA,MAAA;AAEJ;AAAA,IACJ;AACA,QAAI,WAAW,CAAC,IAAI,GAAG,WAAW,KAAK,GAAG;AACtC,UAAI,gBAAgB,MAAM;AACtB;AAAA,UACI,SAAS;AAAA,UACT,GAAG,MAAM,YAAY,IAAI;AAAA,UACzB;AAAA,UACA;AAAA,QAAA;AAEJ;AAAA,MACJ;AACA;AAAA,QACI,SAAS;AAAA,QACT,GAAG,MAAM,YAAY,IAAI;AAAA,QACzB;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AACA,QAAI,KAAK,EAAE,QAAQ,QAAQ,MAAM;AAAA,EACrC;AACA,SAAO;AACX;AAYA,SAAS,gBACL,QACA,MACA,OACA,QACA,MACA,MACI;AACJ,MAAI,WAAW,MAAM;AACjB;AAAA,EACJ;AACA,QAAM,EAAE,SAAS,OAAO;AACxB,MAAI,WAAW,CAAC,IAAI,GAAG,KAAK,IAAI,GAAG;AAC/B;AAAA,MACI,SAAS;AAAA,MACT,GAAG,MAAM,YAAY,IAAI,MAAM,IAAI,uEAAuE,IAAI;AAAA,MAC9G;AAAA,MACA,SAAS,QAAQ,IAAI;AAAA,IAAA;AAAA,EAE7B;AACA,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,QAAQ;AACtD;AAAA,MACI,SAAS;AAAA,MACT,GAAG,MAAM,YAAY,IAAI,MAAM,IAAI,QAAQ,OAAO,KAAK;AAAA,MACvD;AAAA,MACA,SAAS,QAAQ,IAAI;AAAA,IAAA;AAAA,EAE7B;AACA,cAAY,QAAQ,QAAQ,MAAM,MAAM,IAAI;AAChD;AAQA,SAAS,SAAS,QAAgB,MAAiC;AAC/D,MAAI,QAAQ;AACZ,aAAW,OAAO,MAAM;AACpB,QAAI,OAAO,MAAM,GAAG,GAAG;AACnB;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAEA,MAAM,WAAgD,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,EAAA;AACzF,MAAM,cAAoC,CAAC,QAAQ,OAAO,OAAO,QAAQ;AAazE,SAAS,YACL,QACA,QACA,MACA,MACA,aAAa,OACT;AACJ,QAAM,EAAE,MAAM,MAAA,IAAU,OAAO;AAC/B,QAAM,QAAQ,GAAG,MAAM,YAAY,IAAI;AACvC,MAAI,OAAO,UAAU,SAAS,OAAO,UAAU,OAAO;AAClD,QAAI,YAAY;AAChB,UAAM,EAAE,eAAe,OAAO;AAC9B,UAAM,EAAE,SAAS;AACjB,eAAW,OAAO,MAAM;AACpB,UAAI,CAAC,OAAO,MAAM,GAAG,GAAG;AACpB;AAAA,MACJ;AACA,eAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACjC,YAAI,CAAC,OAAO,SAAS,KAAK,MAAM,aAAa,CAAC,CAAC,GAAG;AAC9C;AACA;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AACA,QAAI,YAAY,GAAG;AACf,WAAK,SAAS,WAAW,GAAG,KAAK,KAAK,SAAS,0CAA0C,MAAM,SAAS;AAAA,IAC5G;AACA;AAAA,EACJ;AACA,MAAI,UAAU,YAAY,UAAU,QAAQ;AACxC;AAAA,EACJ;AACA,MAAI,SAAS;AACb,MAAI,YAAY;AAChB,MAAI,MAAM;AACV,QAAM,YAAY,IAAI,cAAc,gBAAgB;AACpD,aAAW,OAAO,MAAM;AACpB,QAAI,CAAC,OAAO,MAAM,GAAG,GAAG;AACpB;AAAA,IACJ;AACA,UAAM,OAAO,OAAO,MAAM,GAAG;AAC7B,QAAI,OAAO,SAAS,UAAU;AAC1B;AAAA,IACJ;AACA;AACA,UAAM,OAAO,eAAe,IAAI;AAChC,aAAS,KAAK,IAAI,QAAQ,SAAS,IAAI,CAAC;AACxC,QAAI,CAAC,UAAU,SAAS;AACpB,UAAI,SAAS,UAAU;AACnB,oBAAY;AAAA,MAChB;AACA,gBAAU,QAAQ,IAAI;AAAA,IAC1B;AAAA,EACJ;AACA,MAAI,cAAc,SAAS,GAAG;AAC1B;AAAA,EACJ;AACA,QAAM,cAAc,aAAa,UAAU,OAAA,MAAa;AACxD,MAAI,SAAS,SAAS,UAAU,CAAC,aAAa;AAC1C,SAAK,SAAS,eAAe,GAAG,KAAK,+BAA+B,YAAY,MAAM,CAAC,IAAI,MAAM,GAAG;AACpG;AAAA,EACJ;AACA,MAAI,UAAU,UAAU,CAAC,aAAa;AAClC;AAAA,MACI,SAAS;AAAA,MACT,GAAG,KAAK;AAAA,MACR;AAAA,MACA;AAAA,IAAA;AAAA,EAER,WAAW,UAAU,YAAY,aAAa;AAC1C,SAAK,SAAS,uBAAuB,GAAG,KAAK,0CAA0C,MAAM,IAAI;AAAA,EACrG;AACJ;AASA,SAAS,SAAS,UAAyB,MAAY,GAAmB;AACtE,MAAI,CAAC,SAAS,YAAY,CAAC,KAAK,QAAQ,eAAe,CAAC,GAAG;AACvD,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAUA,SAAS,cAAc,IAAY,QAA+B;AAC9D,MAAI,WAAW,MAAM;AACjB,WAAO,GAAG,EAAE,IAAI,MAAM;AAAA,EAC1B;AACA,SAAO,GAAG,SAAS,GAAG,IAAI,GAAG,EAAE,MAAM;AACzC;AAaA,UAAU,eAAe,UAAyB,MAAgD;AAC9F,QAAM,EAAE,WAAW,QAAA,IAAY,KAAK;AACpC,QAAM,SAAS,CAAC,SAAyB,IAAI,KAAK,QAAQ,MAAM,IAAI,CAAC;AAGrE,QAAM,QAAQ,CAAC,SACX,qBAAqB,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,IAAI,OAAO,IAAI,IAAI,aAAa,MAAM,SAAS;AAEpG,QAAM,QAAQ,CAAC,SAA0B,QAAQ,KAAK,IAAI,IAAI,OAAO,IAAI,IAAI,MAAM,IAAI;AACvF,QAAM,OAAO,SAAS,SAAA;AACtB,QAAM,EAAE,UAAU,QAAA,IAAY;AAC9B,QAAM,OAAO,IAAI,WAAW,SAAS,SAAS;AAC9C,MAAI,OAAO;AACX,QAAM,UAAU,MAAY;AACxB,WAAO,OAAO,KAAK,UAAU,KAAK,IAAI,MAAM,GAAG;AAC3C;AAAA,IACJ;AAAA,EACJ;AACA,YAAU,QAAQ,MAAkD;AAChE,aAAS,IAAI,MAAM,KAAK,MAAM,KAAK;AAC/B,UAAI,KAAK,CAAC,MAAM,GAAG;AACf,aAAK,CAAC,IAAI;AACV,cAAM,MAAM,KAAK,OAAO,CAAC,CAAC,IAAI;AAAA,MAClC;AAAA,IACJ;AACA,YAAA;AAAA,EACJ;AACA,MAAI,IAAI;AACR,SAAO,IAAI,SAAS,QAAQ;AACxB,UAAM,SAAS,KAAK,IAAI,SAAS,CAAC,CAAC;AACnC,QAAI,MAAM;AACV,WAAO,MAAM,SAAS,UAAU,KAAK,IAAI,SAAS,GAAG,CAAC,MAAM,QAAQ;AAChE;AAAA,IACJ;AAGA,UAAM,QAAkB,CAAA;AACxB,QAAI,UAAU;AACd,QAAI,UAAU;AACd,UAAM,OAAO,CAAC,MAAoB;AAC9B,UAAI,KAAK,CAAC,MAAM,GAAG;AACf,aAAK,CAAC,IAAI;AACV,oBAAY,MAAM,OAAO,MAAM;AAC/B,cAAM,KAAK,CAAC;AACZ,kBAAU,KAAK,IAAI,SAAS,CAAC;AAAA,MACjC;AAAA,IACJ;AACA,SAAK,MAAM;AACX,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC1B,WAAK,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC;AAAA,IAC9B;AACA,QAAI,CAAC,SAAS;AACV,iBAAW,KAAK,OAAO;AACnB,aAAK,CAAC,IAAI;AAAA,MACd;AACA,aAAO,QAAQ,OAAO;AAAA,IAC1B;AACA,YAAA;AACA,UAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AACzC,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC1B,YAAM,IAAI,SAAS,CAAC;AACpB,YAAM,KAAK,MAAM,cAAc,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,GAAG,QAAQ,WAAW,QAAQ,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;AAAA,IACxG;AACA,UAAM,MAAM,KAAK,SAAS,IAAI;AAC9B,QAAI;AAAA,EACR;AACA,SAAO,QAAQ,SAAS,YAAY,CAAC;AACzC;AASA,UAAU,MAAM,UAAyB,MAAgD;AACrF,QAAM,EAAE,QAAQ;AAChB,MAAI,IAAI,UAAU,aAAa;AAC3B,WAAO,eAAe,UAAU,IAAI;AACpC;AAAA,EACJ;AACA,QAAM,EAAE,WAAW,QAAA,IAAY;AAC/B,QAAM,QAAQ,CAAC,SAAyB,aAAa,MAAM,SAAS;AACpE,QAAM,OAAO,CAAC,QAAgB,QAAwB;AAClD,UAAM,OAAO,SAAS,QAAQ,GAAG;AACjC,WAAO,SAAS,OAAO,KAAK,MAAM,IAAI;AAAA,EAC1C;AACA,MAAI,IAAI,UAAU,SAAS;AACvB,UAAMC,WAAU,CAAC,IAAI,QAAQ,EAAE;AAC/B,QAAI,KAAK,cAAc,MAAM;AACzBA,eAAQ,KAAK,KAAK,UAAU,KAAK,IAAI;AAAA,IACzC;AACA,eAAW,EAAE,YAAY,KAAK,aAAa;AACvCA,eAAQ,KAAK,MAAM;AAAA,IACvB;AACA,QAAI,IAAI,QAAQ;AACZ,YAAMA,SAAQ,IAAI,KAAK,EAAE,KAAK,SAAS,IAAI;AAAA,IAC/C;AACA,aAAS,IAAI,GAAG,IAAI,SAAS,WAAW,KAAK;AACzC,YAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC;AACpC,UAAI,KAAK,cAAc,MAAM;AACzB,cAAM,KAAK,KAAK,KAAK,WAAW,CAAC,CAAC;AAAA,MACtC;AACA,iBAAW,EAAE,YAAY,KAAK,aAAa;AACvC,cAAM,KAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,MAC9B;AACA,YAAM,MAAM,KAAK,SAAS,IAAI;AAAA,IAClC;AACA;AAAA,EACJ;AACA,QAAM,UAAU,CAAC,IAAI,QAAQ,QAAQ,IAAI,QAAQ,MAAM;AACvD,MAAI,IAAI,QAAQ,SAAS,MAAM;AAC3B,YAAQ,KAAK,IAAI,QAAQ,IAAI;AAAA,EACjC;AACA,MAAI,KAAK,WAAW,MAAM;AACtB,YAAQ,KAAK,KAAK,OAAO,KAAK,IAAI;AAAA,EACtC;AACA,MAAI,KAAK,cAAc,MAAM;AACzB,YAAQ,KAAK,KAAK,UAAU,KAAK,IAAI;AAAA,EACzC;AACA,MAAI,KAAK,QAAQ,UAAU;AACvB,YAAQ,KAAK,IAAI,QAAQ,MAAM;AAAA,EACnC;AACA,aAAW,EAAE,YAAY,KAAK,aAAa;AACvC,YAAQ,KAAK,MAAM;AAAA,EACvB;AACA,MAAI,IAAI,QAAQ;AACZ,UAAM,QAAQ,IAAI,KAAK,EAAE,KAAK,SAAS,IAAI;AAAA,EAC/C;AACA,QAAM,OAAO,SAAS,SAAA;AACtB,aAAW,KAAK,KAAK,UAAU;AAC3B,UAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/E,QAAI,IAAI,QAAQ,SAAS,MAAM;AAC3B,YAAM,KAAK,SAAS,UAAU,MAAM,CAAC,CAAC;AAAA,IAC1C;AACA,QAAI,KAAK,WAAW,MAAM;AACtB,YAAM,KAAK,KAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,IACnC;AACA,QAAI,KAAK,cAAc,MAAM;AACzB,YAAM,KAAK,KAAK,KAAK,WAAW,CAAC,CAAC;AAAA,IACtC;AACA,QAAI,KAAK,QAAQ,UAAU;AACvB,YAAM,KAAK,KAAK,QAAQ,KAAK,CAAC,KAAK,EAAE;AAAA,IACzC;AACA,eAAW,EAAE,YAAY,KAAK,aAAa;AACvC,YAAM,KAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,IAC9B;AACA,UAAM,MAAM,KAAK,SAAS,IAAI;AAAA,EAClC;AACJ;AASA,SAAS,QAAQ,UAAyB,SAAqE;AAC3G,QAAM,OAAO,WAAW,UAAU,SAAS,KAAK;AAChD,QAAM,YAAY,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,iBAAiB;AAC9E,MAAI,cAAc,QAAW;AACzB,UAAM,IAAI,iBAAiB,gBAAgB,UAAU,SAAS;AAAA,MAC1D,QAAQ;AAAA,MACR,OAAO,UAAU;AAAA,IAAA,CACpB;AAAA,EACL;AACA,SAAO;AACX;AASA,SAAS,MAAM,UAAyB,SAAuE;AAC3G,QAAM,OAAO,WAAW,UAAU,SAAS,IAAI;AAC/C,SAAO,OAAO,OAAO;AAAA,IACjB,GAAG,kBAAkB,UAAU,kBAAkB,KAAK,QAAQ,EAAE,OAAO,YAAY;AAAA,IACnF,GAAG,KAAK;AAAA,EAAA,CACX;AACL;AAGO,MAAM,cAA+C,OAAO,OAAO;AAAA,EACtE,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AAAA,EACA,OAAO,UAAyB,SAA6E;AACzG,WAAO,aAAa,MAAM,UAAU,QAAQ,UAAU,OAAO,CAAC,CAAC;AAAA,EACnE;AAAA,EACA,eAAe,UAAyB,SAAmE;AACvG,WAAO,SAAS,MAAM,UAAU,QAAQ,UAAU,OAAO,CAAC,CAAC;AAAA,EAC/D;AACJ,CAAC;AC/9BM,MAAM,YAAY,OAAO,OAAO;AAAA;AAAA,EAEnC,aAAa;AAAA;AAAA,EAEb,cAAc;AAAA;AAAA,EAEd,kBAAkB;AAAA;AAAA,EAElB,mBAAmB;AAAA;AAAA,EAEnB,kBAAkB;AAAA;AAAA,EAElB,qBAAqB;AAAA;AAAA,EAErB,cAAc;AAAA;AAAA,EAEd,aAAa;AAAA;AAAA,EAEb,kBAAkB;AAAA;AAAA,EAElB,YAAY;AAAA;AAAA,EAEZ,UAAU;AAAA;AAAA,EAEV,gBAAgB;AAAA;AAAA,EAEhB,OAAO;AAAA;AAAA,EAEP,cAAc;AAAA;AAAA,EAEd,gBAAgB;AAAA;AAAA,EAEhB,mBAAmB;AAAA;AAAA,EAEnB,WAAW;AAAA;AAAA,EAEX,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA,EAEhB,aAAa;AAAA;AAAA,EAEb,mBAAmB;AAAA;AAAA,EAEnB,kBAAkB;AAAA;AAAA,EAElB,iBAAiB;AAAA;AAAA,EAEjB,sBAAsB;AAC1B,CAAC;"}
1
+ {"version":3,"file":"csv.js","sources":["../src/formats/csv/header.ts","../src/formats/csv/values.ts","../src/formats/csv/importer.ts","../src/formats/csv/exporter.ts","../src/formats/csv/index.ts"],"sourcesContent":["/**\n * Header handling of the CSV importer: whether the first row is a header, which columns hold the\n * endpoints, the weight, the Gephi per-row `Type`, the edge id and the label (by name lists and\n * by position), and the synthesised names of a headerless file (`column1`, `column2`, ...).\n *\n * Name resolution is exact first, then case-insensitive, over a candidate list in priority order,\n * so `source` / `Source` / `SOURCE`, `src`, `from` all resolve without configuration; the Gephi\n * dialect (exact `Source` and `Target` headers) additionally makes an exact `Type` column the\n * per-row direction. A caller may name any column explicitly or give its 0-based position.\n */\n\nimport { GraphFormatError } from \"@graphty/graph-format\";\n\nimport { inferTextDtype } from \"../../common/text.js\";\n\n/** A column named by header text or by 0-based position. */\nexport type CsvColumnRef = string | number;\n\n/** Header candidates of the source endpoint, in priority order (matched exactly, then case-insensitively). */\nexport const SOURCE_NAMES: readonly string[] = Object.freeze([\n \"source\",\n \"src\",\n \"from\",\n \"start\",\n \"source_id\",\n \"sourceid\",\n \"fromnodeid\",\n \"start_id\",\n \":start_id\",\n]);\n\n/** Header candidates of the target endpoint. */\nexport const TARGET_NAMES: readonly string[] = Object.freeze([\n \"target\",\n \"dst\",\n \"dest\",\n \"to\",\n \"end\",\n \"target_id\",\n \"targetid\",\n \"tonodeid\",\n \"end_id\",\n \":end_id\",\n]);\n\n/** Header candidates of a node table's id column. */\nexport const ID_NAMES: readonly string[] = Object.freeze([\"id\", \"node\", \"name\", \"key\"]);\n\n/** Header candidates of a label column (node or edge). */\nexport const LABEL_NAMES: readonly string[] = Object.freeze([\"label\"]);\n\n/** Header candidates of an edge id column. */\nexport const EDGE_ID_NAMES: readonly string[] = Object.freeze([\"id\"]);\n\n/** The Gephi per-row direction column. */\nexport const TYPE_NAME = \"Type\";\n\n/** Every name that marks a first row as a header when the caller leaves detection to the importer. */\nconst HEADER_MARKERS: ReadonlySet<string> = new Set([\n ...SOURCE_NAMES,\n ...TARGET_NAMES,\n ...ID_NAMES,\n ...LABEL_NAMES,\n \"weight\",\n \"type\",\n \"value\",\n]);\n\n/**\n * Find a column by name: candidates are tried in priority order, each first exactly and then\n * case-insensitively, so `Source` (a case variant of the first candidate) beats an exact `from`.\n * @param names - the header names\n * @param candidates - the names to look for, in priority order\n * @returns the column index, or -1\n */\nexport function findColumn(names: readonly string[], candidates: readonly string[]): number {\n for (const candidate of candidates) {\n const exact = names.indexOf(candidate);\n if (exact >= 0) {\n return exact;\n }\n const lower = candidate.toLowerCase();\n const loose = names.findIndex((name) => name.toLowerCase() === lower);\n if (loose >= 0) {\n return loose;\n }\n }\n return -1;\n}\n\n/**\n * Resolve an explicit column option: a 0-based position (checked against the width) or a header\n * name (exact, then case-insensitive). An option naming a column the file does not have is\n * E_UNSUPPORTED (details.option, details.found): the file cannot be read as requested.\n * @param names - the header names\n * @param ref - the option value\n * @param option - the option name, for the error\n * @returns the column index\n */\nexport function resolveColumnRef(names: readonly string[], ref: CsvColumnRef, option: string): number {\n if (typeof ref === \"number\") {\n if (!Number.isInteger(ref) || ref < 0 || ref >= names.length) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option ${option}: column ${ref} does not exist (the file has ${names.length} column(s))`,\n { option, found: ref, columns: names.length },\n );\n }\n return ref;\n }\n const index = findColumn(names, [ref]);\n if (index < 0) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${option}: no column named ${JSON.stringify(ref)}`, {\n option,\n found: ref,\n columns: [...names],\n });\n }\n return index;\n}\n\n/**\n * Decide whether the first row of a file is a header when the caller left it to the importer: it\n * is when any cell is a known column name (source, target, id, label, weight, type...), or when\n * every cell is non-numeric text while the second row has a numeric cell (`u,v` over `1,2`);\n * a first row with a numeric cell is data, and so is one that looks exactly like the rows below it.\n * @param first - the first row\n * @param second - the second row, or null when the file has one row\n * @returns true when the first row is a header\n */\nexport function looksLikeHeader(first: readonly string[], second: readonly string[] | null): boolean {\n let allText = true;\n for (const cell of first) {\n const text = cell.trim();\n if (HEADER_MARKERS.has(text.toLowerCase())) {\n return true;\n }\n if (text.length === 0 || inferTextDtype(text) !== \"string\") {\n allText = false;\n }\n }\n if (!allText || second === null) {\n return false;\n }\n return second.some((cell) => cell.trim().length > 0 && inferTextDtype(cell.trim()) !== \"string\");\n}\n\n/**\n * The synthesised header of a headerless file: `column1` ... `columnN`.\n * @param width - the number of cells of the first row\n * @returns the names\n */\nexport function positionalNames(width: number): string[] {\n const names: string[] = [];\n for (let i = 1; i <= width; i++) {\n names.push(`column${i}`);\n }\n return names;\n}\n\n/**\n * Header cells as column names: trimmed, a BOM-free first cell (the reader strips it), and an\n * empty cell named by its position so every column has a name.\n * @param cells - the header row\n * @returns the names\n */\nexport function headerNames(cells: readonly string[]): string[] {\n return cells.map((cell, i) => {\n const name = cell.trim();\n return name.length === 0 ? `column${i + 1}` : name;\n });\n}\n","/**\n * The untyped attribute columns of the CSV importer (design sections 5.1 and 5.4): every cell is\n * parsed by the fixed lexical grammar and pushed as a scalar, the sink's per-column inference\n * widens `bool -> i32 -> f64 -> string`, and a column whose sampled values are all text becomes a\n * `dict` when its cardinality stays below half the sampled rows (the 1024-row heuristic of\n * design section 5.4). The sample is the only buffering: at most DICT_SAMPLE_ROWS (row, text)\n * pairs per column, flushed on the decision.\n *\n * An unquoted empty cell is unset (never written); a quoted empty cell is the empty string. A\n * column whose every cell is unset is declared as a nullable string column at the end so the\n * header survives a round trip. Inferred columns go through the shared TextCellWriter, which keeps\n * the column's dtype the one the 5.1 grammar implies and the lexical form of numeric-looking cells\n * that a widening to string would otherwise rewrite.\n */\n\nimport { type ColumnHandle, GraphFormatError, type GraphSink, INVALID_INDEX } from \"@graphty/graph-format\";\n\nimport { DICT_SAMPLE_ROWS, DictHeuristic } from \"../../common/attributes.js\";\nimport { type ImportReportBuilder } from \"../../common/report.js\";\nimport { inferTextDtype, parseTextCell, TextCellWriter } from \"../../common/text.js\";\n\n/**\n * One inferred attribute column of a node or edge table, written cell by cell.\n */\nexport class InferredColumn {\n /** The column name in the sink. */\n readonly name: string;\n\n private readonly domain: \"node\" | \"edge\";\n\n private readonly sink: GraphSink;\n\n private readonly report: ImportReportBuilder;\n\n private handle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n /** Whether values are pushed as text: a dict column, or a caller's string / dict column of the name. */\n private text = false;\n\n /** Whether the column was declared as a dict by this writer. */\n private dict = false;\n\n /** Whether the dtype decision was made; until then values are sampled. */\n private decided = false;\n\n /** Whether every sampled value so far is text under the lexical grammar (a dict candidate). */\n private candidate = true;\n\n private readonly heuristic: DictHeuristic;\n\n /** The inferred-column writer, once the column was decided to be inferred (neither dict nor a caller's). */\n private writer: TextCellWriter | null = null;\n\n private readonly pendingRows: number[] = [];\n\n private readonly pendingTexts: string[] = [];\n\n /**\n * Create a column writer; nothing is declared until the first value or finish().\n * @param name - the column name\n * @param domain - node or edge\n * @param sink - the sink\n * @param report - the report a refused sampled value is recorded in (the flush has no line numbers)\n * @param sampleRows - rows sampled before the dict decision; DICT_SAMPLE_ROWS by default\n */\n constructor(\n name: string,\n domain: \"node\" | \"edge\",\n sink: GraphSink,\n report: ImportReportBuilder,\n sampleRows: number = DICT_SAMPLE_ROWS,\n ) {\n this.name = name;\n this.domain = domain;\n this.sink = sink;\n this.report = report;\n this.heuristic = new DictHeuristic(sampleRows);\n }\n\n /**\n * Whether the column was declared as a dict.\n * @returns true after a dict decision\n */\n get isDict(): boolean {\n return this.dict;\n }\n\n /**\n * Write one set cell.\n * @param row - the node or edge index\n * @param text - the cell text (the empty string for a quoted empty cell)\n */\n write(row: number, text: string): void {\n if (this.decided) {\n this.push(row, text);\n return;\n }\n this.pendingRows.push(row);\n this.pendingTexts.push(text);\n if (this.candidate && inferTextDtype(text) !== \"string\") {\n this.candidate = false;\n }\n const full = this.heuristic.observe(text);\n if (full || !this.candidate) {\n this.decide();\n }\n }\n\n /**\n * Decide an undecided column from what was sampled (or declare an all-empty column as string)\n * and flush the sample. Call once at the end of the input.\n */\n finish(): void {\n if (!this.decided) {\n this.decide();\n }\n if (this.handle === INVALID_INDEX) {\n this.handle = this.lookup();\n if (this.handle === INVALID_INDEX) {\n this.handle = this.declare(\"string\");\n }\n }\n }\n\n /**\n * Make the dtype decision: a dict when every sampled value is text and the cardinality is low\n * and no column of the name exists yet; inference otherwise. A column the sink already holds\n * (a caller's) receives the cell text when it is a string or dict column and the parsed value\n * otherwise. The sample is flushed either way; a sampled value the sink refuses (a caller's\n * typed column of the name) is recorded as an issue of this column and the flush continues.\n */\n private decide(): void {\n this.decided = true;\n const existing = this.lookup();\n if (existing !== INVALID_INDEX) {\n this.handle = existing;\n this.text = this.holdsText();\n } else if (this.candidate && this.heuristic.decide() === \"dict\") {\n this.dict = true;\n this.text = true;\n this.handle = this.declare(\"dict\");\n } else {\n this.writer = new TextCellWriter(this.name, this.domain, this.sink, this.report);\n }\n const rows = this.pendingRows;\n const texts = this.pendingTexts;\n for (let i = 0; i < rows.length; i++) {\n try {\n this.push(rows[i], texts[i]);\n } catch (err) {\n this.report.recordError(err, { element: this.name });\n }\n }\n rows.length = 0;\n texts.length = 0;\n }\n\n /**\n * Push one value: text into a dict or a caller's text column, the parsed scalar into a caller's\n * typed column, and everything else through the inferred-column writer.\n * @param row - the node or edge index\n * @param text - the cell text\n */\n private push(row: number, text: string): void {\n if (this.writer !== null) {\n this.writer.write(row, text);\n this.handle = this.writer.column;\n return;\n }\n const value: unknown = this.text ? text : parseTextCell(text);\n this.set(this.handle, row, value);\n }\n\n /**\n * Set a cell in the sink's table for this domain.\n * @param column - the handle or name\n * @param row - the row\n * @param value - the value\n */\n private set(column: ColumnHandle | string, row: number, value: unknown): void {\n if (this.domain === \"node\") {\n this.sink.setNodeValue(column, row, value);\n } else {\n this.sink.setEdgeValue(column, row, value);\n }\n }\n\n /**\n * The sink's handle for this column's name.\n * @returns the handle, or INVALID_INDEX when the sink has no such column yet\n */\n private lookup(): ColumnHandle {\n return this.domain === \"node\" ? this.sink.nodeColumn(this.name) : this.sink.edgeColumn(this.name);\n }\n\n /**\n * Whether the sink's existing column of this name is a string or dict column, probed through\n * the sink's declare (the same shape returns the existing handle, another is E_COLUMN_EXISTS).\n * @returns true when cell text is what the column holds\n */\n private holdsText(): boolean {\n for (const dtype of [\"string\", \"dict\"] as const) {\n try {\n this.declare(dtype);\n return true;\n } catch (err) {\n if (!(err instanceof GraphFormatError) || err.code !== \"E_COLUMN_EXISTS\") {\n throw err;\n }\n }\n }\n return false;\n }\n\n /**\n * Declare the column with a fixed dtype.\n * @param dtype - dict or string\n * @returns the handle\n */\n private declare(dtype: \"dict\" | \"string\"): ColumnHandle {\n const decl = { name: this.name, dtype, nullable: true, origin: { format: \"csv\" } };\n return this.domain === \"node\" ? this.sink.declareNodeColumn(decl) : this.sink.declareEdgeColumn(decl);\n }\n}\n","/**\n * The CSV / TSV importer (design sections 8.4 and 8.6; research note 07 section 2.6): a streaming\n * edge-list reader for the generic (`source,target[,weight,...]`), Gephi (`Source,Target,Type,Id,\n * Label,Weight,...`) and headerless (`u v [w]`) dialects, with an optional node table merged by id,\n * and, with `table: \"adjacency\"`, an adjacency table (`node,neighbour[:weight],...`).\n *\n * - The delimiter is sniffed from a preview unless given; LF, CRLF and lone-CR files all read.\n * - The first row is a header when it holds a known column name or when it is all text over a\n * numeric second row (`header: \"auto\"`); a headerless file is positional: source, target,\n * weight (when `weightFrom` is not null), then `column4`... as attributes.\n * - Endpoint and node ids are text cells coerced by ONE rule per import call (`ids`, default\n * \"canonical\": `\"1\"` becomes the number 1, `\"01\"` stays a string) over the node table and the\n * edge table alike, so they agree across the two files.\n * - Direction is per row in the Gephi dialect (`Type` = Directed / Undirected / Mutual, blank =\n * `defaultDirected`) and `defaultDirected` (true) otherwise; the first edge row sets the sink's\n * direction and later rows that differ go through `onMixedDirection` (expansion by default).\n * - The weight column (`weightFrom`, default \"weight\", matched case-insensitively) is parsed per\n * row; a blank cell means \"no weight given\" and the edge is pushed without one.\n * - Every other column is an attribute: cells are parsed by the fixed lexical grammar of design\n * section 5.1 and the sink infers the column dtype (widening per column, never per cell); an\n * all-text column of low cardinality becomes a dict (design section 5.4); an `id` column of the\n * edge table is the edge id (role id, unique); a `label` column is the label (role label).\n * - An adjacency table has no header (unless `header: true`, which skips the first row): each row\n * is a node followed by its neighbours, one edge per neighbour in row order. A neighbour cell\n * `id:weight` carries the edge's weight when the text after its LAST colon is a number; a cell\n * ending in a bare colon (`a:1:`) is the id before it with no weight; any other cell is the id\n * as written (`http://x`). A row holding only its node adds an isolated node. An empty adjacency\n * table is the empty graph. The delimiter sniff skips a candidate under which a closing quote is\n * followed by other text, since the field counts it otherwise relies on vary by row.\n * - A node table without an id column is refused unless `rowNumberIds` is set: then each data row's\n * 0-based number is its id, coerced by `ids` like any other id cell. The option applies to the\n * node table (the `nodes` input, or the input itself when nothing is paired with it), whose first\n * row it makes a header under `header: \"auto\"`; a paired edge table is read as without it.\n * - Per-row problems (wrong field count, blank endpoint, invalid weight, bad Type, refused id)\n * are recorded and the row skipped; the import aborts with ImportError once `errorLimit` is\n * exceeded, on a malformed or unterminated quoted field, on an empty input and on a header\n * without endpoint (or id) columns.\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, RENAMED_CODE, uniqueColumnName } from \"../../common/attributes.js\";\nimport {\n DUPLICATE_EDGE_ID_CODE as SHARED_DUPLICATE_EDGE_ID_CODE,\n DUPLICATE_NODE_CODE as SHARED_DUPLICATE_NODE_CODE,\n EMPTY_INPUT_CODE as SHARED_EMPTY_INPUT_CODE,\n ID_MERGED_CODE as SHARED_ID_MERGED_CODE,\n MISSING_ENDPOINT_CODE as SHARED_MISSING_ENDPOINT_CODE,\n MISSING_ID_CODE as SHARED_MISSING_ID_CODE,\n ROLE_TAKEN_CODE as SHARED_ROLE_TAKEN_CODE,\n} from \"../../common/codes.js\";\nimport { DirectionResolver, type EdgeKind } from \"../../common/direction.js\";\nimport { IdCoercer } from \"../../common/ids.js\";\nimport { throwIfAborted } from \"../../common/input.js\";\nimport {\n reportSinkOptions,\n reportUnusedOptions,\n type ResolvedImportOptions,\n resolveImportOptions,\n} from \"../../common/options.js\";\nimport { ImportReportBuilder } from \"../../common/report.js\";\nimport { parseWeightText } from \"../../common/weights.js\";\nimport { type CommonImportOptions, type GraphImporter, type ImportInput, type ImportReport } from \"../../types.js\";\nimport {\n type CsvColumnRef,\n EDGE_ID_NAMES,\n findColumn,\n headerNames,\n ID_NAMES,\n LABEL_NAMES,\n looksLikeHeader,\n positionalNames,\n resolveColumnRef,\n SOURCE_NAMES,\n TARGET_NAMES,\n TYPE_NAME,\n} from \"./header.js\";\nimport { type CsvReaderOptions, CsvRecordReader, sniffDelimiter, sniffNewline } from \"./records.js\";\nimport { InferredColumn } from \"./values.js\";\n\n/** The format-specific options of the CSV importer. */\nexport interface CsvImportOptions {\n /** The field delimiter; sniffed from the first rows when omitted (`,`, tab, `;`, `|`, space). */\n delimiter?: string | undefined;\n /** Whether the first row is a header; \"auto\" (default) decides from its content. */\n header?: boolean | \"auto\" | undefined;\n /**\n * What the input is: an edge table, a node table, an adjacency table (`node,neighbour[:weight],...`\n * per row, no header by default), or \"auto\" (default): an edge table when source and target\n * columns resolve, a node table when only an id column does. An adjacency table is never\n * guessed: nothing in its rows tells it from an edge list.\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\" | undefined;\n /** The source column, by name or 0-based position; resolved from the header by default. */\n sourceColumn?: CsvColumnRef | undefined;\n /** The target column, by name or 0-based position; resolved from the header by default. */\n targetColumn?: CsvColumnRef | undefined;\n /**\n * The per-row direction column (Directed / Undirected / Mutual); by default the exact `Type`\n * column of a Gephi table (exact `Source` and `Target` headers); null reads no such column.\n */\n typeColumn?: CsvColumnRef | null | undefined;\n /** The id column of a node table, by name or position; resolved from the header by default. */\n idColumn?: CsvColumnRef | undefined;\n /** A node table read before the edges: its ids become nodes and its other columns node attributes. */\n nodes?: ImportInput | undefined;\n /**\n * A node table whose header has no id column gets its ids from the row numbers (0 for the first\n * data row), coerced by `ids`, instead of failing with E_CSV_NO_ID_COLUMN. The node table is the\n * `nodes` input when one is given (the edge table is then read as without this option), else\n * the input itself; its first row is a header even under `header: \"auto\"`. False by default.\n */\n rowNumberIds?: boolean | undefined;\n}\n\n/** Issue code: the input holds no header row at all. */\nexport const EMPTY_INPUT_CODE = SHARED_EMPTY_INPUT_CODE;\n/** Issue code: the header names no source / target (or, for a node table, no id) column. */\nexport const NO_ENDPOINT_COLUMNS_CODE = \"E_CSV_NO_ENDPOINT_COLUMNS\";\n/** Issue code: a node table without an id column. */\nexport const NO_ID_COLUMN_CODE = \"E_CSV_NO_ID_COLUMN\";\n/** Issue code: a row with a different number of fields than the header. */\nexport const FIELD_COUNT_CODE = \"E_CSV_FIELD_COUNT\";\n/** Issue code: an edge row with a blank source or target cell. */\nexport const MISSING_ENDPOINT_CODE = SHARED_MISSING_ENDPOINT_CODE;\n/** Issue code: a node row with a blank id cell. */\nexport const MISSING_ID_CODE = SHARED_MISSING_ID_CODE;\n/** Issue code: a Type cell that is not Directed, Undirected or Mutual. */\nexport const BAD_TYPE_CODE = \"E_CSV_BAD_TYPE\";\n/** Issue code: the table has a header and no data rows. */\nexport const NO_DATA_ROWS_CODE = \"W_CSV_NO_DATA_ROWS\";\n/** Issue code: a node table row repeats an id; its attributes overwrite the earlier row's. */\nexport const DUPLICATE_NODE_CODE = SHARED_DUPLICATE_NODE_CODE;\n/** Issue code: two distinct id cells became one id under `ids: \"number\"` (design section 4.1). */\nexport const ID_MERGED_CODE = SHARED_ID_MERGED_CODE;\n/** Issue code: an explicitly named weight column the file does not have. */\nexport const COLUMN_MISSING_CODE = \"W_CSV_COLUMN_MISSING\";\n/** Issue code: a column whose role (id, label) is already held by another column of the sink. */\nexport const ROLE_TAKEN_CODE = SHARED_ROLE_TAKEN_CODE;\n/** Issue code: a repeated edge id (the column is unique); the edge is skipped. */\nexport const DUPLICATE_EDGE_ID_CODE = SHARED_DUPLICATE_EDGE_ID_CODE;\n\nconst TABLE_MODES: ReadonlySet<string> = new Set([\"edges\", \"nodes\", \"adjacency\", \"auto\"]);\n\n/** The common options an edge-table import reads (the rest is reported by reportUnusedOptions). */\nconst USED_OPTIONS: ReadonlySet<keyof CommonImportOptions> = new Set<keyof CommonImportOptions>([\n \"ids\",\n \"addMissingNodes\",\n \"duplicateEdges\",\n \"selfLoops\",\n \"onMixedDirection\",\n \"defaultDirected\",\n \"weightFrom\",\n \"weightDtype\",\n \"errorLimit\",\n \"signal\",\n \"onProgress\",\n]);\n\n/** The common options an import with a node table reads: nodeIdFrom applies to the node table. */\nconst USED_OPTIONS_WITH_NODES: ReadonlySet<keyof CommonImportOptions> = new Set<keyof CommonImportOptions>([\n ...USED_OPTIONS,\n \"nodeIdFrom\",\n]);\nconst BAD_DELIMITERS: ReadonlySet<string> = new Set(['\"', \"\\n\", \"\\r\"]);\n\n/** The CSV options with defaults applied. */\ninterface ResolvedCsvOptions {\n readonly delimiter: string | null;\n readonly header: boolean | \"auto\";\n readonly table: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\";\n readonly sourceColumn: CsvColumnRef | null;\n readonly targetColumn: CsvColumnRef | null;\n /** The direction column reference; null for none; undefined for the Gephi rule. */\n readonly typeColumn: CsvColumnRef | null | undefined;\n readonly idColumn: CsvColumnRef | null;\n readonly nodes: ImportInput | null;\n readonly rowNumberIds: boolean;\n}\n\n/** The columns of an edge table, by index. */\ninterface EdgePlan {\n readonly kind: \"edges\";\n readonly names: readonly string[];\n readonly width: number;\n readonly source: number;\n readonly target: number;\n readonly weight: number;\n readonly type: number;\n readonly id: number;\n readonly label: number;\n readonly attributes: readonly number[];\n}\n\n/** The columns of a node table, by index. */\ninterface NodePlan {\n readonly kind: \"nodes\";\n readonly names: readonly string[];\n readonly width: number;\n /** The column ids are read from, or -1 under nodeIdFrom \"index\" or rowNumberIds. */\n readonly id: number;\n /** Whether a row's id is its row number coerced by `ids` (rowNumberIds); else the raw ordinal under -1. */\n readonly rowNumber: boolean;\n readonly label: number;\n readonly attributes: readonly number[];\n}\n\n/** An adjacency table: no columns, a node and its neighbours per row. */\ninterface AdjacencyPlan {\n readonly kind: \"adjacency\";\n readonly names: readonly string[];\n}\n\n/** Everything one import call shares between its tables. */\ninterface ImportState {\n readonly sink: GraphSink;\n readonly report: ImportReportBuilder;\n readonly common: ResolvedImportOptions;\n readonly csv: ResolvedCsvOptions;\n readonly weightFromExplicit: boolean;\n readonly coercer: IdCoercer;\n readonly resolver: DirectionResolver;\n headerSet: boolean;\n /**\n * The file-level direction a SNAP (`# Directed graph` / `# Undirected graph`) or KONECT\n * (`% asym` / `% sym` / `% bip`) comment header declares; null when the file declares none\n * (the `defaultDirected` option applies).\n */\n commentDirected: boolean | null;\n}\n\n/** The comment characters of the SNAP (`#`) and KONECT (`%`) headers (research note 07 section 2.6). */\nconst COMMENT_CHARS: readonly string[] = Object.freeze([\"#\", \"%\"]);\n\n/**\n * The direction a SNAP or KONECT comment header declares (research note 07 section 2.6): SNAP\n * pages write `# Directed graph` / `# Undirected graph`, KONECT's first line is `% sym` (undirected),\n * `% asym` (directed) or `% bip` (bipartite, undirected).\n * @param comments - the leading comment lines\n * @returns true / false when a line declares the direction, null otherwise\n */\nfunction commentDirection(comments: readonly string[]): boolean | null {\n for (const comment of comments) {\n const text = comment.slice(1).trim().toLowerCase();\n if (text.startsWith(\"directed graph\") || text.startsWith(\"asym\")) {\n return true;\n }\n if (text.startsWith(\"undirected graph\") || text.startsWith(\"sym\") || text.startsWith(\"bip\")) {\n return false;\n }\n }\n return null;\n}\n\n/**\n * Apply the defaults of the CSV options and check every value.\n * @param options - the caller's options\n * @returns the resolved options; E_UNSUPPORTED for a value outside its set\n */\nfunction resolveCsvOptions(options: (CsvImportOptions & CommonImportOptions) | undefined): ResolvedCsvOptions {\n const o: CsvImportOptions = options ?? {};\n if (o.delimiter !== undefined && (typeof o.delimiter !== \"string\" || o.delimiter.length === 0)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option delimiter: expected a non-empty string\", {\n option: \"delimiter\",\n found: o.delimiter,\n });\n }\n if (o.delimiter !== undefined && BAD_DELIMITERS.has(o.delimiter)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option delimiter: a quote or a line break cannot delimit\", {\n option: \"delimiter\",\n found: o.delimiter,\n });\n }\n if (o.header !== undefined && o.header !== \"auto\" && typeof o.header !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option header: expected true, false or \"auto\"', {\n option: \"header\",\n found: o.header,\n });\n }\n if (o.table !== undefined && !TABLE_MODES.has(o.table)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option table: expected \"edges\", \"nodes\", \"adjacency\" or \"auto\"', {\n option: \"table\",\n found: o.table,\n });\n }\n for (const name of [\"sourceColumn\", \"targetColumn\", \"idColumn\"] as const) {\n checkColumnRef(name, o[name]);\n }\n if (o.typeColumn !== null) {\n checkColumnRef(\"typeColumn\", o.typeColumn);\n }\n if (o.table === \"adjacency\") {\n for (const name of [\"sourceColumn\", \"targetColumn\", \"typeColumn\", \"idColumn\"] as const) {\n if (o[name] !== undefined) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${name}: an adjacency table has no columns`, {\n option: name,\n found: o[name],\n });\n }\n }\n if (o.rowNumberIds === true) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option rowNumberIds: an adjacency table names its nodes\", {\n option: \"rowNumberIds\",\n found: o.rowNumberIds,\n });\n }\n }\n if (o.rowNumberIds !== undefined && typeof o.rowNumberIds !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option rowNumberIds: expected a boolean\", {\n option: \"rowNumberIds\",\n found: o.rowNumberIds,\n });\n }\n return {\n delimiter: o.delimiter ?? null,\n header: o.header ?? \"auto\",\n table: o.table ?? \"auto\",\n sourceColumn: o.sourceColumn ?? null,\n targetColumn: o.targetColumn ?? null,\n typeColumn: o.typeColumn,\n idColumn: o.idColumn ?? null,\n nodes: o.nodes ?? null,\n rowNumberIds: o.rowNumberIds ?? false,\n };\n}\n\n/**\n * Check a column reference option: a string name or a non-negative integer position.\n * @param name - the option name\n * @param value - the value\n */\nfunction checkColumnRef(name: string, value: unknown): void {\n if (value === undefined) {\n return;\n }\n if (typeof value === \"string\" && value.length > 0) {\n return;\n }\n if (typeof value === \"number\" && Number.isInteger(value) && value >= 0) {\n return;\n }\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${name}: expected a column name or a 0-based position`, {\n option: name,\n found: value,\n });\n}\n\n/**\n * Whether a cell is empty (nothing or whitespace only): an unset value, a missing endpoint.\n * @param text - the cell text\n * @returns true when blank\n */\nfunction isBlank(text: string): boolean {\n return text.length === 0 || text.trim().length === 0;\n}\n\n/**\n * Whether a cell is unset: blank and not quoted. A quoted blank cell (`\"\"`, `\" \"`) is a set value\n * (the empty string is a legal id and a legal value, design section 4.1); an unquoted one is\n * nothing at all.\n * @param text - the cell text\n * @param quoted - whether the cell was quoted\n * @returns true when the cell carries no value\n */\nfunction isUnset(text: string, quoted: boolean | undefined): boolean {\n return quoted !== true && isBlank(text);\n}\n\n/** Rows between two checks of the cancellation signal on an in-memory input. */\nconst ABORT_CHECK_INTERVAL = 64;\n\n/**\n * Header names made unique within the table: a repeated name becomes `<name>#<position>` (1-based,\n * the CSV analogue of the `#<origin.id>` rule of design section 5.6) and is reported.\n * @param names - the header names\n * @param report - the report\n * @param line - the header line\n * @returns the unique names\n */\nfunction uniqueNames(names: readonly string[], report: ImportReportBuilder, line: number): string[] {\n const seen = new Set<string>();\n return names.map((name, i) => {\n const unique = uniqueColumnName(name, String(i + 1), (n) => seen.has(n));\n seen.add(unique);\n if (unique !== name) {\n report.warning(\n \"coercion\",\n RENAMED_CODE,\n `column ${i + 1} \"${name}\" renamed to \"${unique}\": the header repeats the name`,\n { line, element: name },\n );\n }\n return unique;\n });\n}\n\n/**\n * Find a column by candidates, skipping indices already claimed by another role.\n * @param names - the header names\n * @param candidates - the names to look for\n * @param claimed - indices taken\n * @returns the index, or -1\n */\nfunction findFree(names: readonly string[], candidates: readonly string[], claimed: ReadonlySet<number>): number {\n const masked = names.map((name, i) => (claimed.has(i) ? \"\" : name));\n return findColumn(masked, candidates);\n}\n\n/**\n * Declare a role column (edge id, label) on the sink through the shared design section 5.6 rule:\n * a name already declared differently is renamed `<name>#<position>` (reported); a role already\n * held by another column is dropped from this declaration (reported), the values are kept under\n * the name.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the declaration\n * @param position - the 1-based column position, for the rename\n * @param report - the report\n * @param line - the header line\n * @returns the handle\n */\nfunction declareRoleColumn(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n decl: ColumnDecl,\n position: number,\n report: ImportReportBuilder,\n line: number,\n): ColumnHandle {\n const withOrigin: ColumnDecl = { ...decl, origin: { ...decl.origin, id: String(position) } };\n const resolved = declareResolved(sink, domain, withOrigin, report, { line, element: decl.name });\n if (resolved.roleDropped) {\n // a unique constraint belongs to the id role; without the role the column is plain text\n return resolved.handle;\n }\n return resolved.handle;\n}\n\n/**\n * Parse a Gephi Type cell.\n * @param text - the cell text\n * @returns the kind; undefined for a blank cell (the default applies); null for an unknown word\n */\nfunction parseKind(text: string): EdgeKind | null | undefined {\n if (isBlank(text)) {\n return undefined;\n }\n switch (text.trim().toLowerCase()) {\n case \"directed\":\n return \"directed\";\n case \"undirected\":\n return \"undirected\";\n case \"mutual\":\n return \"mutual\";\n default:\n return null;\n }\n}\n\n/**\n * Split an adjacency neighbour cell into its id and weight: the text after the LAST colon is the\n * weight when it is a number, nothing when it is empty (`a:1:` is the id `a:1`, unweighted, the\n * form the exporter writes for an id containing a colon), and part of the id otherwise.\n * @param text - the cell text\n * @returns the id text and the weight (undefined when the cell has none)\n */\nexport function splitNeighbour(text: string): { readonly id: string; readonly weight: number | undefined } {\n const colon = text.lastIndexOf(\":\");\n if (colon < 0) {\n return { id: text, weight: undefined };\n }\n const suffix = text.slice(colon + 1);\n if (suffix.length === 0) {\n return { id: text.slice(0, colon), weight: undefined };\n }\n const weight = numberOrNull(suffix);\n return weight === null ? { id: text, weight: undefined } : { id: text.slice(0, colon), weight };\n}\n\n/**\n * A weight suffix as a number, or null when the text is not one.\n * @param text - the suffix\n * @returns the number, or null\n */\nfunction numberOrNull(text: string): number | null {\n try {\n return parseWeightText(text) ?? null;\n } catch {\n // not a number: the colon belongs to the id\n return null;\n }\n}\n\n/**\n * Read one CSV table into the sink.\n */\nclass TableReader {\n private readonly state: ImportState;\n\n private readonly reader: CsvRecordReader;\n\n private readonly kind: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\";\n\n /**\n * Whether rowNumberIds applies to this table: a node table, or an \"auto\" table with no paired\n * node table. Never the edge table of a paired import, which must still fail loudly when its\n * header names no endpoints.\n */\n private readonly rowNumbers: boolean;\n\n private plan: EdgePlan | NodePlan | AdjacencyPlan | null = null;\n\n private writers: (InferredColumn | null)[] = [];\n\n private idHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n private labelHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n private dataRows = 0;\n\n private nodeOrdinal = 0;\n\n /** The edge ids seen so far (the id column is unique; a repeat is skipped with an issue). */\n private readonly edgeIds = new Set<string>();\n\n private readonly where: { line: number | null; element: string | null } = { line: null, element: null };\n\n /**\n * Create a table reader.\n * @param state - the import state\n * @param input - the table's input\n * @param kind - what the table is, or \"auto\"\n * @param progress - whether this table reports byte progress\n */\n constructor(\n state: ImportState,\n input: ImportInput,\n kind: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\",\n progress: boolean,\n ) {\n this.state = state;\n this.kind = kind;\n this.rowNumbers = state.csv.rowNumberIds && (kind === \"nodes\" || (kind === \"auto\" && state.csv.nodes === null));\n const readerOptions: CsvReaderOptions = {\n delimiter: state.csv.delimiter,\n comments: COMMENT_CHARS,\n // field counts say nothing about an adjacency table, whose rows vary in width\n skipQuoteErrors: kind === \"adjacency\",\n signal: state.common.signal,\n onProgress: progress ? state.common.onProgress : null,\n encoding: state.common.encoding,\n };\n this.reader = new CsvRecordReader(input, state.report, readerOptions);\n }\n\n /**\n * Read every row; the reader is closed (and a stream cancelled) when the import aborts midway.\n */\n async read(): Promise<void> {\n const iterator = this.reader[Symbol.asyncIterator]();\n try {\n await this.readRows(iterator);\n } finally {\n await iterator.return(undefined);\n }\n }\n\n /**\n * Read the header (or decide there is none), resolve the plan, then push every row.\n * @param iterator - the record iterator\n */\n private async readRows(iterator: AsyncGenerator<string[], void, undefined>): Promise<void> {\n const { report } = this.state;\n const first = await iterator.next();\n if (first.done && this.kind === \"adjacency\") {\n // an adjacency table has no header: an empty one is the empty graph\n return;\n }\n const firstRow: string[] = first.done\n ? report.fail(EMPTY_INPUT_CODE, \"the input is empty: no header row and no records\")\n : first.value;\n const firstLine = this.reader.line;\n const firstQuoted = this.reader.quoted.slice(0, firstRow.length);\n const pending: { row: string[]; quoted: readonly boolean[]; line: number }[] = [];\n let header: boolean;\n // an adjacency table has no header unless the caller says so: its rows vary in width; a\n // table numbered by rowNumberIds has one, since a headerless table takes its ids from column 0\n let mode = this.state.csv.header;\n if (mode === \"auto\" && this.kind === \"adjacency\") {\n mode = false;\n } else if (mode === \"auto\" && this.rowNumbers) {\n mode = true;\n }\n if (mode === \"auto\") {\n const second = await iterator.next();\n const secondRow: string[] | null = second.done ? null : second.value;\n const secondLine = this.reader.line;\n const secondQuoted = this.reader.quoted.slice(0, secondRow?.length ?? 0);\n header = looksLikeHeader(firstRow, secondRow);\n if (!header) {\n pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });\n }\n if (secondRow !== null) {\n pending.push({ row: secondRow, quoted: secondQuoted, line: secondLine });\n }\n } else {\n header = mode;\n if (!header) {\n pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });\n }\n }\n const names = header ? uniqueNames(headerNames(firstRow), report, firstLine) : positionalNames(firstRow.length);\n if (this.kind !== \"nodes\" && this.state.commentDirected === null) {\n this.state.commentDirected = commentDirection(this.reader.leadingComments);\n }\n this.plan = this.resolvePlan(names, header, firstLine);\n this.prepareColumns(firstLine);\n for (const { row, quoted, line } of pending) {\n this.processRow(row, quoted, line);\n }\n const { signal } = this.state.common;\n let sinceCheck = 0;\n for (;;) {\n const next = await iterator.next();\n if (next.done) {\n break;\n }\n this.processRow(next.value, this.reader.quoted, this.reader.line);\n if (++sinceCheck >= ABORT_CHECK_INTERVAL) {\n sinceCheck = 0;\n throwIfAborted(signal);\n }\n }\n for (const writer of this.writers) {\n writer?.finish();\n }\n if (this.dataRows === 0 && header) {\n report.warning(\"missing-value\", NO_DATA_ROWS_CODE, \"the table has a header and no data rows\", {\n line: firstLine,\n });\n }\n }\n\n /**\n * Decide the table's columns from its header.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param line - the header line\n * @returns the plan; the import aborts when no endpoints (or id) resolve\n */\n private resolvePlan(names: readonly string[], header: boolean, line: number): EdgePlan | NodePlan | AdjacencyPlan {\n const { csv, report } = this.state;\n if (this.kind === \"adjacency\") {\n return { kind: \"adjacency\", names: [] };\n }\n const width = names.length;\n let source = -1;\n let target = -1;\n if (this.kind !== \"nodes\") {\n if (csv.sourceColumn !== null) {\n source = resolveColumnRef(names, csv.sourceColumn, \"sourceColumn\");\n } else if (header) {\n source = findColumn(names, SOURCE_NAMES);\n } else {\n source = width >= 2 ? 0 : -1;\n }\n if (csv.targetColumn !== null) {\n target = resolveColumnRef(names, csv.targetColumn, \"targetColumn\");\n } else if (header) {\n target = findColumn(names, TARGET_NAMES);\n } else {\n target = width >= 2 ? 1 : -1;\n }\n if (source >= 0 && target >= 0 && source === target) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"sourceColumn and targetColumn name the same column\", {\n option: \"targetColumn\",\n found: names[target],\n });\n }\n }\n if (source >= 0 && target >= 0) {\n return this.edgePlan(names, header, source, target);\n }\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n if (\n this.kind === \"edges\" ||\n (this.kind === \"auto\" && (csv.sourceColumn !== null || csv.targetColumn !== null))\n ) {\n report.fail(\n NO_ENDPOINT_COLUMNS_CODE,\n `no source / target columns in the header (${shown}); a node table goes in the nodes option`,\n { line },\n { columns: [...names] },\n );\n }\n const idResolves = header ? findColumn(names, ID_NAMES) >= 0 : width >= 1;\n if (this.kind === \"auto\" && csv.idColumn === null && !idResolves && !this.rowNumbers) {\n report.fail(\n NO_ENDPOINT_COLUMNS_CODE,\n `no source / target columns and no id column in the header (${shown}); the input is neither an edge table nor a node table`,\n { line },\n { columns: [...names] },\n );\n }\n return this.nodePlan(names, header, line);\n }\n\n /**\n * The columns of an edge table.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param source - the source column\n * @param target - the target column\n * @returns the plan\n */\n private edgePlan(names: readonly string[], header: boolean, source: number, target: number): EdgePlan {\n const { csv, common, report } = this.state;\n const claimed = new Set<number>([source, target]);\n let weight = -1;\n if (common.weightFrom !== null) {\n if (header) {\n weight = findFree(names, [common.weightFrom], claimed);\n if (weight < 0 && this.state.weightFromExplicit) {\n report.warning(\n \"missing-value\",\n COLUMN_MISSING_CODE,\n `weight column ${JSON.stringify(common.weightFrom)} not found; edges are unweighted`,\n { line: this.reader.line, element: common.weightFrom },\n );\n }\n } else if (names.length >= 3 && !claimed.has(2)) {\n weight = 2;\n }\n }\n if (weight >= 0) {\n claimed.add(weight);\n }\n let type = -1;\n if (csv.typeColumn === undefined) {\n if (header && names[source] === \"Source\" && names[target] === \"Target\") {\n type = findFree(names, [TYPE_NAME], claimed);\n if (type >= 0 && names[type] !== TYPE_NAME) {\n type = -1;\n }\n }\n } else if (csv.typeColumn !== null) {\n type = resolveColumnRef(names, csv.typeColumn, \"typeColumn\");\n if (claimed.has(type)) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"typeColumn names an endpoint or weight column\", {\n option: \"typeColumn\",\n found: names[type],\n });\n }\n }\n if (type >= 0) {\n claimed.add(type);\n }\n const id = header ? findFree(names, EDGE_ID_NAMES, claimed) : -1;\n if (id >= 0) {\n claimed.add(id);\n }\n const label = header ? findFree(names, LABEL_NAMES, claimed) : -1;\n if (label >= 0) {\n claimed.add(label);\n }\n const attributes: number[] = [];\n for (let i = 0; i < names.length; i++) {\n if (!claimed.has(i)) {\n attributes.push(i);\n }\n }\n return { kind: \"edges\", names, width: names.length, source, target, weight, type, id, label, attributes };\n }\n\n /**\n * The columns of a node table.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param line - the header line\n * @returns the plan; the import aborts when no id column resolves\n */\n private nodePlan(names: readonly string[], header: boolean, line: number): NodePlan {\n const { csv, common, report } = this.state;\n const claimed = new Set<number>();\n let idColumn = -1;\n if (csv.idColumn !== null) {\n idColumn = resolveColumnRef(names, csv.idColumn, \"idColumn\");\n } else if (header) {\n idColumn = findColumn(names, ID_NAMES);\n } else if (names.length >= 1) {\n idColumn = 0;\n }\n const label = header ? findFree(names, LABEL_NAMES, new Set(idColumn >= 0 ? [idColumn] : [])) : -1;\n let id: number;\n switch (common.nodeIdFrom) {\n case \"label\":\n if (label < 0) {\n report.fail(NO_ID_COLUMN_CODE, 'nodeIdFrom is \"label\" but the node table has no label column', {\n line,\n });\n }\n id = label;\n break;\n case \"index\":\n id = -1;\n break;\n default:\n if (idColumn < 0 && this.rowNumbers) {\n id = -1;\n break;\n }\n if (idColumn < 0) {\n report.fail(\n NO_ID_COLUMN_CODE,\n `no id column in the node table header (${names.map((n) => JSON.stringify(n)).join(\", \")})`,\n { line },\n { columns: [...names] },\n );\n }\n id = idColumn;\n claimed.add(idColumn);\n break;\n }\n if (label >= 0) {\n claimed.add(label);\n }\n const attributes: number[] = [];\n for (let i = 0; i < names.length; i++) {\n if (!claimed.has(i)) {\n attributes.push(i);\n }\n }\n const rowNumber = id < 0 && common.nodeIdFrom !== \"index\";\n return { kind: \"nodes\", names, width: names.length, id, rowNumber, label, attributes };\n }\n\n /**\n * Declare the role columns and create a writer per attribute column.\n * @param line - the header line\n */\n private prepareColumns(line: number): void {\n const plan = this.requirePlan();\n if (plan.kind === \"adjacency\") {\n return;\n }\n const { sink, report } = this.state;\n const domain = plan.kind === \"edges\" ? \"edge\" : \"node\";\n const origin = { format: \"csv\" };\n if (plan.kind === \"edges\" && plan.id >= 0) {\n this.idHandle = declareRoleColumn(\n sink,\n \"edge\",\n { name: plan.names[plan.id], dtype: \"string\", nullable: true, role: \"id\", unique: true, origin },\n plan.id + 1,\n report,\n line,\n );\n }\n if (plan.label >= 0) {\n this.labelHandle = declareRoleColumn(\n sink,\n domain,\n { name: plan.names[plan.label], dtype: \"string\", nullable: true, role: \"label\", origin },\n plan.label + 1,\n report,\n line,\n );\n }\n this.writers = plan.names.map(() => null);\n for (const index of plan.attributes) {\n this.writers[index] = new InferredColumn(plan.names[index], domain, sink, report);\n }\n }\n\n /**\n * The plan, which exists once the header was read.\n * @returns the plan\n */\n private requirePlan(): EdgePlan | NodePlan | AdjacencyPlan {\n if (this.plan === null) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"the header has not been read\", { reason: \"no plan\" });\n }\n return this.plan;\n }\n\n /**\n * Push one data row.\n * @param row - the cells\n * @param quoted - whether each cell was quoted (a quoted empty cell is the empty string)\n * @param line - the row's line\n */\n private processRow(row: string[], quoted: readonly boolean[], line: number): void {\n const plan = this.requirePlan();\n this.dataRows++;\n if (plan.kind === \"edges\") {\n this.processEdgeRow(plan, row, quoted, line);\n } else if (plan.kind === \"nodes\") {\n this.processNodeRow(plan, row, quoted, line);\n } else {\n this.processAdjacencyRow(row, quoted, line);\n }\n }\n\n /**\n * Push one adjacency row: the node, then one edge per set neighbour cell, in row order.\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processAdjacencyRow(row: string[], quoted: readonly boolean[], line: number): void {\n const { report, sink, resolver, common } = this.state;\n const { counts } = report;\n const { where } = this;\n where.line = line;\n where.element = null;\n let neighbours = 0;\n for (let k = 1; k < row.length; k++) {\n if (!isUnset(row[k], quoted[k])) {\n neighbours++;\n }\n }\n if (isUnset(row[0], quoted[0])) {\n report.error(\"missing-value\", MISSING_ID_CODE, `line ${line}: blank node cell`, { line });\n counts.skippedNodes++;\n counts.skippedEdges += neighbours;\n return;\n }\n const kind: EdgeKind = (this.state.commentDirected ?? common.defaultDirected) ? \"directed\" : \"undirected\";\n if (!this.state.headerSet) {\n this.state.headerSet = true;\n resolver.setHeader(kind !== \"undirected\", where);\n }\n let source: NodeId;\n try {\n where.element = row[0];\n source = this.coerce(row[0]);\n if (sink.indexOf(source) === INVALID_INDEX) {\n counts.nodes++;\n }\n sink.addNode(source);\n } catch (err) {\n report.recordError(err, where);\n counts.skippedNodes++;\n counts.skippedEdges += neighbours;\n return;\n }\n for (let k = 1; k < row.length; k++) {\n if (isUnset(row[k], quoted[k])) {\n continue;\n }\n where.element = row[k];\n try {\n const cell = splitNeighbour(row[k]);\n const target = this.coerce(cell.id);\n const targetNew = sink.indexOf(target) === INVALID_INDEX;\n const before = sink.edgeCount;\n resolver.addEdge(source, target, kind, common.weightFrom === null ? undefined : cell.weight, where);\n counts.edges += sink.edgeCount - before;\n counts.nodes += targetNew ? 1 : 0;\n } catch (err) {\n report.recordError(err, where);\n counts.skippedEdges++;\n }\n }\n }\n\n /**\n * Push one edge row: endpoints, weight, direction, then the attribute cells.\n * @param plan - the edge plan\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processEdgeRow(plan: EdgePlan, row: string[], quoted: readonly boolean[], line: number): void {\n const { report, sink, resolver, common } = this.state;\n const { counts } = report;\n if (row.length !== plan.width) {\n report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `line ${line}: ${row.length} field(s), the header has ${plan.width}`,\n { line },\n );\n counts.skippedEdges++;\n return;\n }\n const sourceText = row[plan.source];\n const targetText = row[plan.target];\n const sourceMissing = isUnset(sourceText, quoted[plan.source]);\n if (sourceMissing || isUnset(targetText, quoted[plan.target])) {\n report.error(\n \"missing-value\",\n MISSING_ENDPOINT_CODE,\n `line ${line}: blank ${sourceMissing ? \"source\" : \"target\"} cell`,\n { line },\n );\n counts.skippedEdges++;\n return;\n }\n let kind: EdgeKind = (this.state.commentDirected ?? common.defaultDirected) ? \"directed\" : \"undirected\";\n if (plan.type >= 0) {\n const parsed = parseKind(row[plan.type]);\n if (parsed === null) {\n report.error(\n \"validation-error\",\n BAD_TYPE_CODE,\n `line ${line}: Type ${JSON.stringify(row[plan.type])} is not Directed, Undirected or Mutual`,\n { line },\n );\n counts.skippedEdges++;\n return;\n }\n if (parsed !== undefined) {\n kind = parsed;\n }\n }\n const { where } = this;\n where.line = line;\n where.element = null;\n const idText = plan.id >= 0 && !isUnset(row[plan.id], quoted[plan.id]) ? row[plan.id] : null;\n if (idText !== null) {\n if (this.edgeIds.has(idText)) {\n report.error(\n \"validation-error\",\n DUPLICATE_EDGE_ID_CODE,\n `line ${line}: edge id ${JSON.stringify(idText)} repeats an earlier row's; the row is skipped`,\n { line, element: idText },\n );\n counts.skippedEdges++;\n return;\n }\n this.edgeIds.add(idText);\n }\n let edge: number;\n try {\n const source = this.coerce(sourceText);\n const target = this.coerce(targetText);\n const weight = plan.weight >= 0 ? parseWeightText(row[plan.weight]) : undefined;\n if (!this.state.headerSet) {\n this.state.headerSet = true;\n resolver.setHeader(kind !== \"undirected\", where);\n }\n const sourceNew = sink.indexOf(source) === INVALID_INDEX;\n const targetNew = source !== target && sink.indexOf(target) === INVALID_INDEX;\n const before = sink.edgeCount;\n edge = resolver.addEdge(source, target, kind, weight, where);\n counts.edges += sink.edgeCount - before;\n counts.nodes += (sourceNew ? 1 : 0) + (targetNew ? 1 : 0);\n } catch (err) {\n where.element = idText ?? `${sourceText}->${targetText}`;\n report.recordError(err, where);\n counts.skippedEdges++;\n return;\n }\n if (idText !== null) {\n this.writeRole(this.idHandle, \"edge\", edge, idText, plan.names[plan.id], line);\n }\n if (plan.label >= 0 && !isUnset(row[plan.label], quoted[plan.label])) {\n this.writeRole(this.labelHandle, \"edge\", edge, row[plan.label], plan.names[plan.label], line);\n }\n this.writeAttributes(plan, row, quoted, edge, line);\n }\n\n /**\n * Push one node row: the id, then the label and attribute cells.\n * @param plan - the node plan\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processNodeRow(plan: NodePlan, row: string[], quoted: readonly boolean[], line: number): void {\n const { report, sink } = this.state;\n const { counts } = report;\n const ordinal = this.nodeOrdinal++;\n if (row.length !== plan.width) {\n report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `line ${line}: ${row.length} field(s), the header has ${plan.width}`,\n { line },\n );\n counts.skippedNodes++;\n return;\n }\n const idText = plan.id >= 0 ? row[plan.id] : String(ordinal);\n if (plan.id >= 0 && isUnset(idText, quoted[plan.id])) {\n report.error(\"missing-value\", MISSING_ID_CODE, `line ${line}: blank id cell`, { line });\n counts.skippedNodes++;\n return;\n }\n const { where } = this;\n where.line = line;\n where.element = idText;\n let index: number;\n try {\n const id = plan.id >= 0 || plan.rowNumber ? this.coerce(idText) : ordinal;\n if (sink.indexOf(id) !== INVALID_INDEX) {\n report.warning(\n \"merged\",\n DUPLICATE_NODE_CODE,\n `line ${line}: node ${JSON.stringify(id)} already exists; its attributes are overwritten`,\n where,\n );\n } else {\n counts.nodes++;\n }\n index = sink.addNode(id);\n } catch (err) {\n report.recordError(err, where);\n counts.skippedNodes++;\n return;\n }\n if (plan.label >= 0 && !isUnset(row[plan.label], quoted[plan.label])) {\n this.writeRole(this.labelHandle, \"node\", index, row[plan.label], plan.names[plan.label], line);\n }\n this.writeAttributes(plan, row, quoted, index, line);\n }\n\n /**\n * Coerce an id cell, reporting a merge under `ids: \"number\"`.\n * @param text - the cell text\n * @returns the id\n */\n private coerce(text: string): NodeId {\n const id = this.state.coercer.text(text);\n const merge = this.state.coercer.lastMerge;\n if (merge !== null) {\n this.state.report.warnOnce(\n \"coercion\",\n ID_MERGED_CODE,\n `id ${JSON.stringify(merge.text)} merged with ${JSON.stringify(merge.previousText)} as ${merge.id} under ids: \"number\"`,\n this.where,\n );\n }\n return id;\n }\n\n /**\n * Write a role column cell (edge id, label).\n * @param handle - the column handle\n * @param domain - node or edge\n * @param row - the node or edge index\n * @param text - the cell text\n * @param name - the column name, for issues\n * @param line - the row's line\n */\n private writeRole(\n handle: ColumnHandle,\n domain: \"node\" | \"edge\",\n row: number,\n text: string,\n name: string,\n line: number,\n ): void {\n try {\n if (domain === \"node\") {\n this.state.sink.setNodeValue(handle, row, text);\n } else {\n this.state.sink.setEdgeValue(handle, row, text);\n }\n } catch (err) {\n this.state.report.recordError(err, { line, element: name });\n }\n }\n\n /**\n * Write the attribute cells of a row: an unquoted blank cell is unset, a quoted one (`\"\"`,\n * `\" \"`) is the text it holds.\n * @param plan - the plan\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @param index - the node or edge index\n * @param line - the row's line\n */\n private writeAttributes(\n plan: EdgePlan | NodePlan,\n row: readonly string[],\n quoted: readonly boolean[],\n index: number,\n line: number,\n ): void {\n for (const k of plan.attributes) {\n const text = row[k];\n if (isUnset(text, quoted[k])) {\n continue;\n }\n const writer = this.writers[k];\n if (writer === null) {\n continue;\n }\n try {\n writer.write(index, text);\n } catch (err) {\n this.state.report.recordError(err, { line, element: writer.name });\n }\n }\n }\n}\n\nconst HEAD_BYTES = 4096;\nconst OTHER_FORMAT = /^\\s*(<|[[{]|(strict\\s+)?(di)?graph(\\s+\\S+)?\\s*\\{|\\*vertices|creator\\b|graph\\s*\\[)/i;\n\n/**\n * Sniff confidence for the registry: 0 for XML, JSON, GML, DOT and Pajek openings; otherwise a\n * delimited first row with endpoint headers is 0.9, with an id header 0.6, any consistently\n * delimited rows 0.3, a single column 0.\n * @param head - the first bytes of the input\n * @returns a confidence in 0..1\n */\nfunction sniff(head: Uint8Array): number {\n const text = new TextDecoder(\"utf-8\").decode(head.subarray(0, HEAD_BYTES));\n const body = text.startsWith(String.fromCharCode(0xfeff)) ? text.slice(1) : text;\n if (body.trim().length === 0 || OTHER_FORMAT.test(body)) {\n return 0;\n }\n const newline = sniffNewline(body);\n const delimiter = sniffDelimiter(body, newline);\n if (delimiter === null) {\n return 0;\n }\n const end = body.indexOf(newline);\n const firstLine = end < 0 ? body : body.slice(0, end);\n const names = headerNames(firstLine.replace(/\\r$/, \"\").split(delimiter));\n if (findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0) {\n return 0.9;\n }\n if (findColumn(names, ID_NAMES) >= 0) {\n return 0.6;\n }\n return 0.3;\n}\n\n/**\n * Import a CSV edge table (and an optional node table) into a sink.\n * @param input - the edge table (or, with `table: \"nodes\"` or a header without endpoints, a node table)\n * @param sink - the sink\n * @param options - CSV and common options\n * @returns the report; ImportError with the partial report when the import aborts\n */\nasync function importCsv(\n input: ImportInput,\n sink: GraphSink,\n options?: CsvImportOptions & CommonImportOptions,\n): Promise<ImportReport> {\n const common = resolveImportOptions(options, { ids: \"canonical\", defaultDirected: true, weightFrom: \"weight\" });\n const csv = resolveCsvOptions(options);\n const report = new ImportReportBuilder(\"csv\", common.errorLimit);\n reportSinkOptions(sink, options, report);\n reportUnusedOptions(\n options,\n report,\n csv.table === \"nodes\" || csv.nodes !== null ? USED_OPTIONS_WITH_NODES : USED_OPTIONS,\n );\n const state: ImportState = {\n sink,\n report,\n common,\n csv,\n weightFromExplicit: typeof options?.weightFrom === \"string\",\n coercer: new IdCoercer(common.ids),\n resolver: new DirectionResolver(sink, report, common.onMixedDirection),\n headerSet: false,\n commentDirected: null,\n };\n if (csv.nodes !== null) {\n await new TableReader(state, csv.nodes, \"nodes\", false).read();\n }\n await new TableReader(state, input, csv.table, true).read();\n if (state.coercer.mergeCount > 1) {\n report.warning(\n \"coercion\",\n ID_MERGED_CODE,\n `${state.coercer.mergeCount} id cell(s) merged into ids other cells already produced under ids: \"number\"`,\n );\n }\n throwIfAborted(common.signal);\n return report.finish();\n}\n\n/** The CSV / TSV importer plugin (subpath `@graphty/graph-io/csv`). */\nexport const csvImporter: GraphImporter<CsvImportOptions> = Object.freeze({\n format: \"csv\",\n extensions: Object.freeze([\".csv\", \".tsv\", \".edges\", \".edgelist\"]),\n mimeTypes: Object.freeze([\"text/csv\", \"text/tab-separated-values\", \"text/plain\"]),\n sniff,\n import: importCsv,\n});\n","/**\n * The CSV / TSV exporter (design section 8.5; research note 07 section 9): writes the edge table\n * (`Source,Target,Type,Id,Label,Weight,<attributes>` in the Gephi dialect, `source,target,weight,\n * <attributes>` in the generic one) or, with `table: \"nodes\"`, the node table (`Id,Label,\n * <attributes>`), RFC 4180 quoted, one row per logical edge with expanded pairs folded back\n * through the `pair` role column; or, with `table: \"adjacency\"`, an adjacency table: a node and its\n * neighbours per row (`id:weight` for an explicit weight), no header, no attribute columns.\n *\n * What survives a re-import exactly: ids (as text under the canonical rule), topology and\n * orientation, explicit weights (blank cells for defaulted ones), the per-row direction of the\n * Gephi dialect, edge ids and labels (string), bool / i32 / f64 / string columns (an f64 value is\n * written with a decimal point so it reads back as f64), low-cardinality dicts, and set empty\n * strings (the quoted empty cell; an unset cell is written as nothing). check() reports everything\n * else: the generic dialect drops direction, mutual pairs become two directed rows, lists and json\n * values are written as text, non-finite numbers do not read back, attribute names that collide\n * with the reserved headers are not written, node attributes are written by the node table only,\n * and the edge table carries neither isolated nodes nor the node order.\n *\n * The adjacency table keeps ids, the node order, isolated nodes, the edge order, orientation and\n * explicit weights: consecutive edges with the same source share a row, a node the rows would\n * otherwise introduce out of index order gets a row of its own first, and a neighbour id holding a\n * colon is written `id:` when it has no weight so the importer does not read its tail as one.\n */\n\nimport { type Column, GraphFormatError, type GraphSnapshot, type NodeId } from \"@graphty/graph-format\";\n\nimport { DICT_SAMPLE_ROWS, DictHeuristic } from \"../../common/attributes.js\";\nimport { type PairFolding, pairFolding } from \"../../common/direction.js\";\nimport { quoteCsvCell } from \"../../common/escape.js\";\nimport { capabilities, checkCapabilities, countMixedEdges, LOSS } from \"../../common/export.js\";\nimport { formatDecimal, formatInteger } from \"../../common/format.js\";\nimport { canonicalId } from \"../../common/ids.js\";\nimport { joinListText } from \"../../common/lists.js\";\nimport { type ResolvedExportOptions, resolveExportOptions } from \"../../common/options.js\";\nimport { inferTextDtype, type TextDtype } from \"../../common/text.js\";\nimport { type ExplicitWeights, explicitWeights } from \"../../common/weights.js\";\nimport { encodeChunks, joinText } from \"../../common/writer.js\";\nimport { type CommonExportOptions, type ExportCapabilities, type GraphExporter, type LossNote } from \"../../types.js\";\nimport { EDGE_ID_NAMES, findColumn, LABEL_NAMES } from \"./header.js\";\nimport { DELIMITER_CANDIDATES } from \"./records.js\";\n\n/** The format-specific options of the CSV exporter. */\nexport interface CsvExportOptions {\n /**\n * The header spelling: \"gephi\" (default) writes `Source,Target,Type,...,Weight` with the per-row\n * direction; \"generic\" writes `source,target,...,weight` and no direction column.\n */\n dialect?: \"gephi\" | \"generic\" | undefined;\n /**\n * Which table to write: the edge table (default), the node table, or an adjacency table (a node\n * and its neighbours per row; read back with the importer's `table: \"adjacency\"`).\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | undefined;\n /** The field delimiter; \",\" by default. */\n delimiter?: string | undefined;\n /** The line terminator; \"\\n\" by default. */\n newline?: \"\\n\" | \"\\r\\n\" | undefined;\n /** Whether to write the header row; true by default (an adjacency table never has one). */\n header?: boolean | undefined;\n}\n\n/** The CSV loss-note codes of check(); the shared ones are LOSS's. */\nexport const CSV_LOSS = Object.freeze({\n /** Two node ids share one text (a number and a string); export() throws E_INVALID_ID. */\n ID_TEXT_COLLISION: LOSS.ID_TEXT_COLLISION,\n /** Ids whose text reads back as the other type under the canonical rule. */\n ID_TEXT_TYPE: LOSS.ID_TEXT_TYPE,\n /**\n * The generic dialect has no direction column; an undirected or mixed graph reads back as\n * directed. The Gephi dialect loses the direction of an undirected graph without edges (no\n * row carries a Type cell).\n */\n DIRECTION_DROPPED: \"W_CSV_DIRECTION_DROPPED\",\n /** Mutual pairs are written as two directed rows. */\n MUTUAL_EXPANDED: LOSS.MUTUAL_EXPANDED,\n /** An attribute column named like a reserved header is not written. */\n RESERVED_NAME: \"W_CSV_RESERVED_NAME\",\n /** A column without a role that the importer gives one back by its name. */\n ROLE_ASSUMED: LOSS.ROLE_ASSUMED,\n /** A role column (id, label) whose name the importer does not recognise; the role is lost. */\n ROLE_NAME: \"W_CSV_ROLE_NAME\",\n /** A role column (id, label) that is not string / dict reads back as string. */\n TEXT_ROLE: \"W_CSV_TEXT_ROLE\",\n /** NaN / Infinity in a numeric column read back as text. */\n NONFINITE: \"W_CSV_NONFINITE\",\n /** A text column whose every value reads back as a number or boolean. */\n TEXT_INFERRED: LOSS.TEXT_INFERRED,\n /** A dict column whose cardinality makes the importer read it back as string, or the reverse. */\n STORAGE_CLASS_CHANGED: LOSS.STORAGE_CLASS,\n /** Node attributes are written by a `table: \"nodes\"` export only. */\n NODE_TABLE: \"W_CSV_NODE_TABLE\",\n /** The edge table carries no node without an edge: isolated nodes vanish on re-import. */\n ISOLATED_NODES: \"W_CSV_ISOLATED_NODES\",\n /** The edge table lists nodes by first appearance; the node order (and indices) change on re-import. */\n NODE_ORDER: \"W_CSV_NODE_ORDER\",\n /** An adjacency table holds no edge column but the weight: edge ids, labels and attributes are not written. */\n EDGE_COLUMNS: \"W_CSV_EDGE_COLUMNS\",\n});\n\n/** What the CSV format keeps as declared. */\nexport const CSV_CAPABILITIES: ExportCapabilities = capabilities({\n mixedDirection: true,\n multiEdges: true,\n selfLoops: true,\n edgeIds: \"optional\",\n idCharset: \"any\",\n dtypes: [\"bool\", \"i32\", \"f64\", \"string\", \"dict\"],\n});\n\n/** The dialect header spellings. */\ninterface Dialect {\n readonly source: string;\n readonly target: string;\n readonly type: string | null;\n readonly id: string;\n readonly label: string;\n readonly weight: string;\n}\n\nconst DIALECTS: Readonly<Record<\"gephi\" | \"generic\", Dialect>> = {\n gephi: { source: \"Source\", target: \"Target\", type: \"Type\", id: \"Id\", label: \"Label\", weight: \"Weight\" },\n generic: { source: \"source\", target: \"target\", type: null, id: \"id\", label: \"label\", weight: \"weight\" },\n};\n\n/** The roles the tables have a slot for (the id and label headers); every other role is reported. */\nconst KEPT_ROLES: ReadonlySet<string> = new Set([\"id\", \"label\"]);\n\n/** Roles the edge and node tables never write as attributes. */\nconst SKIPPED_ROLES: ReadonlySet<string> = new Set([\n \"directed\",\n \"pair\",\n \"mutual\",\n \"weight\",\n \"timeText\",\n \"position\",\n \"color\",\n \"size\",\n \"shape\",\n \"thickness\",\n \"parent\",\n \"parents\",\n \"start\",\n \"end\",\n \"timestamp\",\n \"timestamps\",\n \"spells\",\n \"open\",\n \"spellsOpen\",\n]);\n\n/** The CSV options with defaults applied. */\ninterface ResolvedCsvExportOptions {\n readonly dialect: Dialect;\n readonly table: \"edges\" | \"nodes\" | \"adjacency\";\n readonly delimiter: string;\n readonly newline: string;\n readonly header: boolean;\n}\n\n/** A column written under a header. */\ninterface ColumnOut {\n readonly column: Column;\n readonly header: string;\n}\n\n/** The plan of one export call: what is written, and the notes. */\ninterface Plan {\n readonly csv: ResolvedCsvExportOptions;\n readonly common: ResolvedExportOptions;\n readonly notes: LossNote[];\n /** The rows of the edge table (logical edge indices; mirrors of undirected pairs left out). */\n readonly edgeRows: number[];\n readonly folding: PairFolding;\n readonly edgeId: Column | null;\n readonly edgeLabel: Column | null;\n readonly weights: ExplicitWeights;\n readonly edgeColumns: ColumnOut[];\n readonly nodeLabel: Column | null;\n readonly nodeColumns: ColumnOut[];\n /** The id text of every node. */\n readonly idText: (index: number) => string;\n}\n\n/**\n * Apply the defaults of the CSV export options and check every value.\n * @param options - the caller's options\n * @returns the resolved options; E_UNSUPPORTED for a value outside its set\n */\nfunction resolveCsvExportOptions(\n options: (CsvExportOptions & CommonExportOptions) | undefined,\n): ResolvedCsvExportOptions {\n const o: CsvExportOptions = options ?? {};\n if (o.dialect !== undefined && o.dialect !== \"gephi\" && o.dialect !== \"generic\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option dialect: expected \"gephi\" or \"generic\"', {\n option: \"dialect\",\n found: o.dialect,\n });\n }\n if (o.table !== undefined && o.table !== \"edges\" && o.table !== \"nodes\" && o.table !== \"adjacency\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option table: expected \"edges\", \"nodes\" or \"adjacency\"', {\n option: \"table\",\n found: o.table,\n });\n }\n if (\n o.delimiter !== undefined &&\n (typeof o.delimiter !== \"string\" ||\n o.delimiter.length !== 1 ||\n o.delimiter === '\"' ||\n o.delimiter === \"\\n\" ||\n o.delimiter === \"\\r\")\n ) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n \"option delimiter: expected one character other than a quote or a line break\",\n { option: \"delimiter\", found: o.delimiter },\n );\n }\n if (o.newline !== undefined && o.newline !== \"\\n\" && o.newline !== \"\\r\\n\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option newline: expected LF or CRLF\", {\n option: \"newline\",\n found: o.newline,\n });\n }\n if (o.table === \"adjacency\" && o.header === true) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option header: an adjacency table has no header row\", {\n option: \"header\",\n found: o.header,\n });\n }\n if (o.header !== undefined && typeof o.header !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"option header: expected a boolean\", {\n option: \"header\",\n found: o.header,\n });\n }\n return {\n dialect: DIALECTS[o.dialect ?? \"gephi\"],\n table: o.table ?? \"edges\",\n delimiter: o.delimiter ?? \",\",\n newline: o.newline ?? \"\\n\",\n header: o.header ?? true,\n };\n}\n\n/**\n * One scalar (or one list item) as text by its dtype.\n * @param dtype - the scalar dtype\n * @param value - the value\n * @returns the text\n */\nfunction scalarText(dtype: string, value: unknown): string {\n switch (dtype) {\n case \"bool\":\n return value === true ? \"true\" : \"false\";\n case \"f32\":\n case \"f64\":\n return typeof value === \"number\" ? formatDecimal(value, dtype) : String(value);\n case \"i32\":\n case \"u32\":\n case \"u8\":\n return typeof value === \"number\" ? formatInteger(value) : String(value);\n case \"string\":\n case \"dict\":\n return typeof value === \"string\" ? value : String(value);\n case \"json\":\n return JSON.stringify(value) ?? \"\";\n default:\n return String(value);\n }\n}\n\n/**\n * The text of one cell: null for an unset row (written as nothing between the delimiters);\n * components and list items joined by `;`.\n * @param column - the column\n * @param row - the row\n * @returns the cell text, or null\n */\nfunction cellText(column: Column, row: number): string | null {\n if (!column.isSet(row)) {\n return null;\n }\n switch (column.dtype) {\n case \"list\": {\n const items = column.sliceOf(row);\n const { dtype } = column.child;\n return joinListText(\n items.map((item) => scalarText(dtype, item)),\n \"semicolon\",\n );\n }\n case \"json\":\n return JSON.stringify(column.values[row]) ?? \"\";\n case \"bool\":\n case \"string\":\n case \"dict\":\n return scalarText(column.dtype, column.value(row));\n default: {\n const { components } = column.meta;\n const { data } = column;\n if (components === 1) {\n return scalarText(column.dtype, data[row]);\n }\n const parts: string[] = [];\n for (let k = 0; k < components; k++) {\n parts.push(scalarText(column.dtype, data[row * components + k]));\n }\n return parts.join(\";\");\n }\n }\n}\n\n/**\n * Build the plan of an export: resolve options, choose the rows and columns, and collect the\n * CSV-specific loss notes (check() adds the generic ones). The value-level notes need a pass over\n * every written cell and are collected for check() only; export() needs the plan and the fatal\n * id-collision note alone.\n * @param snapshot - the snapshot\n * @param options - the caller's options\n * @param values - whether to scan the values for notes\n * @returns the plan\n */\n/**\n * The id notes: text collisions are fatal (export() throws E_INVALID_ID); type changes under the\n * canonical re-read are reported.\n * @param snapshot - the snapshot\n * @param values - whether the values are inspected (false for a plan that writes nothing)\n * @param note - the note recorder\n */\nfunction idNotes(\n snapshot: GraphSnapshot,\n values: boolean,\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n): void {\n const { ids } = snapshot;\n if (ids.kind === \"mixed\") {\n const seen = new Set<string>();\n let collisions = 0;\n for (let i = 0; i < ids.size; i++) {\n const text = String(ids.idOf(i));\n if (seen.has(text)) {\n collisions++;\n } else {\n seen.add(text);\n }\n }\n if (collisions > 0) {\n note(\n CSV_LOSS.ID_TEXT_COLLISION,\n `${collisions} node id(s) share their text with another id (a number and a string); export() will throw E_INVALID_ID`,\n null,\n collisions,\n );\n }\n }\n if (values && ids.kind !== \"identity\" && ids.kind !== \"dense\") {\n let changed = 0;\n for (let i = 0; i < ids.size; i++) {\n const id: NodeId = ids.idOf(i);\n if (typeof canonicalId(String(id)) !== typeof id) {\n changed++;\n }\n }\n if (changed > 0) {\n note(\n CSV_LOSS.ID_TEXT_TYPE,\n `${changed} node id(s) read back as the other type under ids: \"canonical\" (a string \"1\" becomes 1, a number 1.5 becomes \"1.5\")`,\n null,\n changed,\n );\n }\n }\n}\n\nfunction planExport(\n snapshot: GraphSnapshot,\n options: (CsvExportOptions & CommonExportOptions) | undefined,\n values: boolean,\n): Plan {\n const common = resolveExportOptions(options);\n const csv = resolveCsvExportOptions(options);\n const notes: LossNote[] = [];\n const note = (code: string, message: string, column: string | null = null, count: number | null = null): void => {\n notes.push(Object.freeze({ code, message, column, count }));\n };\n const { ids } = snapshot;\n const idText = (index: number): string => String(ids.idOf(index));\n idNotes(snapshot, values, note);\n\n // edge rows: fold the mirrors of undirected pairs; mutual pairs stay two directed rows\n const folding = pairFolding(snapshot);\n const edgeRows: number[] = [];\n for (let e = 0; e < snapshot.edgeCount; e++) {\n if (!folding.folded(e)) {\n edgeRows.push(e);\n }\n }\n if (csv.dialect.type === null || csv.table === \"adjacency\") {\n const where = csv.table === \"adjacency\" ? \"an adjacency table\" : \"the generic dialect\";\n const mixed = countMixedEdges(snapshot);\n if (!snapshot.directed) {\n note(\n CSV_LOSS.DIRECTION_DROPPED,\n `${where} has no direction column; ${snapshot.edgeCount} undirected edge(s) read back as directed unless the importer is told otherwise`,\n null,\n snapshot.edgeCount,\n );\n } else if (mixed > 0) {\n note(\n CSV_LOSS.DIRECTION_DROPPED,\n `${where} has no direction column; ${mixed} undirected edge(s) of a mixed graph read back as one directed edge each`,\n null,\n mixed,\n );\n }\n } else if (csv.table === \"edges\" && !snapshot.directed && edgeRows.length === 0) {\n note(\n CSV_LOSS.DIRECTION_DROPPED,\n \"the direction lives in the Type cell of each edge row; an undirected graph without edges reads back as directed\",\n null,\n 0,\n );\n }\n if (folding.mutualCount > 0) {\n note(\n CSV_LOSS.MUTUAL_EXPANDED,\n `${folding.mutualCount} mutual pair(s) are written as two directed rows; the mutual mark is lost`,\n null,\n folding.mutualCount,\n );\n }\n if (csv.table === \"edges\") {\n noteNodeCoverage(snapshot, edgeRows, note);\n }\n\n // weights\n const weights = explicitWeights(snapshot);\n\n // the edge table's columns\n const edgeId = snapshot.edges.byRole(\"id\");\n const edgeLabel = snapshot.edges.byRole(\"label\");\n const edgeReserved = new Set<string>([csv.dialect.source, csv.dialect.target, csv.dialect.weight].map(lower));\n if (csv.dialect.type !== null) {\n edgeReserved.add(lower(csv.dialect.type));\n }\n const edgeColumns = attributeColumns(\n snapshot.edges,\n \"edge\",\n edgeReserved,\n edgeId,\n edgeLabel,\n csv.table === \"edges\" ? note : null,\n );\n if (csv.table === \"adjacency\") {\n const dropped = edgeColumns.length + (edgeId === null ? 0 : 1) + (edgeLabel === null ? 0 : 1);\n if (dropped > 0) {\n note(\n CSV_LOSS.EDGE_COLUMNS,\n `${dropped} edge column(s) are not written: an adjacency table holds the weight only`,\n null,\n dropped,\n );\n }\n }\n if (csv.table === \"edges\" && values) {\n checkRoleColumn(edgeId, \"id\", EDGE_ID_NAMES, \"edge\", edgeRows, note);\n checkRoleColumn(edgeLabel, \"label\", LABEL_NAMES, \"edge\", edgeRows, note);\n for (const { column } of edgeColumns) {\n checkValues(column, \"edge\", edgeRows, note);\n }\n }\n\n // the node table's columns\n const nodeLabel = snapshot.nodes.byRole(\"label\");\n const nodeReserved = new Set<string>([lower(csv.dialect.id)]);\n const nodeRows: number[] = [];\n if (csv.table === \"nodes\" && values) {\n for (let i = 0; i < snapshot.nodeCount; i++) {\n nodeRows.push(i);\n }\n }\n const nodeColumns = attributeColumns(\n snapshot.nodes,\n \"node\",\n nodeReserved,\n null,\n nodeLabel,\n csv.table === \"nodes\" ? note : null,\n );\n if (csv.table === \"nodes\") {\n if (values) {\n checkRoleColumn(nodeLabel, \"label\", LABEL_NAMES, \"node\", nodeRows, note);\n for (const { column } of nodeColumns) {\n checkValues(column, \"node\", nodeRows, note);\n }\n }\n } else {\n const written = nodeColumns.length + (nodeLabel === null ? 0 : 1);\n if (written > 0) {\n note(\n CSV_LOSS.NODE_TABLE,\n `${written} node column(s) are written by a table: \"nodes\" export only`,\n null,\n written,\n );\n }\n }\n\n return {\n csv,\n common,\n notes,\n edgeRows,\n folding,\n edgeId,\n edgeLabel,\n weights,\n edgeColumns,\n nodeLabel,\n nodeColumns,\n idText,\n };\n}\n\n/**\n * The edge table is not a node carrier (research note 07 section 2.6: node attributes and node\n * presence come from a separate node table): a node without an edge is not written at all, and\n * the importer creates nodes in the order the edge rows first mention them. Both are reported so\n * check() predicts the ids and the order a re-import gives back (design section 8.5).\n * @param snapshot - the snapshot\n * @param edgeRows - the edge rows written\n * @param note - the recorder\n */\nfunction noteNodeCoverage(\n snapshot: GraphSnapshot,\n edgeRows: readonly number[],\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n): void {\n const { nodeCount } = snapshot;\n if (nodeCount === 0) {\n return;\n }\n const list = snapshot.edgeList();\n const seen = new Uint8Array(nodeCount);\n let next = 0;\n let reordered = 0;\n const mention = (index: number): void => {\n if (seen[index] === 1) {\n return;\n }\n seen[index] = 1;\n if (index !== next) {\n reordered++;\n }\n next++;\n };\n for (const e of edgeRows) {\n mention(list.src[e]);\n mention(list.dst[e]);\n }\n const isolated = nodeCount - next;\n if (isolated > 0) {\n note(\n CSV_LOSS.ISOLATED_NODES,\n `${isolated} node(s) have no edge and cannot be written by the edge table; write the node table (table: \"nodes\") to keep them`,\n null,\n isolated,\n );\n }\n if (reordered > 0) {\n note(\n CSV_LOSS.NODE_ORDER,\n `${reordered} node(s) are first mentioned by an edge row out of index order; a re-import numbers nodes by first appearance`,\n null,\n reordered,\n );\n }\n}\n\n/**\n * Lower-case a header name for reserved-name comparisons.\n * @param name - the name\n * @returns the lower-case name\n */\nfunction lower(name: string): string {\n return name.toLowerCase();\n}\n\n/**\n * The attribute columns of a table that are written, in declaration order, skipping the role\n * columns handled elsewhere, the roles the format cannot hold, and names that collide with the\n * reserved headers (reported).\n * @param table - the node or edge table\n * @param domain - node or edge, for messages\n * @param reserved - lower-case header names the dialect writes for structure\n * @param idColumn - the table's id role column, or null\n * @param labelColumn - the table's label role column, or null\n * @param note - the recorder, or null when this table's notes are not wanted\n * @returns the columns and their headers\n */\nfunction attributeColumns(\n table: Iterable<Column>,\n domain: \"node\" | \"edge\",\n reserved: ReadonlySet<string>,\n idColumn: Column | null,\n labelColumn: Column | null,\n note: ((code: string, message: string, column?: string | null, count?: number | null) => void) | null,\n): ColumnOut[] {\n const out: ColumnOut[] = [];\n for (const column of table) {\n const { name, role } = column.meta;\n if (column === idColumn || column === labelColumn) {\n continue;\n }\n if (role !== null && SKIPPED_ROLES.has(role)) {\n continue;\n }\n const low = lower(name);\n const set = column.length - column.nullCount;\n const isIdName = domain === \"edge\" ? findColumn([name], EDGE_ID_NAMES) >= 0 : false;\n if (reserved.has(low) || isIdName || (idColumn !== null && lower(idColumn.meta.name) === low)) {\n note?.(\n CSV_LOSS.RESERVED_NAME,\n `${domain} column \"${name}\" is not written: the name is reserved for the ${isIdName ? \"edge id\" : low} column`,\n name,\n set,\n );\n continue;\n }\n if (findColumn([name], LABEL_NAMES) >= 0) {\n if (labelColumn !== null) {\n note?.(\n CSV_LOSS.RESERVED_NAME,\n `${domain} column \"${name}\" is not written: the name is reserved for the label column`,\n name,\n set,\n );\n continue;\n }\n note?.(\n CSV_LOSS.ROLE_ASSUMED,\n `${domain} column \"${name}\" reads back with the label role (string)`,\n name,\n set,\n );\n }\n out.push({ column, header: name });\n }\n return out;\n}\n\n/**\n * Notes about a role column (id or label): a name the importer does not map to the role, and a\n * dtype other than string / dict (read back as string).\n * @param column - the role column, or null\n * @param role - the role\n * @param names - the header names the importer maps to the role\n * @param domain - node or edge\n * @param rows - the rows written\n * @param note - the recorder\n */\nfunction checkRoleColumn(\n column: Column | null,\n role: \"id\" | \"label\",\n names: readonly string[],\n domain: \"node\" | \"edge\",\n rows: readonly number[],\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n): void {\n if (column === null) {\n return;\n }\n const { name } = column.meta;\n if (findColumn([name], names) < 0) {\n note(\n CSV_LOSS.ROLE_NAME,\n `${domain} column \"${name}\" (${role}) is written under its name, which the importer does not map to the ${role} role`,\n name,\n countSet(column, rows),\n );\n }\n if (column.dtype !== \"string\" && column.dtype !== \"dict\") {\n note(\n CSV_LOSS.TEXT_ROLE,\n `${domain} column \"${name}\" (${role}) is ${column.dtype}; it reads back as string`,\n name,\n countSet(column, rows),\n );\n }\n checkValues(column, domain, rows, note, true);\n}\n\n/**\n * Set rows among the rows written.\n * @param column - the column\n * @param rows - the rows written\n * @returns the count\n */\nfunction countSet(column: Column, rows: readonly number[]): number {\n let count = 0;\n for (const row of rows) {\n if (column.isSet(row)) {\n count++;\n }\n }\n return count;\n}\n\nconst WIDENING: Readonly<Record<TextDtype, number>> = { bool: 0, i32: 1, f64: 2, string: 3 };\nconst TEXT_DTYPES: readonly TextDtype[] = [\"bool\", \"i32\", \"f64\", \"string\"];\n\n/**\n * Value-level notes of one written column over the rows written: non-finite numbers (text on\n * re-import), a text column whose values all read back as numbers or booleans, and the dict\n * heuristic's verdict when it differs from the dtype. A set empty string is written as the quoted\n * empty cell and reads back exactly.\n * @param column - the column\n * @param domain - node or edge\n * @param rows - the rows written\n * @param note - the recorder\n * @param roleColumn - whether the column is declared string on re-import (no inference)\n */\nfunction checkValues(\n column: Column,\n domain: \"node\" | \"edge\",\n rows: readonly number[],\n note: (code: string, message: string, column?: string | null, count?: number | null) => void,\n roleColumn = false,\n): void {\n const { name, dtype } = column.meta;\n const label = `${domain} column \"${name}\"`;\n if (column.dtype === \"f32\" || column.dtype === \"f64\") {\n let nonFinite = 0;\n const { components } = column.meta;\n const { data } = column;\n for (const row of rows) {\n if (!column.isSet(row)) {\n continue;\n }\n for (let k = 0; k < components; k++) {\n if (!Number.isFinite(data[row * components + k])) {\n nonFinite++;\n break;\n }\n }\n }\n if (nonFinite > 0) {\n note(CSV_LOSS.NONFINITE, `${label}: ${nonFinite} non-finite value(s) read back as text`, name, nonFinite);\n }\n return;\n }\n if (dtype !== \"string\" && dtype !== \"dict\") {\n return;\n }\n let widest = -1;\n let candidate = true;\n let set = 0;\n const heuristic = new DictHeuristic(DICT_SAMPLE_ROWS);\n for (const row of rows) {\n if (!column.isSet(row)) {\n continue;\n }\n const text = column.value(row);\n if (typeof text !== \"string\") {\n continue;\n }\n set++;\n const kind = inferTextDtype(text);\n widest = Math.max(widest, WIDENING[kind]);\n if (!heuristic.decided) {\n if (kind !== \"string\") {\n candidate = false;\n }\n heuristic.observe(text);\n }\n }\n if (roleColumn || widest < 0) {\n return;\n }\n const readsAsDict = candidate && heuristic.decide() === \"dict\";\n if (widest < WIDENING.string && !readsAsDict) {\n note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[widest]}`, name, set);\n return;\n }\n if (dtype === \"dict\" && !readsAsDict) {\n note(\n CSV_LOSS.STORAGE_CLASS_CHANGED,\n `${label}: reads back as string (cardinality too high for a dict)`,\n name,\n null,\n );\n } else if (dtype === \"string\" && readsAsDict) {\n note(CSV_LOSS.STORAGE_CLASS_CHANGED, `${label}: reads back as dict (low cardinality)`, name, null);\n }\n}\n\n/**\n * The Type cell of an edge row.\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @param e - the edge\n * @returns \"Directed\" or \"Undirected\"\n */\nfunction typeText(snapshot: GraphSnapshot, plan: Plan, e: number): string {\n if (!snapshot.directed || !plan.folding.sourceDirected(e)) {\n return \"Undirected\";\n }\n return \"Directed\";\n}\n\n/**\n * A neighbour cell of the adjacency table: `id:weight` for an explicit weight; `id:` for an id\n * holding a colon without one, so the importer does not read the id's tail as a weight; `id`\n * otherwise.\n * @param id - the neighbour's id text\n * @param weight - the weight text, or null\n * @returns the cell text (unquoted)\n */\nfunction neighbourCell(id: string, weight: string | null): string {\n if (weight !== null) {\n return `${id}:${weight}`;\n }\n return id.includes(\":\") ? `${id}:` : id;\n}\n\n/**\n * The rows of the adjacency table (design section 8.5: the order survives the importer's first-\n * appearance numbering). Consecutive edges with one source share a row, so the edge order is kept;\n * a row that would introduce nodes out of index order is preceded by one-cell rows for every node\n * up to its highest new index, and the nodes no edge names close the table the same way, so the\n * node order and the isolated nodes are kept.\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @yields one line at a time\n * @returns nothing\n */\nfunction* adjacencyLines(snapshot: GraphSnapshot, plan: Plan): Generator<string, void, undefined> {\n const { delimiter, newline } = plan.csv;\n const forced = (text: string): string => `\"${text.replace(/\"/g, '\"\"')}\"`;\n // rows vary in width by design, so a bare cell holding any delimiter the importer sniffs for\n // (a space in \"New York\") can win the sniff over the real delimiter: such a cell is quoted\n const quote = (text: string): string =>\n DELIMITER_CANDIDATES.some((c) => text.includes(c)) ? forced(text) : quoteCsvCell(text, delimiter);\n // a leading `#` or `%` would open a comment line on the first row\n const first = (text: string): string => (/^[#%]/.test(text) ? forced(text) : quote(text));\n const list = snapshot.edgeList();\n const { edgeRows, weights } = plan;\n const seen = new Uint8Array(snapshot.nodeCount);\n let next = 0;\n const advance = (): void => {\n while (next < seen.length && seen[next] === 1) {\n next++;\n }\n };\n function* singles(upTo: number): Generator<string, void, undefined> {\n for (let i = next; i <= upTo; i++) {\n if (seen[i] === 0) {\n seen[i] = 1;\n yield first(plan.idText(i)) + newline;\n }\n }\n advance();\n }\n let r = 0;\n while (r < edgeRows.length) {\n const source = list.src[edgeRows[r]];\n let end = r;\n while (end < edgeRows.length && list.src[edgeRows[end]] === source) {\n end++;\n }\n // the nodes this row introduces, in the order the importer meets them; in index order from\n // `next` they need nothing more\n const fresh: number[] = [];\n let highest = -1;\n let inOrder = true;\n const mark = (v: number): void => {\n if (seen[v] === 0) {\n seen[v] = 1;\n inOrder &&= v === next + fresh.length;\n fresh.push(v);\n highest = Math.max(highest, v);\n }\n };\n mark(source);\n for (let k = r; k < end; k++) {\n mark(list.dst[edgeRows[k]]);\n }\n if (!inOrder) {\n for (const v of fresh) {\n seen[v] = 0;\n }\n yield* singles(highest);\n }\n advance();\n const cells = [first(plan.idText(source))];\n for (let k = r; k < end; k++) {\n const e = edgeRows[k];\n cells.push(quote(neighbourCell(plan.idText(list.dst[e]), weights.weighted ? weights.text(e) : null)));\n }\n yield cells.join(delimiter) + newline;\n r = end;\n }\n yield* singles(snapshot.nodeCount - 1);\n}\n\n/**\n * The lines of the export, one string per row (terminator included).\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @yields one line at a time\n * @returns nothing\n */\nfunction* lines(snapshot: GraphSnapshot, plan: Plan): Generator<string, void, undefined> {\n const { csv } = plan;\n if (csv.table === \"adjacency\") {\n yield* adjacencyLines(snapshot, plan);\n return;\n }\n const { delimiter, newline } = csv;\n const quote = (text: string): string => quoteCsvCell(text, delimiter);\n const cell = (column: Column, row: number): string => {\n const text = cellText(column, row);\n return text === null ? \"\" : quote(text);\n };\n if (csv.table === \"nodes\") {\n const headers = [csv.dialect.id];\n if (plan.nodeLabel !== null) {\n headers.push(plan.nodeLabel.meta.name);\n }\n for (const { header } of plan.nodeColumns) {\n headers.push(header);\n }\n if (csv.header) {\n yield headers.map(quote).join(delimiter) + newline;\n }\n for (let i = 0; i < snapshot.nodeCount; i++) {\n const cells = [quote(plan.idText(i))];\n if (plan.nodeLabel !== null) {\n cells.push(cell(plan.nodeLabel, i));\n }\n for (const { column } of plan.nodeColumns) {\n cells.push(cell(column, i));\n }\n yield cells.join(delimiter) + newline;\n }\n return;\n }\n const headers = [csv.dialect.source, csv.dialect.target];\n if (csv.dialect.type !== null) {\n headers.push(csv.dialect.type);\n }\n if (plan.edgeId !== null) {\n headers.push(plan.edgeId.meta.name);\n }\n if (plan.edgeLabel !== null) {\n headers.push(plan.edgeLabel.meta.name);\n }\n if (plan.weights.weighted) {\n headers.push(csv.dialect.weight);\n }\n for (const { header } of plan.edgeColumns) {\n headers.push(header);\n }\n if (csv.header) {\n yield headers.map(quote).join(delimiter) + newline;\n }\n const list = snapshot.edgeList();\n for (const e of plan.edgeRows) {\n const cells = [quote(plan.idText(list.src[e])), quote(plan.idText(list.dst[e]))];\n if (csv.dialect.type !== null) {\n cells.push(typeText(snapshot, plan, e));\n }\n if (plan.edgeId !== null) {\n cells.push(cell(plan.edgeId, e));\n }\n if (plan.edgeLabel !== null) {\n cells.push(cell(plan.edgeLabel, e));\n }\n if (plan.weights.weighted) {\n cells.push(plan.weights.text(e) ?? \"\");\n }\n for (const { column } of plan.edgeColumns) {\n cells.push(cell(column, e));\n }\n yield cells.join(delimiter) + newline;\n }\n}\n\n/**\n * Plan an export and refuse it when a fatal note is present (E_INVALID_ID for id text collisions),\n * before anything is written.\n * @param snapshot - the snapshot\n * @param options - the caller's options\n * @returns the plan\n */\nfunction prepare(snapshot: GraphSnapshot, options: (CsvExportOptions & CommonExportOptions) | undefined): Plan {\n const plan = planExport(snapshot, options, false);\n const collision = plan.notes.find((n) => n.code === CSV_LOSS.ID_TEXT_COLLISION);\n if (collision !== undefined) {\n throw new GraphFormatError(\"E_INVALID_ID\", collision.message, {\n reason: \"collision\",\n count: collision.count,\n });\n }\n return plan;\n}\n\n/**\n * Pre-flight: what a CSV export would lose. The generic notes describe the snapshot as a whole\n * against the format; the CSV notes concern the table selected by `table`.\n * @param snapshot - the snapshot\n * @param options - CSV and common options\n * @returns the notes, empty when the export is exact\n */\nfunction check(snapshot: GraphSnapshot, options?: CsvExportOptions & CommonExportOptions): readonly LossNote[] {\n const plan = planExport(snapshot, options, true);\n return Object.freeze([\n ...checkCapabilities(snapshot, CSV_CAPABILITIES, plan.common, { roles: KEPT_ROLES }),\n ...plan.notes,\n ]);\n}\n\n/** The CSV / TSV exporter plugin (subpath `@graphty/graph-io/csv`). */\nexport const csvExporter: GraphExporter<CsvExportOptions> = Object.freeze({\n format: \"csv\",\n capabilities: CSV_CAPABILITIES,\n check,\n export(snapshot: GraphSnapshot, options?: CsvExportOptions & CommonExportOptions): AsyncIterable<Uint8Array> {\n return encodeChunks(lines(snapshot, prepare(snapshot, options)));\n },\n exportToString(snapshot: GraphSnapshot, options?: CsvExportOptions & CommonExportOptions): Promise<string> {\n return joinText(lines(snapshot, prepare(snapshot, options)));\n },\n});\n","/**\n * The CSV / TSV importer and exporter of @graphty/graph-io: the subpath entry `@graphty/graph-io/csv`.\n */\n\nimport {\n COLUMN_RENAMED_CODE,\n DIRECTION_FORCED_CODE,\n DIRECTION_REFUSED_CODE,\n ENCODING_FALLBACK_CODE,\n INVALID_ENCODING_CODE,\n INVALID_UTF8_CODE,\n MIXED_DIRECTION_CODE,\n OPTION_IGNORED_CODE,\n SINK_OPTION_CODE,\n UNKNOWN_ENCODING_CODE,\n} from \"../../common/codes.js\";\nimport { WIDENING_UNSUPPORTED_CODE } from \"../../common/text.js\";\nimport {\n BAD_TYPE_CODE,\n COLUMN_MISSING_CODE,\n DUPLICATE_EDGE_ID_CODE,\n DUPLICATE_NODE_CODE,\n EMPTY_INPUT_CODE,\n FIELD_COUNT_CODE,\n ID_MERGED_CODE,\n MISSING_ENDPOINT_CODE,\n MISSING_ID_CODE,\n NO_DATA_ROWS_CODE,\n NO_ENDPOINT_COLUMNS_CODE,\n NO_ID_COLUMN_CODE,\n ROLE_TAKEN_CODE,\n} from \"./importer.js\";\nimport { BAD_QUOTE_CODE, UNCLOSED_QUOTE_CODE } from \"./records.js\";\n\nexport { CSV_CAPABILITIES, CSV_LOSS, csvExporter, type CsvExportOptions } from \"./exporter.js\";\nexport { type CsvColumnRef } from \"./header.js\";\nexport { csvImporter, type CsvImportOptions } from \"./importer.js\";\n\n/**\n * The issue codes the CSV importer records (design section 8.6), by name: the codes shared with\n * the other importers (src/common/codes.ts) and the CSV-specific ones. A key is the code without\n * its severity and format prefixes.\n */\nexport const CSV_ISSUE = Object.freeze({\n /** The input is empty (fatal). */\n EMPTY_INPUT: EMPTY_INPUT_CODE,\n /** The input holds invalid UTF-8 (fatal). */\n INVALID_UTF8: INVALID_UTF8_CODE,\n /** Invalid bytes in the encoding a BOM, a declaration or the encoding option chose (fatal). */\n INVALID_ENCODING: INVALID_ENCODING_CODE,\n /** Bytes that are not UTF-8 and declare no encoding were read as windows-1252. */\n ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,\n /** A declared encoding the platform cannot decode was ignored. */\n UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,\n /** The header names neither endpoint columns nor an id column (fatal). */\n NO_ENDPOINT_COLUMNS: NO_ENDPOINT_COLUMNS_CODE,\n /** A node table without an id column (fatal). */\n NO_ID_COLUMN: NO_ID_COLUMN_CODE,\n /** A row with a different field count than the header. */\n FIELD_COUNT: FIELD_COUNT_CODE,\n /** An edge row with a blank source or target. */\n MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,\n /** A node row with a blank id. */\n MISSING_ID: MISSING_ID_CODE,\n /** A Type cell outside Directed / Undirected / Mutual. */\n BAD_TYPE: BAD_TYPE_CODE,\n /** An unterminated quoted field (fatal). */\n UNCLOSED_QUOTE: UNCLOSED_QUOTE_CODE,\n /** Text after a closing quote (fatal). */\n QUOTE: BAD_QUOTE_CODE,\n /** A header and no data rows. */\n NO_DATA_ROWS: NO_DATA_ROWS_CODE,\n /** A node table row repeating an id. */\n DUPLICATE_NODE: DUPLICATE_NODE_CODE,\n /** An edge row repeating an edge id (skipped). */\n DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,\n /** Two id cells merged into one number under ids \"number\". */\n ID_MERGED: ID_MERGED_CODE,\n /** An explicitly named weight column the file does not have. */\n COLUMN_MISSING: COLUMN_MISSING_CODE,\n /** A column whose role another column of the sink already holds. */\n ROLE_TAKEN: ROLE_TAKEN_CODE,\n /** A column renamed `<name>#<position>` (a repeated header, or a name the sink holds with another shape). */\n COLUMN_RENAMED: COLUMN_RENAMED_CODE,\n /** A common option the importer has no use for was given. */\n OPTION_IGNORED: OPTION_IGNORED_CODE,\n /** A builder-policy option the sink does not honour. */\n SINK_OPTION: SINK_OPTION_CODE,\n /** The sink refused the file's direction. */\n DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,\n /** Edges forced to the policy's direction. */\n DIRECTION_FORCED: DIRECTION_FORCED_CODE,\n /** A mixed file under onMixedDirection \"error\" (fatal). */\n MIXED_DIRECTION: MIXED_DIRECTION_CODE,\n /** A text column the sink could not widen to the dtype its cells imply. */\n WIDENING_UNSUPPORTED: WIDENING_UNSUPPORTED_CODE,\n});\n"],"names":["lower","SHARED_EMPTY_INPUT_CODE","SHARED_MISSING_ENDPOINT_CODE","SHARED_MISSING_ID_CODE","SHARED_DUPLICATE_NODE_CODE","SHARED_ID_MERGED_CODE","SHARED_ROLE_TAKEN_CODE","SHARED_DUPLICATE_EDGE_ID_CODE","headers"],"mappings":"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"}