@graphty/graph-io 0.3.40 → 0.3.42
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.
- package/README.md +2 -0
- package/dist/chunks/{children-CL3Cy0ez.js → children-CA4coBi0.js} +36 -27
- package/dist/chunks/children-CA4coBi0.js.map +1 -0
- package/dist/chunks/{choose-CS3fvGT1.js → choose-DvpbdRuz.js} +2 -2
- package/dist/chunks/{choose-CS3fvGT1.js.map → choose-DvpbdRuz.js.map} +1 -1
- package/dist/chunks/{constants-DqKZT6cH.js → constants-fT3E8JYY.js} +2 -2
- package/dist/chunks/{constants-DqKZT6cH.js.map → constants-fT3E8JYY.js.map} +1 -1
- package/dist/chunks/{escape-BJRLd3vJ.js → escape-B62ZGM_a.js} +2 -2
- package/dist/chunks/{escape-BJRLd3vJ.js.map → escape-B62ZGM_a.js.map} +1 -1
- package/dist/chunks/{export-B-aVCwyG.js → export-rKLeGQMo.js} +404 -243
- package/dist/chunks/export-rKLeGQMo.js.map +1 -0
- package/dist/chunks/{exporter-6ocL_9x_.js → exporter-8hLNemFk.js} +4 -4
- package/dist/chunks/{exporter-6ocL_9x_.js.map → exporter-8hLNemFk.js.map} +1 -1
- package/dist/chunks/{exporter-BVXnV9-9.js → exporter-BWwEuOPT.js} +29 -17
- package/dist/chunks/exporter-BWwEuOPT.js.map +1 -0
- package/dist/chunks/{exporter-Bcp-l4Bo.js → exporter-CpRyHZ3H.js} +574 -475
- package/dist/chunks/exporter-CpRyHZ3H.js.map +1 -0
- package/dist/chunks/{exporter-SFYkDzD0.js → exporter-yLxKBvLE.js} +23 -20
- package/dist/chunks/{exporter-SFYkDzD0.js.map → exporter-yLxKBvLE.js.map} +1 -1
- package/dist/chunks/{importer-DcKB97kW.js → importer-BbNb7eNg.js} +111 -71
- package/dist/chunks/importer-BbNb7eNg.js.map +1 -0
- package/dist/chunks/{importer-D_srHyj6.js → importer-C98dpSH4.js} +5 -5
- package/dist/chunks/{importer-D_srHyj6.js.map → importer-C98dpSH4.js.map} +1 -1
- package/dist/chunks/{importer-BJlGAOht.js → importer-D794XUTe.js} +3 -3
- package/dist/chunks/{importer-BJlGAOht.js.map → importer-D794XUTe.js.map} +1 -1
- package/dist/chunks/{importer-AjwOJrn2.js → importer-Dz7tI8Sa.js} +202 -181
- package/dist/chunks/importer-Dz7tI8Sa.js.map +1 -0
- package/dist/chunks/{importer-DEp_fYjf.js → importer-Ht7LNg8L.js} +92 -64
- package/dist/chunks/importer-Ht7LNg8L.js.map +1 -0
- package/dist/chunks/{importer-CPqnIY8m.js → importer-WrGRZb0N.js} +42 -29
- package/dist/chunks/importer-WrGRZb0N.js.map +1 -0
- package/dist/chunks/{importer-BpuS3J8C.js → importer-xZ-3suY1.js} +74 -61
- package/dist/chunks/importer-xZ-3suY1.js.map +1 -0
- package/dist/chunks/{json-elements-DpO6AXIc.js → json-elements-DKSqpNW1.js} +3 -3
- package/dist/chunks/{json-elements-DpO6AXIc.js.map → json-elements-DKSqpNW1.js.map} +1 -1
- package/dist/chunks/{ontology-export-50L9DOV4.js → ontology-export-CtRXjg_i.js} +2 -2
- package/dist/chunks/{ontology-export-50L9DOV4.js.map → ontology-export-CtRXjg_i.js.map} +1 -1
- package/dist/chunks/{records-r3SFHz11.js → records-Cu4bxYOm.js} +69 -36
- package/dist/chunks/records-Cu4bxYOm.js.map +1 -0
- package/dist/chunks/{text-Bjfa1xYL.js → text-DHOJXnED.js} +20 -11
- package/dist/chunks/text-DHOJXnED.js.map +1 -0
- package/dist/csv.js +323 -241
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +1 -1
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +1 -1
- package/dist/gexf.js +195 -97
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +170 -149
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +27 -27
- package/dist/graphml.js +2 -2
- package/dist/json.js +1 -1
- package/dist/neo4j.js +103 -65
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +1 -1
- package/dist/pajek.js +1 -1
- package/dist/src/children.d.ts.map +1 -1
- package/dist/src/children.js +50 -27
- package/dist/src/children.js.map +1 -1
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +86 -42
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +358 -184
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/lists.js +30 -15
- package/dist/src/common/lists.js.map +1 -1
- package/dist/src/common/text.d.ts +7 -0
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +15 -7
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +36 -19
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +288 -146
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +129 -80
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +78 -36
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +11 -8
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/tokenizer.d.ts +39 -0
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
- package/dist/src/formats/dot/tokenizer.js +112 -66
- package/dist/src/formats/dot/tokenizer.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +65 -37
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +138 -50
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +51 -35
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +103 -58
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +17 -8
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +34 -24
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +17 -13
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/tree.js +23 -14
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +22 -10
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +54 -20
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +38 -21
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +536 -343
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/obographs.js +29 -17
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +73 -43
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +37 -16
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +21 -11
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts.map +1 -1
- package/dist/src/formats/obo/exporter.js +18 -7
- package/dist/src/formats/obo/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -73
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +26 -15
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +85 -58
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +7 -0
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +79 -40
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +38 -12
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +38 -18
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/sniff.js +109 -57
- package/dist/src/sniff.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/package.json +2 -2
- package/src/children.ts +54 -27
- package/src/common/attributes.ts +118 -49
- package/src/common/input.ts +439 -197
- package/src/common/lists.ts +29 -15
- package/src/common/text.ts +24 -15
- package/src/common/zip.ts +47 -18
- package/src/formats/csv/exporter.ts +377 -175
- package/src/formats/csv/importer.ts +155 -84
- package/src/formats/csv/records.ts +90 -35
- package/src/formats/cys/tables.ts +11 -10
- package/src/formats/dot/tokenizer.ts +118 -68
- package/src/formats/gexf/exporter.ts +67 -36
- package/src/formats/gexf/importer.ts +173 -63
- package/src/formats/gml/exporter.ts +78 -82
- package/src/formats/gml/importer.ts +119 -68
- package/src/formats/gml/syntax.ts +18 -8
- package/src/formats/graphml/exporter.ts +44 -24
- package/src/formats/graphml/importer.ts +18 -11
- package/src/formats/graphml/tree.ts +22 -12
- package/src/formats/graphml/yfiles.ts +23 -10
- package/src/formats/json/dialect.ts +61 -22
- package/src/formats/json/exporter.ts +40 -21
- package/src/formats/json/importer.ts +701 -385
- package/src/formats/json/obographs.ts +35 -15
- package/src/formats/neo4j/exporter.ts +79 -41
- package/src/formats/neo4j/header.ts +50 -16
- package/src/formats/neo4j/importer.ts +21 -10
- package/src/formats/obo/exporter.ts +34 -17
- package/src/formats/pajek/exporter.ts +180 -117
- package/src/formats/pajek/importer.ts +27 -15
- package/src/formats/pajek/syntax.ts +99 -58
- package/src/formats/xgmml/emit.ts +88 -40
- package/src/formats/xgmml/exporter.ts +40 -12
- package/src/formats/xgmml/importer.ts +45 -23
- package/src/sniff.ts +115 -59
- package/dist/chunks/children-CL3Cy0ez.js.map +0 -1
- package/dist/chunks/export-B-aVCwyG.js.map +0 -1
- package/dist/chunks/exporter-BVXnV9-9.js.map +0 -1
- package/dist/chunks/exporter-Bcp-l4Bo.js.map +0 -1
- package/dist/chunks/importer-AjwOJrn2.js.map +0 -1
- package/dist/chunks/importer-BpuS3J8C.js.map +0 -1
- package/dist/chunks/importer-CPqnIY8m.js.map +0 -1
- package/dist/chunks/importer-DEp_fYjf.js.map +0 -1
- package/dist/chunks/importer-DcKB97kW.js.map +0 -1
- package/dist/chunks/records-r3SFHz11.js.map +0 -1
- package/dist/chunks/text-Bjfa1xYL.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 synthesized 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 { plural } from \"../../common/plural.js\";\nimport { inferTextDtype } from \"../../common/text.js\";\n\n/**\n * A column named by header text or by 0-based position.\n * @category Built-in formats\n */\nexport type CsvColumnRef = string | number;\n\n/**\n * Header candidates of the source endpoint, in priority order (matched exactly, then case-insensitively).\n * @category Plugin helpers\n */\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/**\n * Header candidates of the target endpoint.\n * @category Plugin helpers\n */\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/**\n * Header candidates of a node table's id column.\n * @category Plugin helpers\n */\nexport const ID_NAMES: readonly string[] = Object.freeze([\"id\", \"node\", \"name\", \"key\"]);\n\n/**\n * Header candidates of a label column (node or edge).\n * @category Plugin helpers\n */\nexport const LABEL_NAMES: readonly string[] = Object.freeze([\"label\"]);\n\n/**\n * Header candidates of an edge id column.\n * @category Plugin helpers\n */\nexport const EDGE_ID_NAMES: readonly string[] = Object.freeze([\"id\"]);\n\n/**\n * The Gephi per-row direction column.\n * @category Plugin helpers\n */\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 * @category Plugin helpers\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 * @category Plugin helpers\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${plural(names.length)})`,\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 * A two-column row holding a known name is still data when its second cell is the second row's\n * first cell and it does not name both endpoints: a header names columns, while an edge list whose\n * ids happen to be such words (`key,lock` over `lock,door`) chains its ids from row to row.\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 * @category Plugin helpers\n */\nexport function looksLikeHeader(first: readonly string[], second: readonly string[] | null): boolean {\n let allText = true;\n let marker = false;\n for (const cell of first) {\n const text = cell.trim();\n if (HEADER_MARKERS.has(text.toLowerCase())) {\n marker = true;\n }\n if (text.length === 0 || inferTextDtype(text) !== \"string\") {\n allText = false;\n }\n }\n if (marker) {\n const names = headerNames(first);\n if (second === null || (findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0)) {\n return true;\n }\n const chained = first.length === 2 && second.length >= 1 && first[1].trim() === second[0].trim();\n return !chained || first[1].trim().length === 0;\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 synthesized header of a headerless file: `column1` ... `columnN`.\n * @param width - the number of cells of the first row\n * @returns the names\n * @category Plugin helpers\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 * @category Plugin helpers\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 * @category Plugin helpers\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,neighbor[: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 neighbors, one edge per neighbor in row order. A neighbor 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 OPTION_IGNORED_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 { plural } from \"../../common/plural.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 {\n checkRecordSyntax,\n type CsvReaderOptions,\n CsvRecordReader,\n DELIMITER_CANDIDATES,\n sniffDelimiter,\n sniffNewline,\n splitRecords,\n stripLeadingComments,\n} from \"./records.js\";\nimport { InferredColumn } from \"./values.js\";\n\n/**\n * The format-specific options of the CSV importer.\n * @category Built-in formats\n */\nexport interface CsvImportOptions extends CommonImportOptions {\n /**\n * The field delimiter. The default is to detect it from the first rows: `,`, tab, `;`, `|` or\n * space. It applies to the `nodes` table too.\n * @defaultValue detected\n */\n delimiter?: string | undefined;\n /**\n * The decimal separator of numbers. With \",\", a cell such as `2,5` (digits, a comma, digits) is\n * read as the number 2.5, for weights and attributes alike, and the delimiter is detected among\n * the others (`;`, tab, `|`, space), so a spreadsheet saved in a locale that writes decimal\n * commas reads as it is. A thousands separator is not read.\n * @defaultValue \".\"\n */\n decimal?: \".\" | \",\" | undefined;\n /**\n * Whether the first row is a header; \"auto\" decides from its content (a header names columns, a\n * data row holds ids and numbers). It applies to the `nodes` table too.\n * @defaultValue \"auto\"\n */\n header?: boolean | \"auto\" | undefined;\n /**\n * What the input is: \"edges\" (one edge per row), \"nodes\" (one node per row), \"adjacency\" (a\n * node and its neighbors per row, `node,neighbor[:weight],...`, with no header unless you pass\n * `header: true`), or \"auto\": an edge table when source and target columns can be found, a node\n * table when only an id column can. An adjacency table is never detected, because its rows look\n * like an edge list.\n * @defaultValue \"auto\"\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\" | undefined;\n /**\n * The source column, by name or 0-based position. The default is the column whose header is a\n * recognized source name (`source`, `from`, `src` and others; the CSV format page lists them\n * all); in a file without a header, the first column. A header with no recognized source and\n * target names needs this option and `targetColumn`.\n * @defaultValue from the header\n */\n sourceColumn?: CsvColumnRef | undefined;\n /**\n * The target column, by name or 0-based position. The default is the column whose header is a\n * recognized target name (`target`, `to`, `dst` and others; the CSV format page lists them\n * all); in a file without a header, the second column.\n * @defaultValue from the header\n */\n targetColumn?: CsvColumnRef | undefined;\n /**\n * The column that gives each edge's direction (Directed / Undirected / Mutual), by name or\n * position. The default is the `Type` column of a Gephi table (one whose header has exactly\n * `Source` and `Target`); null reads no such column.\n * @defaultValue Gephi's Type\n */\n typeColumn?: CsvColumnRef | null | undefined;\n /**\n * The id column of a node table, by name or position. The default is the column the header\n * names `id` (or `node`, `name`, `key`).\n * @defaultValue from the header\n */\n idColumn?: CsvColumnRef | undefined;\n /**\n * The label column of a node table, by name or position. The default is the column the header\n * names `label` (in any case). With `nodeIdFrom: \"label\"`, the node ids come from this column,\n * and the edge table must name its nodes by these labels too.\n * @defaultValue from the header\n */\n labelColumn?: CsvColumnRef | undefined;\n /**\n * A node table to read before the edges: its ids become nodes and its other columns node\n * attributes. Pass it as a string, bytes, a stream, or a `File` or `Blob` (for example, the second file a\n * user picked).\n */\n nodes?: ImportInput | Blob | undefined;\n /**\n * Give the nodes of a node table without an id column the row number as id (0 for the first\n * data row, turned into an id by `ids`), instead of failing with E_CSV_NO_ID_COLUMN. It applies\n * to the `nodes` table when you pass one, else to the input. Under `header: \"auto\"` that\n * table's first row is then read as a header; under `header: false` the option is ignored with\n * a warning, because a table without a header takes its ids from its first column.\n * @defaultValue false\n */\n rowNumberIds?: boolean | undefined;\n}\n\n/**\n * The input holds no header row at all.\n * @category Issue and loss codes\n */\nexport const EMPTY_INPUT_CODE = SHARED_EMPTY_INPUT_CODE;\n/**\n * The header names no source / target (or, for a node table, no id) column.\n * @category Issue and loss codes\n */\nexport const NO_ENDPOINT_COLUMNS_CODE = \"E_CSV_NO_ENDPOINT_COLUMNS\";\n/**\n * A node table without an id column.\n * @category Issue and loss codes\n */\nexport const NO_ID_COLUMN_CODE = \"E_CSV_NO_ID_COLUMN\";\n/**\n * A row with a different number of fields than the header.\n * @category Issue and loss codes\n */\nexport const FIELD_COUNT_CODE = \"E_CSV_FIELD_COUNT\";\n/**\n * An edge row with a blank source or target cell.\n * @category Issue and loss codes\n */\nexport const MISSING_ENDPOINT_CODE = SHARED_MISSING_ENDPOINT_CODE;\n/**\n * A node row with a blank id cell.\n * @category Issue and loss codes\n */\nexport const MISSING_ID_CODE = SHARED_MISSING_ID_CODE;\n/**\n * A Type cell that is not Directed, Undirected or Mutual.\n * @category Issue and loss codes\n */\nexport const BAD_TYPE_CODE = \"E_CSV_BAD_TYPE\";\n/**\n * The table has a header and no data rows.\n * @category Issue and loss codes\n */\nexport const NO_DATA_ROWS_CODE = \"W_CSV_NO_DATA_ROWS\";\n/**\n * A node table row repeats an id; its attributes overwrite the earlier row's.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_NODE_CODE = SHARED_DUPLICATE_NODE_CODE;\n/**\n * Two different id cells became the same number because `ids` is \"number\", so their nodes were merged.\n * @category Issue and loss codes\n */\nexport const ID_MERGED_CODE = SHARED_ID_MERGED_CODE;\n/**\n * An explicitly named weight column the file does not have.\n * @category Issue and loss codes\n */\nexport const COLUMN_MISSING_CODE = \"W_CSV_COLUMN_MISSING\";\n/**\n * You read into a graph builder that already has an id or label attribute, so this file's one is kept without its\n * role.\n * @category Issue and loss codes\n */\nexport const ROLE_TAKEN_CODE = SHARED_ROLE_TAKEN_CODE;\n/**\n * A repeated edge id (the column is unique); the edge is skipped.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_EDGE_ID_CODE = SHARED_DUPLICATE_EDGE_ID_CODE;\n/**\n * The input starts like another format (XML or HTML, JSON, GML, DOT or Pajek), not CSV. The import stops.\n * @category Issue and loss codes\n */\nexport const OTHER_FORMAT_CODE = \"E_CSV_OTHER_FORMAT\";\n/**\n * A quote inside an unquoted field (RFC 4180 forbids it); the quote is kept as text.\n * @category Issue and loss codes\n */\nexport const STRAY_QUOTE_CODE = \"W_CSV_STRAY_QUOTE\";\n/**\n * A leading comment's direction disagrees with defaultDirected or with an earlier comment.\n * @category Issue and loss codes\n */\nexport const COMMENT_DIRECTION_CODE = \"W_CSV_COMMENT_DIRECTION\";\n/**\n * A column named like Gephi's Type holds direction words but is read as a plain attribute.\n * @category Issue and loss codes\n */\nexport const TYPE_COLUMN_IGNORED_CODE = \"W_CSV_TYPE_COLUMN_IGNORED\";\n/**\n * The header ends in a delimiter; rows without the empty last cell are read as complete.\n * @category Issue and loss codes\n */\nexport const TRAILING_HEADER_DELIMITER_CODE = \"W_CSV_TRAILING_HEADER_DELIMITER\";\n/**\n * Every row is one cell that another delimiter would split (a likely wrong delimiter).\n * @category Issue and loss codes\n */\nexport const SINGLE_COLUMN_CODE = \"W_CSV_SINGLE_COLUMN\";\n/**\n * Several header columns name the same role; the one not chosen is a plain attribute.\n * @category Issue and loss codes\n */\nexport const AMBIGUOUS_COLUMN_CODE = \"W_CSV_AMBIGUOUS_COLUMN\";\n/**\n * An unquoted id with leading or trailing whitespace (kept, RFC 4180), distinct from the bare id.\n * @category Issue and loss codes\n */\nexport const PADDED_ID_CODE = \"W_CSV_PADDED_ID\";\n/**\n * A leading `#` / `%` line skipped as a comment has the fields of a record.\n * @category Issue and loss codes\n */\nexport const COMMENT_LIKE_RECORD_CODE = \"W_CSV_COMMENT_LIKE_RECORD\";\n/**\n * Data rows end in one extra empty cell (a trailing delimiter); the cell is dropped.\n * @category Issue and loss codes\n */\nexport const TRAILING_DELIMITER_CODE = \"W_CSV_TRAILING_DELIMITER\";\n/**\n * A headerless three-column table's third column holds text, so it is an attribute, not the weight.\n * @category Issue and loss codes\n */\nexport const WEIGHT_AS_ATTRIBUTE_CODE = \"W_CSV_WEIGHT_AS_ATTRIBUTE\";\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]);\n\n/** The CSV options with defaults applied. */\ninterface ResolvedCsvOptions {\n readonly delimiter: string | null;\n readonly decimalComma: boolean;\n readonly header: boolean | \"auto\";\n readonly table: NonNullable<CsvImportOptions[\"table\"]>;\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 labelColumn: CsvColumnRef | null;\n readonly nodes: ImportInput | null;\n readonly rowNumberIds: boolean;\n /** The caller's explicit defaultDirected; null when it was left to its default. */\n readonly explicitDirected: boolean | null;\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 /** A column named like Type (any case) left as an attribute, or -1; its direction words are reported. */\n readonly typeHint: number;\n readonly id: number;\n readonly label: number;\n readonly attributes: readonly number[];\n /** Whether a row may omit the last cell (the header ends in a delimiter). */\n readonly optionalLast: boolean;\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 /** Whether a row may omit the last cell (the header ends in a delimiter). */\n readonly optionalLast: boolean;\n}\n\n/** An adjacency table: no columns, a node and its neighbors 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 /** The comment line that set commentDirected. */\n commentText: string;\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/** A number written with a decimal comma: digits, one comma, digits, an optional exponent. */\nconst DECIMAL_COMMA = /^\\s*[+-]?\\d+,\\d+(?:[eE][+-]?\\d+)?\\s*$/;\n\n/**\n * The direction a SNAP or KONECT comment line 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). Only whole words count, so\n * `% symbols` or `# bipartite-ish` declare nothing.\n * @param comment - a leading comment line\n * @returns true / false when the line declares the direction, null otherwise\n */\nfunction commentDirection(comment: string): boolean | null {\n const text = comment.slice(1).trim().toLowerCase();\n if (/^(?:directed graph|asym)\\b/.test(text)) {\n return true;\n }\n if (/^(?:undirected graph|sym|bip)\\b/.test(text)) {\n return false;\n }\n return null;\n}\n\n/**\n * The name of a direction, for messages.\n * @param directed - the direction\n * @returns \"directed\" or \"undirected\"\n */\nfunction directionName(directed: boolean): string {\n return directed ? \"directed\" : \"undirected\";\n}\n\n/**\n * Whether an unquoted id cell has leading or trailing whitespace (kept as written, RFC 4180).\n * @param text - the cell text\n * @param quoted - whether the cell was quoted\n * @returns true when padded\n */\nfunction isPadded(text: string, quoted: boolean | undefined): boolean {\n return quoted !== true && text.trim().length !== text.length;\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.decimal !== undefined && o.decimal !== \".\" && o.decimal !== \",\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option decimal: expected \".\" or \",\"', {\n option: \"decimal\",\n found: o.decimal,\n });\n }\n if (o.decimal === \",\" && o.delimiter === \",\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'options delimiter and decimal are both \",\"; pass delimiter: \";\"', {\n option: \"decimal\",\n found: o.decimal,\n });\n }\n if (o.delimiter !== undefined) {\n // one character, not a line break, not the quote\n checkRecordSyntax({ delimiter: o.delimiter, quote: '\"' });\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\", \"labelColumn\"] 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\", \"labelColumn\"] 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 decimalComma: o.decimal === \",\",\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 labelColumn: o.labelColumn ?? null,\n nodes: nodesInput(o.nodes),\n rowNumberIds: o.rowNumberIds ?? false,\n explicitDirected: typeof options?.defaultDirected === \"boolean\" ? options.defaultDirected : null,\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 neighbor 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 * @category Plugin helpers\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 /** Whether a field-count error has already suggested `table: \"adjacency\"`. */\n private adjacencyHinted = false;\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 // a decimal comma is never the delimiter\n ...(state.csv.decimalComma ? { candidates: DELIMITER_CANDIDATES.filter((d) => d !== \",\") } : {}),\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 // an adjacency table has no header: an empty one is the empty graph\n allowEmpty: kind === \"adjacency\",\n // explicit dialect options say the caller knows the input is a table\n inspect: hasExplicitDialect(state.csv)\n ? undefined\n : (preview, line) => {\n this.refuseOtherFormat(preview, line);\n },\n };\n this.reader = new CsvRecordReader(input, state.report, readerOptions);\n }\n\n /**\n * Read every row; the reader is closed (and a stream canceled) 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 let first = await iterator.next();\n if (!first.done && (this.reader.sepDirective || this.isSepDirective(first.value))) {\n // Excel's `sep=;` line: the reader took the delimiter from it; it is not a row\n first = await iterator.next();\n }\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 // a column option naming a cell of the first row makes that row the header\n header = looksLikeHeader(firstRow, secondRow) || this.namesExplicitColumn(firstRow);\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 if (this.rowNumbers && !header) {\n report.warning(\n \"unsupported\",\n OPTION_IGNORED_CODE,\n \"option rowNumberIds ignored: a headerless node table takes its ids from column 0\",\n { line: firstLine, element: \"rowNumberIds\" },\n );\n }\n const names = header ? uniqueNames(headerNames(firstRow), report, firstLine) : positionalNames(firstRow.length);\n const last = firstRow.length - 1;\n const optionalLast = header && last > 0 && isUnset(firstRow[last], firstQuoted[last]);\n this.applyCommentDirection();\n this.plan = this.resolvePlan(\n names,\n header,\n firstLine,\n pending.map((p) => p.row),\n optionalLast,\n );\n if (optionalLast) {\n report.warning(\n \"validation-error\",\n TRAILING_HEADER_DELIMITER_CODE,\n `the header ends in a delimiter; rows without the empty last cell are read as complete`,\n { line: firstLine, element: names[last] },\n );\n }\n this.reportCommentLikeRecords(names.length);\n this.reportSingleColumn(\n names.length,\n pending.map((p) => p.row),\n firstLine,\n );\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 this.reportReaderFindings();\n }\n\n /**\n * Whether a row is Excel's delimiter directive (`sep=;`) under a delimiter the caller gave (a\n * sniffed one comes from the directive itself: `reader.sepDirective`).\n * @param row - the first row\n * @returns true for the directive\n */\n private isSepDirective(row: readonly string[]): boolean {\n return (\n this.state.csv.delimiter !== null &&\n !this.reader.quoted[0] &&\n /^sep=.$/i.test(row.join(this.state.csv.delimiter))\n );\n }\n\n /**\n * Fail when the input opens like another format (an HTML error page, a JSON, GML, DOT or Pajek\n * file handed to the CSV importer), which any delimiter would otherwise read as rows of text.\n * Runs on the raw preview before any record is parsed, so a quoting error in the other\n * format's syntax cannot abort first.\n * @param preview - the start of the input, leading comments removed\n * @param line - the line it starts on\n */\n private refuseOtherFormat(preview: string, line: number): void {\n if (OTHER_FORMATS.some((r) => r.test(preview))) {\n this.state.report.fail(\n OTHER_FORMAT_CODE,\n `the input opens like another format (XML / HTML, JSON, GML, DOT or Pajek), not CSV: ${JSON.stringify(preview.trimStart().slice(0, 60))}`,\n { line },\n );\n }\n }\n\n /**\n * Whether a column option names a cell of the first row by text.\n * @param row - the first row\n * @returns true when sourceColumn, targetColumn, idColumn or typeColumn names one of its cells\n */\n private namesExplicitColumn(row: readonly string[]): boolean {\n const { csv } = this.state;\n const names = headerNames(row);\n return [csv.sourceColumn, csv.targetColumn, csv.idColumn, csv.typeColumn, csv.labelColumn].some(\n (ref) => typeof ref === \"string\" && findColumn(names, [ref]) >= 0,\n );\n }\n\n /**\n * Take the file-level direction from the leading SNAP / KONECT comments of this table: the first\n * declaring line wins; a later line declaring the other direction, and a declaration that\n * overrides the caller's explicit defaultDirected, are reported.\n */\n private applyCommentDirection(): void {\n const { state } = this;\n const { leadingComments, leadingCommentLines } = this.reader;\n for (let i = 0; i < leadingComments.length; i++) {\n const comment = leadingComments[i];\n const directed = commentDirection(comment);\n if (directed === null) {\n continue;\n }\n const where = { line: leadingCommentLines[i], element: comment };\n if (state.commentDirected === null) {\n state.commentDirected = directed;\n state.commentText = comment;\n const explicit = state.csv.explicitDirected;\n if (explicit !== null && explicit !== directed) {\n state.report.warning(\n \"validation-error\",\n COMMENT_DIRECTION_CODE,\n `${JSON.stringify(comment)} declares a ${directionName(directed)} graph, which overrides defaultDirected: ${explicit}`,\n where,\n );\n }\n } else if (state.commentDirected !== directed) {\n state.report.warning(\n \"validation-error\",\n COMMENT_DIRECTION_CODE,\n `${JSON.stringify(comment)} declares a ${directionName(directed)} graph but ${JSON.stringify(state.commentText)} declared a ${directionName(state.commentDirected)} one; the first applies`,\n where,\n );\n }\n }\n }\n\n /**\n * Report a leading `#` / `%` line that has the fields of a record (a first id starting with `#`,\n * swallowed as a comment): its text directly follows the comment character and splits into as\n * many cells as the table has columns. SNAP and KONECT comment lines put a space after it.\n * @param width - the number of columns\n */\n private reportCommentLikeRecords(width: number): void {\n const { delimiter, leadingComments, leadingCommentLines } = this.reader;\n if (width < 2 || delimiter === null) {\n return;\n }\n for (let i = 0; i < leadingComments.length; i++) {\n const comment = leadingComments[i];\n if (comment.length > 1 && comment[1].trim().length > 0 && comment.split(delimiter).length === width) {\n this.state.report.warnOnce(\n \"parse-error\",\n COMMENT_LIKE_RECORD_CODE,\n `${JSON.stringify(comment)} was skipped as a leading comment but has the fields of a record; quote an id that starts with # or %`,\n { line: leadingCommentLines[i], element: comment },\n );\n }\n }\n }\n\n /**\n * Report a one-column table whose cells another delimiter would split consistently (a delimiter\n * option that does not match the file).\n * @param width - the number of columns\n * @param rows - the data rows read so far\n * @param line - the first line\n */\n private reportSingleColumn(width: number, rows: readonly (readonly string[])[], line: number): void {\n if (width !== 1 || rows.length === 0 || this.plan?.kind === \"adjacency\") {\n return;\n }\n const candidates = DELIMITER_CANDIDATES.filter((d) => d !== this.reader.delimiter);\n const other = sniffDelimiter(`${rows.map((r) => r[0]).join(\"\\n\")}\\n`, \"\\n\", candidates);\n if (other !== null) {\n this.state.report.warning(\n \"parse-error\",\n SINGLE_COLUMN_CODE,\n `every row read so far is one cell that ${JSON.stringify(other)} splits consistently; pass delimiter: ${JSON.stringify(other)} if that is the file's delimiter`,\n { line },\n );\n }\n }\n\n /** Report what the record reader noticed while reading: a stray quote. */\n private reportReaderFindings(): void {\n const { report } = this.state;\n const { strayQuoteLine } = this.reader;\n if (strayQuoteLine > 0) {\n report.warnOnce(\n \"parse-error\",\n STRAY_QUOTE_CODE,\n `a quote inside an unquoted field is kept as text (RFC 4180 quotes a whole field)`,\n { line: strayQuoteLine },\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 * @param sample - the data rows read so far\n * @param optionalLast - whether a row may omit the last cell\n * @returns the plan; the import aborts when no endpoints (or id) resolve\n */\n private resolvePlan(\n names: readonly string[],\n header: boolean,\n line: number,\n sample: readonly (readonly string[])[],\n optionalLast: boolean,\n ): EdgePlan | NodePlan | AdjacencyPlan {\n const { csv } = this.state;\n if (this.kind === \"adjacency\") {\n return { kind: \"adjacency\", names: [] };\n }\n let source = -1;\n let target = -1;\n if (this.kind !== \"nodes\") {\n source = endpointColumn(names, header, csv.sourceColumn, \"sourceColumn\", SOURCE_NAMES, 0);\n target = endpointColumn(names, header, csv.targetColumn, \"targetColumn\", TARGET_NAMES, 1);\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, line, sample, optionalLast);\n }\n this.refuseWithoutEndpoints(names, header, source, target, line);\n return this.nodePlan(names, header, line, optionalLast);\n }\n\n /**\n * Refuse a table without both endpoint columns, unless it can be a node table: an edge table\n * was asked for, a single endpoint column was found, or nothing names an id.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param source - the source column, or -1\n * @param target - the target column, or -1\n * @param line - the header line\n */\n private refuseWithoutEndpoints(\n names: readonly string[],\n header: boolean,\n source: number,\n target: number,\n line: number,\n ): void {\n const { csv, report } = this.state;\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n if (source >= 0 || target >= 0) {\n // one endpoint column is a broken edge table, never a node table\n const [has, missing] = source >= 0 ? [\"source\", \"target\"] : [\"target\", \"source\"];\n report.fail(\n NO_ENDPOINT_COLUMNS_CODE,\n `the header has a ${has} column (${JSON.stringify(names[Math.max(source, target)])}) but no ${missing} column (${shown})`,\n { line },\n { columns: [...names] },\n );\n }\n if (\n this.kind === \"edges\" ||\n (this.kind === \"auto\" && (csv.sourceColumn !== null || csv.targetColumn !== null || csv.nodes !== 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 : names.length >= 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 }\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 * @param line - the header (or first) line\n * @param sample - the data rows read so far\n * @param optionalLast - whether a row may omit the last cell\n * @returns the plan\n */\n private edgePlan(\n names: readonly string[],\n header: boolean,\n source: number,\n target: number,\n line: number,\n sample: readonly (readonly string[])[],\n optionalLast: boolean,\n ): EdgePlan {\n const { csv } = this.state;\n const claimed = new Set<number>([source, target]);\n const weight = this.weightColumn(names, header, claimed, line, sample);\n if (weight >= 0) {\n claimed.add(weight);\n }\n if (header) {\n this.reportEndpointRivals(names, source, target, weight, line);\n }\n const type = this.typeColumn(names, header, source, target, claimed);\n if (type >= 0) {\n claimed.add(type);\n }\n const typeHint =\n header && type < 0 && csv.typeColumn === undefined ? findFree(names, [TYPE_NAME], claimed) : -1;\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 {\n kind: \"edges\",\n names,\n width: names.length,\n source,\n target,\n weight,\n type,\n typeHint,\n id,\n label,\n attributes,\n optionalLast,\n };\n }\n\n /**\n * The weight column of an edge table: the weightFrom column of a header (a warning when it was\n * asked for and is missing), else the third column of a headerless edge list unless it holds text.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param claimed - the endpoint columns\n * @param line - the header (or first) line\n * @param sample - the data rows read so far\n * @returns the column index, or -1\n */\n private weightColumn(\n names: readonly string[],\n header: boolean,\n claimed: ReadonlySet<number>,\n line: number,\n sample: readonly (readonly string[])[],\n ): number {\n const { common, report } = this.state;\n if (common.weightFrom === null) {\n return -1;\n }\n if (header) {\n const 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, element: common.weightFrom },\n );\n }\n return weight;\n }\n if (names.length < 3 || claimed.has(2)) {\n return -1;\n }\n if (isTextTriple(names.length, sample)) {\n report.warnOnce(\n \"validation-error\",\n WEIGHT_AS_ATTRIBUTE_CODE,\n `the third column holds text in the first rows, so it is read as the attribute ${names[2]}, not the weight`,\n { line, element: names[2] },\n );\n return -1;\n }\n return 2;\n }\n\n /**\n * Warn about header columns that name the source, target or weight beside the chosen one.\n * @param names - the header names\n * @param source - the source column\n * @param target - the target column\n * @param weight - the weight column, or -1\n * @param line - the header line\n */\n private reportEndpointRivals(\n names: readonly string[],\n source: number,\n target: number,\n weight: number,\n line: number,\n ): void {\n const { csv, common } = this.state;\n if (csv.sourceColumn === null) {\n this.reportRivals(names, SOURCE_NAMES, source, \"source\", line);\n }\n if (csv.targetColumn === null) {\n this.reportRivals(names, TARGET_NAMES, target, \"target\", line);\n }\n if (weight >= 0 && common.weightFrom !== null) {\n this.reportRivals(names, [common.weightFrom], weight, \"weight\", line);\n }\n }\n\n /**\n * The edge type column: the typeColumn option, else Gephi's `Type` beside `Source` and `Target`.\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 * @param claimed - the columns other roles took\n * @returns the column index, or -1\n */\n private typeColumn(\n names: readonly string[],\n header: boolean,\n source: number,\n target: number,\n claimed: ReadonlySet<number>,\n ): number {\n const { typeColumn } = this.state.csv;\n if (typeColumn === null) {\n return -1;\n }\n if (typeColumn === undefined) {\n const gephi = header && names[source] === \"Source\" && names[target] === \"Target\";\n const type = gephi ? findFree(names, [TYPE_NAME], claimed) : -1;\n return type >= 0 && names[type] === TYPE_NAME ? type : -1;\n }\n const type = resolveColumnRef(names, 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 return type;\n }\n\n /**\n * Warn about header columns that name a role another column was chosen for (`Source,source`,\n * `from,to,source`, `Weight,weight`): the one not chosen is read as a plain attribute.\n * @param names - the header names\n * @param candidates - the names of the role\n * @param chosen - the column chosen\n * @param role - the role, for the message\n * @param line - the header line\n */\n private reportRivals(\n names: readonly string[],\n candidates: readonly string[],\n chosen: number,\n role: string,\n line: number,\n ): void {\n const lower = new Set(candidates.map((c) => c.toLowerCase()));\n names.forEach((name, i) => {\n if (i !== chosen && lower.has(name.toLowerCase())) {\n this.state.report.warning(\n \"validation-error\",\n AMBIGUOUS_COLUMN_CODE,\n `columns ${JSON.stringify(names[chosen])} and ${JSON.stringify(name)} both name the ${role}; ${JSON.stringify(names[chosen])} is used and ${JSON.stringify(name)} is a plain attribute`,\n { line, element: name },\n );\n }\n });\n }\n\n /**\n * The label column of a node table: the labelColumn option, else a header column named `label`.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param claimed - columns another role took\n * @returns the column index, or -1\n */\n private labelOf(names: readonly string[], header: boolean, claimed: ReadonlySet<number>): number {\n const { labelColumn } = this.state.csv;\n if (labelColumn !== null) {\n return resolveColumnRef(names, labelColumn, \"labelColumn\");\n }\n return header ? findFree(names, LABEL_NAMES, claimed) : -1;\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 * @param optionalLast - whether a row may omit the last cell\n * @returns the plan; the import aborts when no id column resolves\n */\n private nodePlan(names: readonly string[], header: boolean, line: number, optionalLast: boolean): NodePlan {\n const { common } = this.state;\n const claimed = new Set<number>();\n const idColumn = this.idColumnOf(names, header);\n const label = this.labelOf(names, header, new Set(idColumn >= 0 ? [idColumn] : []));\n const id = this.nodeIdColumn(names, idColumn, label, line);\n if (id >= 0) {\n claimed.add(id);\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, optionalLast };\n }\n\n /**\n * The id column of a node table: the idColumn option, else a header column with an id name,\n * else the first column of a headerless table.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @returns the column index, or -1\n */\n private idColumnOf(names: readonly string[], header: boolean): number {\n const { idColumn } = this.state.csv;\n if (idColumn !== null) {\n return resolveColumnRef(names, idColumn, \"idColumn\");\n }\n if (header) {\n return findColumn(names, ID_NAMES);\n }\n return names.length >= 1 ? 0 : -1;\n }\n\n /**\n * The column node ids come from, as nodeIdFrom says: the label, none (row numbers), or the id\n * column. The import aborts when that column is missing.\n * @param names - the column names\n * @param idColumn - the id column, or -1\n * @param label - the label column, or -1\n * @param line - the header line\n * @returns the column index, or -1 for row numbers\n */\n private nodeIdColumn(names: readonly string[], idColumn: number, label: number, line: number): number {\n const { common, report } = this.state;\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n if (common.nodeIdFrom === \"index\") {\n return -1;\n }\n if (common.nodeIdFrom === \"label\") {\n if (label < 0) {\n report.fail(\n NO_ID_COLUMN_CODE,\n `nodeIdFrom is \"label\", but the node table has no column named \"label\" (${shown}); pass labelColumn to name the column that holds the labels`,\n { line },\n { columns: [...names] },\n );\n }\n return label;\n }\n if (idColumn < 0 && !this.rowNumbers) {\n report.fail(\n NO_ID_COLUMN_CODE,\n `no id column in the node table header (${shown})`,\n { line },\n { columns: [...names] },\n );\n }\n return idColumn;\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 (this.state.csv.decimalComma) {\n for (let i = 0; i < row.length; i++) {\n if (DECIMAL_COMMA.test(row[i])) {\n row[i] = row[i].replace(\",\", \".\");\n }\n }\n }\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 neighbor 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 neighbors = 0;\n for (let k = 1; k < row.length; k++) {\n if (!isUnset(row[k], quoted[k])) {\n neighbors++;\n }\n }\n if (isUnset(row[0], quoted[0])) {\n report.error(\"missing-value\", MISSING_ID_CODE, `blank node cell`, { line });\n counts.skippedNodes++;\n counts.skippedEdges += neighbors;\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 += neighbors;\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 cellQuoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processEdgeRow(plan: EdgePlan, row: string[], cellQuoted: readonly boolean[], line: number): void {\n const { report } = this.state;\n const { counts } = report;\n const quoted = completeRow(plan, row, cellQuoted, report, line);\n if (row.length !== plan.width) {\n this.reportRowWidth(plan, row, line);\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(\"missing-value\", MISSING_ENDPOINT_CODE, `blank ${sourceMissing ? \"source\" : \"target\"} cell`, {\n line,\n });\n counts.skippedEdges++;\n return;\n }\n const kind = this.rowKind(plan, row, line);\n if (kind === null) {\n counts.skippedEdges++;\n return;\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 && this.edgeIds.has(idText)) {\n report.error(\n \"validation-error\",\n DUPLICATE_EDGE_ID_CODE,\n `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.checkPadded(sourceText, quoted[plan.source], line);\n this.checkPadded(targetText, quoted[plan.target], line);\n let edge: number;\n try {\n edge = this.addEdge(sourceText, targetText, kind, plan.weight >= 0 ? row[plan.weight] : null);\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 // only an imported edge holds its id: a skipped row leaves it free\n this.edgeIds.add(idText);\n this.writeRole(this.idHandle, \"edge\", edge, idText, plan.names[plan.id], line);\n }\n this.warnTypeHint(plan, row, line);\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 * Warn, once, when a column that is not the type column holds a direction word.\n * @param plan - the edge plan\n * @param row - the cells\n * @param line - the row's line\n */\n private warnTypeHint(plan: EdgePlan, row: readonly string[], line: number): void {\n if (plan.typeHint < 0 || typeof parseKind(row[plan.typeHint]) !== \"string\") {\n return;\n }\n this.state.report.warnOnce(\n \"unsupported\",\n TYPE_COLUMN_IGNORED_CODE,\n `column ${JSON.stringify(plan.names[plan.typeHint])} holds the direction word ${JSON.stringify(row[plan.typeHint])} but is a plain attribute; the per-row direction is read from an exact \"Type\" column beside \"Source\" and \"Target\", or from the typeColumn option`,\n { line, element: plan.names[plan.typeHint] },\n );\n }\n\n /**\n * Report a row with the wrong number of cells. The first row too long for an auto table says\n * what an adjacency list needs, since one reads this way.\n * @param plan - the edge plan\n * @param row - the cells\n * @param line - the row's line\n */\n private reportRowWidth(plan: EdgePlan, row: readonly string[], line: number): void {\n const hint =\n this.state.csv.table === \"auto\" && row.length > plan.width && !this.adjacencyHinted\n ? '; if each line is a node followed by its neighbors, pass table: \"adjacency\"'\n : \"\";\n this.adjacencyHinted ||= hint !== \"\";\n this.state.report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `${row.length} field${plural(row.length)}, expected ${plan.width}${hint}`,\n { line },\n );\n }\n\n /**\n * The direction of an edge row: its Type cell, else the file's or the default direction.\n * @param plan - the edge plan\n * @param row - the cells\n * @param line - the row's line\n * @returns the kind, or null when the Type cell is not a direction (reported)\n */\n private rowKind(plan: EdgePlan, row: readonly string[], line: number): EdgeKind | null {\n const directed = this.state.commentDirected ?? this.state.common.defaultDirected;\n const kind: EdgeKind = directed ? \"directed\" : \"undirected\";\n if (plan.type < 0) {\n return kind;\n }\n const parsed = parseKind(row[plan.type]);\n if (parsed === null) {\n this.state.report.error(\n \"validation-error\",\n BAD_TYPE_CODE,\n `Type ${JSON.stringify(row[plan.type])} is not Directed, Undirected or Mutual`,\n { line },\n );\n return null;\n }\n return parsed ?? kind;\n }\n\n /**\n * Add an edge row's edge, counting the edge and the nodes it brings in.\n * @param sourceText - the source cell\n * @param targetText - the target cell\n * @param kind - the direction\n * @param weightText - the weight cell, or null\n * @returns the edge's index\n */\n private addEdge(sourceText: string, targetText: string, kind: EdgeKind, weightText: string | null): number {\n const { report, sink, resolver } = this.state;\n const { where } = this;\n const source = this.coerce(sourceText);\n const target = this.coerce(targetText);\n const weight = weightText === null ? undefined : parseWeightText(weightText, report);\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 const edge = resolver.addEdge(source, target, kind, weight, where);\n report.counts.edges += sink.edgeCount - before;\n report.counts.nodes += (sourceNew ? 1 : 0) + (targetNew ? 1 : 0);\n return edge;\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 cellQuoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processNodeRow(plan: NodePlan, row: string[], cellQuoted: readonly boolean[], line: number): void {\n const { report, sink } = this.state;\n const { counts } = report;\n const ordinal = this.nodeOrdinal++;\n const quoted = completeRow(plan, row, cellQuoted, report, line);\n if (row.length !== plan.width) {\n report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `${row.length} field${plural(row.length)}, expected ${plan.width}`,\n {\n line,\n },\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, `blank id cell`, { line });\n counts.skippedNodes++;\n return;\n }\n if (plan.id >= 0) {\n this.checkPadded(idText, quoted[plan.id], line);\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 `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 * Warn once per import about an unquoted id cell with leading or trailing whitespace, which is\n * a different id from the bare text (RFC 4180 keeps spaces).\n * @param text - the cell text\n * @param quoted - whether the cell was quoted\n * @param line - the row's line\n */\n private checkPadded(text: string, quoted: boolean | undefined, line: number): void {\n if (isPadded(text, quoted)) {\n this.state.report.warnOnce(\n \"validation-error\",\n PADDED_ID_CODE,\n `id ${JSON.stringify(text)} has leading or trailing whitespace and is kept as written, a different id from the trimmed text`,\n { line, element: text },\n );\n }\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\n/**\n * Whether a headerless table is a triple list whose third column is text (`a b knows`): every row\n * read so far has exactly three cells and a third cell that is not a number. That column is then an\n * attribute rather than the positional weight. A wider or ragged table keeps the positional weight,\n * so prose or an adjacency list read as edges still fails per row instead of importing.\n * @param width - the number of columns\n * @param sample - the data rows read so far\n * @returns true for a text triple list\n */\nfunction isTextTriple(width: number, sample: readonly (readonly string[])[]): boolean {\n return (\n width === 3 &&\n sample.length > 0 &&\n sample.every((row) => row.length === 3 && !isBlank(row[2]) && numberOrNull(row[2]) === null)\n );\n}\n\n/**\n * Whether the caller fixed how the input is read (a delimiter, the header, the table kind or a\n * column): it is then a table, and its first line is never refused as another format.\n * @param csv - the resolved options\n * @returns true when any of them is explicit\n */\nfunction hasExplicitDialect(csv: ResolvedCsvOptions): boolean {\n return (\n csv.delimiter !== null ||\n csv.header !== \"auto\" ||\n csv.table !== \"auto\" ||\n csv.sourceColumn !== null ||\n csv.targetColumn !== null ||\n csv.idColumn !== null ||\n (csv.typeColumn !== undefined && csv.typeColumn !== null)\n );\n}\n\n/**\n * Fit a row whose width differs from the header's by one empty last cell: a row that omits the\n * empty last cell of a header ending in a delimiter gets an unset cell appended, and a row ending\n * in a delimiter the header does not have (Excel, pandas) loses that empty cell, reported once.\n * @param plan - the plan\n * @param row - the cells (changed in place)\n * @param quoted - whether each cell was quoted\n * @param report - the report\n * @param line - the row's line\n * @returns the quoted flags of the fitted row\n */\nfunction completeRow(\n plan: EdgePlan | NodePlan,\n row: string[],\n quoted: readonly boolean[],\n report: ImportReportBuilder,\n line: number,\n): readonly boolean[] {\n if (row.length === plan.width + 1 && isUnset(row[plan.width], quoted[plan.width])) {\n row.pop();\n report.warnOnce(\n \"validation-error\",\n TRAILING_DELIMITER_CODE,\n `the row ends in a delimiter the header does not have; the empty last cell is dropped`,\n { line },\n );\n return quoted;\n }\n if (!plan.optionalLast || row.length !== plan.width - 1) {\n return quoted;\n }\n const flags = quoted.slice(0, row.length);\n row.push(\"\");\n flags.push(false);\n return flags;\n}\n\nconst HEAD_BYTES = 4096;\n\n/**\n * The openings of the formats a CSV reader would otherwise split into rows of text: an XML / HTML\n * tag or declaration (not an IRI such as `<http://a>`, nor a bracketed id such as `<alice smith>`\n * or `<a>,b`: a tag needs an attribute, or another tag after it), a JSON object (`{\"` or `{}`) or\n * array, a DOT graph, a Pajek section, GML's `Creator \"...\"` or `graph [`. Matched against the\n * start of the input.\n */\n/**\n * An endpoint column: the one the option names, else (with a header) the first with a known\n * endpoint name, else (without one) the column at its position in a headerless edge list.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param option - the sourceColumn / targetColumn option, or null\n * @param optionName - the option's name, for its error\n * @param known - the endpoint names to look for\n * @param position - the column of this endpoint in a headerless edge list\n * @returns the column index, or -1\n */\nfunction endpointColumn(\n names: readonly string[],\n header: boolean,\n option: CsvColumnRef | null,\n optionName: string,\n known: readonly string[],\n position: number,\n): number {\n if (option !== null) {\n return resolveColumnRef(names, option, optionName);\n }\n if (header) {\n return findColumn(names, known);\n }\n return names.length >= 2 ? position : -1;\n}\n\nconst TAG_NAME = String.raw`<[a-z_][\\w.-]*(?::[a-z_][\\w.-]*)?`;\nconst OTHER_FORMATS: readonly RegExp[] = [\n /^\\s*<(?:\\?xml|!doctype|!--)/i, // an XML declaration, a doctype or a comment\n new RegExp(String.raw`^\\s*${TAG_NAME}\\s*\\/?>\\s*(?:[^,;|\\s]*<|$)`, \"i\"), // a tag, then another tag\n new RegExp(String.raw`^\\s*${TAG_NAME}\\s+[a-z_][\\w.:-]*\\s*=`, \"i\"), // a tag with an attribute\n /^\\s*\\/[*/]/, // a C comment (DOT)\n /^\\s*\\{\\s*(?:[\"}]|$)/, // a JSON object\n /^\\s*\\[\\s*(?:[[{\"\\]\\d-]|$)/, // a JSON array\n /^\\s*(?:strict\\s+)?(?:di)?graph(?:\\s+\\S+)?\\s*\\{/i, // a DOT graph\n /^\\s*\\*(?:vertices|network|arcs|edges)\\b/i, // a Pajek section\n /^\\s*(?:creator\\s+\"|graph\\s*\\[)/i, // GML\n];\n\n/**\n * Sniff confidence for the registry: 0 for XML, JSON, GML, DOT (also a leading C comment) and Pajek\n * openings after the leading `#` / `%` comment lines the reader skips, and 0 for a head that is\n * only such comments (the comments of another format, too long to see past), and 0 for text\n * holding a C0 control character (hasControlCharacter); otherwise a\n * delimited first row with endpoint headers is 0.9 (even when a later row has the wrong width, which\n * the importer reports), with an id header 0.6, and other consistently delimited rows 0.3, except\n * rows of more than three space-separated words, which is plain text such as a sentence, not a\n * headerless edge list (source, target, weight), so it is 0.\n * @param head - the first bytes of the input\n * @returns a confidence in 0..1\n */\nfunction sniff(head: Uint8Array): number {\n let label = \"utf-8\";\n if (head[0] === 0xff && head[1] === 0xfe) {\n label = \"utf-16le\";\n } else if (head[0] === 0xfe && head[1] === 0xff) {\n label = \"utf-16be\";\n }\n // the decoder drops the BOM it was chosen by; the reader skips leading comment lines\n const body = stripLeadingComments(new TextDecoder(label).decode(head.subarray(0, HEAD_BYTES)), COMMENT_CHARS);\n if (body.trim().length === 0 || hasControlCharacter(body) || OTHER_FORMATS.some((r) => r.test(body))) {\n return 0;\n }\n const newline = sniffNewline(body);\n const delimiter = sniffDelimiter(body, newline, DELIMITER_CANDIDATES, '\"', false, true);\n if (delimiter === null) {\n // a header naming both endpoints is evidence enough; a short or long row later is the\n // importer's to report, not a reason to refuse the file\n const first = body.split(newline, 1)[0];\n return DELIMITER_CANDIDATES.some((d) => hasEndpointHeader(first, d)) ? 0.9 : 0;\n }\n // the first record, honoring quotes (R and pandas quote every header name)\n const records = splitRecords(body, delimiter, '\"', 2, false, true) ?? [];\n const names = headerNames(records[0] ?? []);\n if (findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0) {\n return 0.9;\n }\n // a space-split row naming an id is as likely a line of text (\"1 First node\") as a header\n if (delimiter !== \" \" && findColumn(names, ID_NAMES) >= 0) {\n return 0.6;\n }\n if (delimiter === \" \" && names.length > 3) {\n return 0;\n }\n return looksLikeProse(body, newline, delimiter) ? 0 : 0.3;\n}\n\n/**\n * Whether text holds a C0 control character other than TAB, LF and CR (a final Ctrl-Z, the DOS\n * end-of-file marker, aside): such text is binary data or another format, never a CSV a reader\n * means to load, so it is only read as CSV when the caller names the format.\n * @param text - the decoded head\n * @returns whether it holds one\n */\nfunction hasControlCharacter(text: string): boolean {\n const last = text.length - 1;\n for (let i = 0; i <= last; i++) {\n const c = text.codePointAt(i) ?? 0;\n if (c < 0x20 && c !== 0x09 && c !== 0x0a && c !== 0x0d && !(c === 0x1a && i === last)) {\n return true;\n }\n }\n return false;\n}\n\n/** A cell that reads as part of a sentence: words with a sentence end after a letter (`Monday.`, `world. Thanks`). */\nconst SENTENCE = /[A-Za-z]{2}[.!?](?:\\s|$)/;\n\n/**\n * Whether headerless delimited text is more likely a line of prose than an edge list: a single line\n * (\"hello world\", \"Dear team, the meeting is on Monday.\"), or a cell of more than one word that\n * ends a sentence. An edge list a reader means to load has more than one row and ids, not sentences;\n * such input is still read as CSV when the caller names the format.\n * @param body - the head, decoded, without its leading comments\n * @param newline - the head's newline\n * @param delimiter - the sniffed delimiter\n * @returns true for prose\n */\nfunction looksLikeProse(body: string, newline: string, delimiter: string): boolean {\n const lines = body.split(newline).filter((l) => l.trim() !== \"\");\n if (lines.length < 2) {\n return true;\n }\n const rows = splitRecords(body, delimiter, '\"', 8, false, true) ?? [];\n return rows.some((row) => row.some((cell) => /\\S\\s+\\S/.test(cell.trim()) && SENTENCE.test(cell.trim())));\n}\n\n/**\n * Whether one line, split on a delimiter, names a source and a target column.\n * @param line - the first line of the input\n * @param delimiter - the delimiter to split on\n * @returns true for an edge table header\n */\nfunction hasEndpointHeader(line: string, delimiter: string): boolean {\n const names = headerNames(splitRecords(line, delimiter, '\"', 1, false, true)?.[0] ?? []);\n return names.length > 1 && findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0;\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 commentText: \"\",\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${plural(state.coercer.mergeCount)} merged into ids other cells already produced under ids: \"number\"`,\n );\n }\n throwIfAborted(common.signal);\n return report.finish();\n}\n\n/**\n * The CSV / TSV importer plugin (subpath `@graphty/graph-io/csv`).\n * @category Built-in formats\n */\nexport const csvImporter: GraphImporter<CsvImportOptions> = Object.freeze({\n format: \"csv\",\n options: Object.freeze([\n \"decimal\",\n \"delimiter\",\n \"header\",\n \"idColumn\",\n \"labelColumn\",\n \"nodes\",\n \"rowNumberIds\",\n \"sourceColumn\",\n \"table\",\n \"targetColumn\",\n \"typeColumn\",\n ]),\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/**\n * The `nodes` option as an input: a Blob or File is read through its stream. Checked by shape, so a\n * Blob from another realm or from Node's `fs.openAsBlob()` counts too.\n * @param value - the option value\n * @returns the input, or null when there is no node table\n */\nfunction nodesInput(value: ImportInput | Blob | undefined): ImportInput | null {\n if (value === undefined) {\n return null;\n }\n const object = value as { stream?: unknown; size?: unknown };\n const blob = typeof value === \"object\" && typeof object.stream === \"function\" && typeof object.size === \"number\";\n return blob ? (value as Blob).stream() : (value as ImportInput);\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 * neighbors 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 neighbor 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 { otherFormatDialect, type ResolvedExportOptions, resolveExportOptions } from \"../../common/options.js\";\nimport { agree, plural } from \"../../common/plural.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/**\n * The format-specific options of the CSV exporter.\n * @category Built-in formats\n */\nexport interface CsvExportOptions extends CommonExportOptions {\n /**\n * The header spelling: \"gephi\" writes `Source,Target,Type,...,Weight`, with each edge's\n * direction in `Type`; \"generic\" writes `source,target,...,weight` and no direction column. A JSON\n * dialect name, from an options object shared with JSON saves, is ignored.\n * @defaultValue \"gephi\"\n */\n dialect?: \"gephi\" | \"generic\" | undefined;\n /**\n * Which table to write. \"edges\": one edge per row, with the edge attributes; isolated nodes and\n * node attributes are not in it. \"nodes\": one node per row, with the node attributes and no\n * edges. \"adjacency\": a node and its neighbors per row, which keeps every node and the node\n * order but no attributes (read it back with `table: \"adjacency\"`). To keep both nodes and\n * edges, write the node table and the edge table to two files and read them back with the\n * `nodes` import option.\n * @defaultValue \"edges\"\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | undefined;\n /**\n * The field delimiter.\n * @defaultValue \",\"\n */\n delimiter?: string | undefined;\n /**\n * The line terminator.\n * @defaultValue \"\\n\"\n */\n newline?: \"\\n\" | \"\\r\\n\" | undefined;\n /**\n * Whether to write the header row (an adjacency table never has one). A file without a header is\n * read back by position: source, target, weight, then unnamed columns. checkExport() returns\n * W_CSV_HEADERLESS when the table has other columns; for a plain headerless edge list, pass\n * `dialect: \"generic\"` and write a graph whose edges have only weights.\n * @defaultValue true\n */\n header?: boolean | undefined;\n}\n\n/**\n * The CSV loss-note codes of check(); the shared ones are LOSS's.\n * @category Built-in formats\n */\nexport const CSV_LOSS = Object.freeze({\n /**\n * Two node ids would be written as the same text (the number 5 and the text \"5\"); the save fails with\n * E_INVALID_ID.\n */\n ID_TEXT_COLLISION: LOSS.ID_TEXT_COLLISION,\n /** Ids that read back as a different type, such as the text \"7\" as the number 7. */\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 /**\n * An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and\n * reads back with that role.\n */\n ROLE_ASSUMED: LOSS.ROLE_ASSUMED,\n /** A role column (id, label) whose name the importer does not recognize; the role is lost. */\n ROLE_NAME: \"W_CSV_ROLE_NAME\",\n /** An id or label attribute that is not text reads back as text. */\n TEXT_ROLE: \"W_CSV_TEXT_ROLE\",\n /** NaN / Infinity in a numeric column read back as text. */\n NONFINITE: \"W_CSV_NONFINITE\",\n /**\n * A text value that reads back as a number or a boolean, because the format does not record that it was text (the\n * text \"42\" reads back as the number 42).\n */\n TEXT_INFERRED: LOSS.TEXT_INFERRED,\n /**\n * A text attribute reads back as a dictionary attribute, or the reverse, because the importer chooses by how often\n * its values repeat. The values are the same.\n */\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 /**\n * The edge table has no row for a node without edges, so isolated nodes are missing when the file is read back.\n * Write the node table too (`table: \"nodes\"`).\n */\n ISOLATED_NODES: \"W_CSV_ISOLATED_NODES\",\n /**\n * The edge table lists nodes in the order they first appear in an edge, so the node order changes when the file is\n * read back.\n */\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 * `header: false` with columns a file without a header cannot name: graph-io reads such a file by position\n * (source, target, weight, then columns it names column4, column5, ...), so these columns read back under other\n * names or in the wrong place (for example, an edge id in the weight's place). Write the header, or write only source,\n * target and weight with `dialect: \"generic\"`.\n */\n HEADERLESS: \"W_CSV_HEADERLESS\",\n});\n\n/**\n * What the CSV format keeps as declared.\n * @category Built-in formats\n */\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 (\n o.dialect !== undefined &&\n o.dialect !== \"gephi\" &&\n o.dialect !== \"generic\" &&\n !otherFormatDialect(\"csv\", o.dialect)\n ) {\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 // another format's dialect (a JSON \"d3\" in a shared options object) leaves CSV's default\n dialect: DIALECTS[o.dialect === \"generic\" ? \"generic\" : \"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 * W_CSV_NODE_TABLE: the node columns an edge-table or adjacency export leaves for a second, node-table export.\n * @param names - the node columns, label first\n * @param table - the table being written (\"edges\" or \"adjacency\")\n * @param notes - receives the note\n */\nfunction nodeTableNote(names: readonly string[], table: string, notes: LossNote[]): void {\n const written = names.length;\n if (written > 0) {\n const shown =\n names\n .slice(0, 3)\n .map((n) => JSON.stringify(n))\n .join(\", \") + (written > 3 ? \", ...\" : \"\");\n notes.push(\n Object.freeze({\n code: CSV_LOSS.NODE_TABLE,\n message: `the ${table === \"adjacency\" ? \"adjacency\" : \"edge\"} table has no room for node attributes: ${written} node column${plural(written)} (${shown}) ${agree(written, \"is\", \"are\")} written only by a second export with table: \"nodes\"`,\n column: null,\n count: written,\n columns: Object.freeze([...names]),\n }),\n );\n }\n}\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${plural(collisions)} ${agree(collisions, \"shares\", \"share\")} their text with another id (a number and a string); the save fails with 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${plural(changed)} ${agree(changed, \"reads\", \"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 // A node table holds no edge: the direction and mutual notes are the edge table's.\n if (csv.table !== \"nodes\" && (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${plural(snapshot.edgeCount)} ${agree(snapshot.edgeCount, \"reads\", \"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${plural(mixed)} 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 && csv.table !== \"nodes\") {\n note(\n CSV_LOSS.MUTUAL_EXPANDED,\n `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, \"is\", \"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${plural(dropped)} ${agree(dropped, \"is\", \"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 nodeTableNote(\n [...(nodeLabel === null ? [] : [nodeLabel.meta.name]), ...nodeColumns.map((c) => c.column.meta.name)],\n csv.table,\n notes,\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${plural(isolated)} ${agree(isolated, \"has\", \"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${plural(reordered)} ${agree(reordered, \"is\", \"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(\n CSV_LOSS.NONFINITE,\n `${label}: ${nonFinite} non-finite value${plural(nonFinite)} ${agree(nonFinite, \"reads\", \"read\")} back as text`,\n name,\n nonFinite,\n );\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 neighbor 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 neighbor'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.replaceAll('\"', '\"\"')}\"`;\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 * The note of a table written without its header: a headerless file is read by position (source, target, weight,\n * then unnamed columns; a node table: id, then unnamed columns), so any other column reads back elsewhere or unnamed.\n * @param plan - the export plan\n * @returns the note, or nothing when the header is written or the positions match\n */\nfunction headerlessNotes(plan: Plan): LossNote[] {\n const { csv } = plan;\n if (csv.header || csv.table === \"adjacency\") {\n return [];\n }\n const names = csv.table === \"nodes\" ? headerlessNodeColumns(plan) : headerlessEdgeColumns(plan);\n if (names.length === 0) {\n return [];\n }\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n return [\n Object.freeze({\n code: CSV_LOSS.HEADERLESS,\n message: `without a header the file is read back by position (${csv.table === \"nodes\" ? \"id\" : \"source, target, weight\"}, then unnamed columns), so ${names.length === 1 ? \"the column\" : \"the columns\"} ${shown} ${agree(names.length, \"reads\", \"read\")} back unnamed or in another column's place; write the header, or for an edge list pass dialect: \"generic\" and write only source, target and weight`,\n column: null,\n count: names.length,\n columns: Object.freeze([...names]),\n }),\n ];\n}\n\n/**\n * The node columns a headerless node table does not read back by position: every one after the id.\n * @param plan - the export plan\n * @returns their names\n */\nfunction headerlessNodeColumns(plan: Plan): string[] {\n const label = plan.nodeLabel === null ? [] : [plan.nodeLabel.meta.name];\n return [...label, ...plan.nodeColumns.map((c) => c.header)];\n}\n\n/**\n * The edge columns a headerless edge list does not read back by position: every one after the\n * endpoints and the weight, and the weight when another column comes before it.\n * @param plan - the export plan\n * @returns their names\n */\nfunction headerlessEdgeColumns(plan: Plan): string[] {\n const { dialect } = plan.csv;\n const names: string[] = [];\n if (dialect.type !== null) {\n names.push(dialect.type);\n }\n if (plan.edgeId !== null) {\n names.push(plan.edgeId.meta.name);\n }\n if (plan.edgeLabel !== null) {\n names.push(plan.edgeLabel.meta.name);\n }\n // a weight right after the target is where a headerless read expects it\n if (plan.weights.weighted && names.length > 0) {\n names.push(dialect.weight);\n }\n names.push(...plan.edgeColumns.map((c) => c.header));\n return names;\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, {\n roles: KEPT_ROLES,\n ...(plan.csv.table === \"nodes\" ? { tables: [\"node\"] as const } : {}),\n }),\n ...plan.notes,\n ...headerlessNotes(plan),\n ]);\n}\n\n/**\n * The CSV / TSV exporter plugin (subpath `@graphty/graph-io/csv`).\n * @category Built-in formats\n */\nexport const csvExporter: GraphExporter<CsvExportOptions> = Object.freeze({\n format: \"csv\",\n options: Object.freeze([\"delimiter\", \"dialect\", \"header\", \"newline\", \"table\"]),\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 * @module @graphty/graph-io/csv\n */\n\nimport {\n COLUMN_RENAMED_CODE,\n DIRECTION_FORCED_CODE,\n DIRECTION_REFUSED_CODE,\n ELEMENT_ISSUE,\n ENCODING_FALLBACK_CODE,\n INPUT_ISSUE,\n INVALID_ENCODING_CODE,\n INVALID_UTF8_CODE,\n MIXED_DIRECTION_CODE,\n OPTION_IGNORED_CODE,\n PRECISION_CODE,\n SINK_OPTION_CODE,\n UNKNOWN_ENCODING_CODE,\n} from \"../../common/codes.js\";\nimport { WIDENING_UNSUPPORTED_CODE } from \"../../common/text.js\";\nimport {\n AMBIGUOUS_COLUMN_CODE,\n BAD_TYPE_CODE,\n COLUMN_MISSING_CODE,\n COMMENT_DIRECTION_CODE,\n COMMENT_LIKE_RECORD_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 OTHER_FORMAT_CODE,\n PADDED_ID_CODE,\n ROLE_TAKEN_CODE,\n SINGLE_COLUMN_CODE,\n STRAY_QUOTE_CODE,\n TRAILING_DELIMITER_CODE,\n TRAILING_HEADER_DELIMITER_CODE,\n TYPE_COLUMN_IGNORED_CODE,\n WEIGHT_AS_ATTRIBUTE_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, by name: the codes shared with\n * the other importers and the CSV-specific ones. A key is the code without\n * its severity and format prefixes.\n * @category Built-in formats\n */\nexport const CSV_ISSUE = Object.freeze({\n ...INPUT_ISSUE,\n ...ELEMENT_ISSUE,\n /** An integer beyond 2^53 was stored as the nearest 64-bit float; reported once per column. */\n PRECISION: PRECISION_CODE,\n /** The input is empty. The import stops. */\n EMPTY_INPUT: EMPTY_INPUT_CODE,\n /** The input is not valid UTF-8. The import stops. */\n INVALID_UTF8: INVALID_UTF8_CODE,\n /**\n * Some bytes are not valid in the encoding that was chosen (by a byte order mark, the file's declaration or the\n * `encoding` option). The import stops.\n */\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 /**\n * The header names no source and target columns and no id column. Pass `sourceColumn` and `targetColumn` to name\n * the columns. The import stops.\n */\n NO_ENDPOINT_COLUMNS: NO_ENDPOINT_COLUMNS_CODE,\n /**\n * The node table has no id column. Pass `idColumn` to name it, or `rowNumberIds: true` to number the rows. The\n * import stops.\n */\n NO_ID_COLUMN: NO_ID_COLUMN_CODE,\n /**\n * A row with a different number of fields than the header; the row is skipped. An adjacency list read without\n * `table: \"adjacency\"` gives one per long row, and the first one's message suggests the option.\n */\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 /** A quoted field is never closed. The import stops. */\n UNCLOSED_QUOTE: UNCLOSED_QUOTE_CODE,\n /**\n * There is text after a closing quote, such as `\"a\"b`. Inside a quoted field, write a quote as two quotes. The\n * import stops.\n */\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 different id cells became the same number because `ids` is \"number\", so their nodes were merged. */\n ID_MERGED: ID_MERGED_CODE,\n /** An explicitly named weight column the file does not have. */\n COLUMN_MISSING: COLUMN_MISSING_CODE,\n /**\n * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as\n * a plain attribute.\n */\n ROLE_TAKEN: ROLE_TAKEN_CODE,\n /**\n * An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example a\n * repeated column header.\n */\n COLUMN_RENAMED: COLUMN_RENAMED_CODE,\n /** You set an option this format does not use; it had no effect. The message names the option. */\n OPTION_IGNORED: OPTION_IGNORED_CODE,\n /**\n * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,\n * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.\n */\n SINK_OPTION: SINK_OPTION_CODE,\n /**\n * You read into a graph builder whose direction is already set, or which already holds edges, so the file is read\n * with the builder's direction instead of its own.\n */\n DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,\n /** Edges of the other direction were read with the direction `onMixedDirection` chose. */\n DIRECTION_FORCED: DIRECTION_FORCED_CODE,\n /**\n * The graph has both directed and undirected edges and `onMixedDirection` is \"error\". An import stops; a save to a\n * format that holds one direction per file fails with E_DIRECTED. Pass \"directed\" or \"undirected\" to read or write\n * it anyway.\n */\n MIXED_DIRECTION: MIXED_DIRECTION_CODE,\n /**\n * Your graph builder cannot change an attribute's type after its first value, so a text column keeps the type of\n * its first values.\n */\n WIDENING_UNSUPPORTED: WIDENING_UNSUPPORTED_CODE,\n /** The input starts like another format (XML or HTML, JSON, GML, DOT or Pajek), not CSV. The import stops. */\n OTHER_FORMAT: OTHER_FORMAT_CODE,\n /**\n * A quote inside a field that does not start with a quote is kept as part of the text. Reported once per import.\n */\n STRAY_QUOTE: STRAY_QUOTE_CODE,\n /** A leading comment's direction disagrees with defaultDirected or an earlier comment. */\n COMMENT_DIRECTION: COMMENT_DIRECTION_CODE,\n /**\n * A column named like `Type` holds direction words (Directed, Undirected), but graph-io reads direction only from\n * a `Type` column beside `Source` and `Target`, or from the `typeColumn` option. Reported once per import.\n */\n TYPE_COLUMN_IGNORED: TYPE_COLUMN_IGNORED_CODE,\n /** The header ends in a delimiter; rows without the empty last cell are complete. */\n TRAILING_HEADER_DELIMITER: TRAILING_HEADER_DELIMITER_CODE,\n /** Every row is one cell another delimiter would split (a likely wrong delimiter option). */\n SINGLE_COLUMN: SINGLE_COLUMN_CODE,\n /** Several header columns name one role; the one not chosen is a plain attribute. */\n AMBIGUOUS_COLUMN: AMBIGUOUS_COLUMN_CODE,\n /**\n * An id that is not quoted has spaces at its start or end; they are kept, so it is a different id from the trimmed\n * text. Reported once per import.\n */\n PADDED_ID: PADDED_ID_CODE,\n /** A leading # / % line skipped as a comment has the fields of a record. */\n COMMENT_LIKE_RECORD: COMMENT_LIKE_RECORD_CODE,\n /** Every data row ends in one extra delimiter; the empty last cell is dropped. Reported once per import. */\n TRAILING_DELIMITER: TRAILING_DELIMITER_CODE,\n /**\n * In a file without a header, the third column holds text, so it is read as an attribute, not as the edge weight.\n * Reported once per import.\n */\n WEIGHT_AS_ATTRIBUTE: WEIGHT_AS_ATTRIBUTE_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","type","lines","headers"],"mappings":"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"}
|
|
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 synthesized 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 { plural } from \"../../common/plural.js\";\nimport { inferTextDtype } from \"../../common/text.js\";\n\n/**\n * A column named by header text or by 0-based position.\n * @category Built-in formats\n */\nexport type CsvColumnRef = string | number;\n\n/**\n * Header candidates of the source endpoint, in priority order (matched exactly, then case-insensitively).\n * @category Plugin helpers\n */\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/**\n * Header candidates of the target endpoint.\n * @category Plugin helpers\n */\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/**\n * Header candidates of a node table's id column.\n * @category Plugin helpers\n */\nexport const ID_NAMES: readonly string[] = Object.freeze([\"id\", \"node\", \"name\", \"key\"]);\n\n/**\n * Header candidates of a label column (node or edge).\n * @category Plugin helpers\n */\nexport const LABEL_NAMES: readonly string[] = Object.freeze([\"label\"]);\n\n/**\n * Header candidates of an edge id column.\n * @category Plugin helpers\n */\nexport const EDGE_ID_NAMES: readonly string[] = Object.freeze([\"id\"]);\n\n/**\n * The Gephi per-row direction column.\n * @category Plugin helpers\n */\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 * @category Plugin helpers\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 * @category Plugin helpers\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${plural(names.length)})`,\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 * A two-column row holding a known name is still data when its second cell is the second row's\n * first cell and it does not name both endpoints: a header names columns, while an edge list whose\n * ids happen to be such words (`key,lock` over `lock,door`) chains its ids from row to row.\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 * @category Plugin helpers\n */\nexport function looksLikeHeader(first: readonly string[], second: readonly string[] | null): boolean {\n let allText = true;\n let marker = false;\n for (const cell of first) {\n const text = cell.trim();\n if (HEADER_MARKERS.has(text.toLowerCase())) {\n marker = true;\n }\n if (text.length === 0 || inferTextDtype(text) !== \"string\") {\n allText = false;\n }\n }\n if (marker) {\n const names = headerNames(first);\n if (second === null || (findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0)) {\n return true;\n }\n const chained = first.length === 2 && second.length >= 1 && first[1].trim() === second[0].trim();\n return !chained || first[1].trim().length === 0;\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 synthesized header of a headerless file: `column1` ... `columnN`.\n * @param width - the number of cells of the first row\n * @returns the names\n * @category Plugin helpers\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 * @category Plugin helpers\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 * @category Plugin helpers\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,neighbor[: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 neighbors, one edge per neighbor in row order. A neighbor 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 OPTION_IGNORED_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 { plural } from \"../../common/plural.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 {\n checkRecordSyntax,\n type CsvReaderOptions,\n CsvRecordReader,\n DELIMITER_CANDIDATES,\n sniffDelimiter,\n sniffNewline,\n splitRecords,\n stripLeadingComments,\n} from \"./records.js\";\nimport { InferredColumn } from \"./values.js\";\n\n/**\n * The format-specific options of the CSV importer.\n * @category Built-in formats\n */\nexport interface CsvImportOptions extends CommonImportOptions {\n /**\n * The field delimiter. The default is to detect it from the first rows: `,`, tab, `;`, `|` or\n * space. It applies to the `nodes` table too.\n * @defaultValue detected\n */\n delimiter?: string | undefined;\n /**\n * The decimal separator of numbers. With \",\", a cell such as `2,5` (digits, a comma, digits) is\n * read as the number 2.5, for weights and attributes alike, and the delimiter is detected among\n * the others (`;`, tab, `|`, space), so a spreadsheet saved in a locale that writes decimal\n * commas reads as it is. A thousands separator is not read.\n * @defaultValue \".\"\n */\n decimal?: \".\" | \",\" | undefined;\n /**\n * Whether the first row is a header; \"auto\" decides from its content (a header names columns, a\n * data row holds ids and numbers). It applies to the `nodes` table too.\n * @defaultValue \"auto\"\n */\n header?: boolean | \"auto\" | undefined;\n /**\n * What the input is: \"edges\" (one edge per row), \"nodes\" (one node per row), \"adjacency\" (a\n * node and its neighbors per row, `node,neighbor[:weight],...`, with no header unless you pass\n * `header: true`), or \"auto\": an edge table when source and target columns can be found, a node\n * table when only an id column can. An adjacency table is never detected, because its rows look\n * like an edge list.\n * @defaultValue \"auto\"\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | \"auto\" | undefined;\n /**\n * The source column, by name or 0-based position. The default is the column whose header is a\n * recognized source name (`source`, `from`, `src` and others; the CSV format page lists them\n * all); in a file without a header, the first column. A header with no recognized source and\n * target names needs this option and `targetColumn`.\n * @defaultValue from the header\n */\n sourceColumn?: CsvColumnRef | undefined;\n /**\n * The target column, by name or 0-based position. The default is the column whose header is a\n * recognized target name (`target`, `to`, `dst` and others; the CSV format page lists them\n * all); in a file without a header, the second column.\n * @defaultValue from the header\n */\n targetColumn?: CsvColumnRef | undefined;\n /**\n * The column that gives each edge's direction (Directed / Undirected / Mutual), by name or\n * position. The default is the `Type` column of a Gephi table (one whose header has exactly\n * `Source` and `Target`); null reads no such column.\n * @defaultValue Gephi's Type\n */\n typeColumn?: CsvColumnRef | null | undefined;\n /**\n * The id column of a node table, by name or position. The default is the column the header\n * names `id` (or `node`, `name`, `key`).\n * @defaultValue from the header\n */\n idColumn?: CsvColumnRef | undefined;\n /**\n * The label column of a node table, by name or position. The default is the column the header\n * names `label` (in any case). With `nodeIdFrom: \"label\"`, the node ids come from this column,\n * and the edge table must name its nodes by these labels too.\n * @defaultValue from the header\n */\n labelColumn?: CsvColumnRef | undefined;\n /**\n * A node table to read before the edges: its ids become nodes and its other columns node\n * attributes. Pass it as a string, bytes, a stream, or a `File` or `Blob` (for example, the second file a\n * user picked).\n */\n nodes?: ImportInput | Blob | undefined;\n /**\n * Give the nodes of a node table without an id column the row number as id (0 for the first\n * data row, turned into an id by `ids`), instead of failing with E_CSV_NO_ID_COLUMN. It applies\n * to the `nodes` table when you pass one, else to the input. Under `header: \"auto\"` that\n * table's first row is then read as a header; under `header: false` the option is ignored with\n * a warning, because a table without a header takes its ids from its first column.\n * @defaultValue false\n */\n rowNumberIds?: boolean | undefined;\n}\n\n/**\n * The input holds no header row at all.\n * @category Issue and loss codes\n */\nexport const EMPTY_INPUT_CODE = SHARED_EMPTY_INPUT_CODE;\n/**\n * The header names no source / target (or, for a node table, no id) column.\n * @category Issue and loss codes\n */\nexport const NO_ENDPOINT_COLUMNS_CODE = \"E_CSV_NO_ENDPOINT_COLUMNS\";\n/**\n * A node table without an id column.\n * @category Issue and loss codes\n */\nexport const NO_ID_COLUMN_CODE = \"E_CSV_NO_ID_COLUMN\";\n/**\n * A row with a different number of fields than the header.\n * @category Issue and loss codes\n */\nexport const FIELD_COUNT_CODE = \"E_CSV_FIELD_COUNT\";\n/**\n * An edge row with a blank source or target cell.\n * @category Issue and loss codes\n */\nexport const MISSING_ENDPOINT_CODE = SHARED_MISSING_ENDPOINT_CODE;\n/**\n * A node row with a blank id cell.\n * @category Issue and loss codes\n */\nexport const MISSING_ID_CODE = SHARED_MISSING_ID_CODE;\n/**\n * A Type cell that is not Directed, Undirected or Mutual.\n * @category Issue and loss codes\n */\nexport const BAD_TYPE_CODE = \"E_CSV_BAD_TYPE\";\n/**\n * The table has a header and no data rows.\n * @category Issue and loss codes\n */\nexport const NO_DATA_ROWS_CODE = \"W_CSV_NO_DATA_ROWS\";\n/**\n * A node table row repeats an id; its attributes overwrite the earlier row's.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_NODE_CODE = SHARED_DUPLICATE_NODE_CODE;\n/**\n * Two different id cells became the same number because `ids` is \"number\", so their nodes were merged.\n * @category Issue and loss codes\n */\nexport const ID_MERGED_CODE = SHARED_ID_MERGED_CODE;\n/**\n * An explicitly named weight column the file does not have.\n * @category Issue and loss codes\n */\nexport const COLUMN_MISSING_CODE = \"W_CSV_COLUMN_MISSING\";\n/**\n * You read into a graph builder that already has an id or label attribute, so this file's one is kept without its\n * role.\n * @category Issue and loss codes\n */\nexport const ROLE_TAKEN_CODE = SHARED_ROLE_TAKEN_CODE;\n/**\n * A repeated edge id (the column is unique); the edge is skipped.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_EDGE_ID_CODE = SHARED_DUPLICATE_EDGE_ID_CODE;\n/**\n * The input starts like another format (XML or HTML, JSON, GML, DOT or Pajek), not CSV. The import stops.\n * @category Issue and loss codes\n */\nexport const OTHER_FORMAT_CODE = \"E_CSV_OTHER_FORMAT\";\n/**\n * A quote inside an unquoted field (RFC 4180 forbids it); the quote is kept as text.\n * @category Issue and loss codes\n */\nexport const STRAY_QUOTE_CODE = \"W_CSV_STRAY_QUOTE\";\n/**\n * A leading comment's direction disagrees with defaultDirected or with an earlier comment.\n * @category Issue and loss codes\n */\nexport const COMMENT_DIRECTION_CODE = \"W_CSV_COMMENT_DIRECTION\";\n/**\n * A column named like Gephi's Type holds direction words but is read as a plain attribute.\n * @category Issue and loss codes\n */\nexport const TYPE_COLUMN_IGNORED_CODE = \"W_CSV_TYPE_COLUMN_IGNORED\";\n/**\n * The header ends in a delimiter; rows without the empty last cell are read as complete.\n * @category Issue and loss codes\n */\nexport const TRAILING_HEADER_DELIMITER_CODE = \"W_CSV_TRAILING_HEADER_DELIMITER\";\n/**\n * Every row is one cell that another delimiter would split (a likely wrong delimiter).\n * @category Issue and loss codes\n */\nexport const SINGLE_COLUMN_CODE = \"W_CSV_SINGLE_COLUMN\";\n/**\n * Several header columns name the same role; the one not chosen is a plain attribute.\n * @category Issue and loss codes\n */\nexport const AMBIGUOUS_COLUMN_CODE = \"W_CSV_AMBIGUOUS_COLUMN\";\n/**\n * An unquoted id with leading or trailing whitespace (kept, RFC 4180), distinct from the bare id.\n * @category Issue and loss codes\n */\nexport const PADDED_ID_CODE = \"W_CSV_PADDED_ID\";\n/**\n * A leading `#` / `%` line skipped as a comment has the fields of a record.\n * @category Issue and loss codes\n */\nexport const COMMENT_LIKE_RECORD_CODE = \"W_CSV_COMMENT_LIKE_RECORD\";\n/**\n * Data rows end in one extra empty cell (a trailing delimiter); the cell is dropped.\n * @category Issue and loss codes\n */\nexport const TRAILING_DELIMITER_CODE = \"W_CSV_TRAILING_DELIMITER\";\n/**\n * A headerless three-column table's third column holds text, so it is an attribute, not the weight.\n * @category Issue and loss codes\n */\nexport const WEIGHT_AS_ATTRIBUTE_CODE = \"W_CSV_WEIGHT_AS_ATTRIBUTE\";\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]);\n\n/** The CSV options with defaults applied. */\ninterface ResolvedCsvOptions {\n readonly delimiter: string | null;\n readonly decimalComma: boolean;\n readonly header: boolean | \"auto\";\n readonly table: NonNullable<CsvImportOptions[\"table\"]>;\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 labelColumn: CsvColumnRef | null;\n readonly nodes: ImportInput | null;\n readonly rowNumberIds: boolean;\n /** The caller's explicit defaultDirected; null when it was left to its default. */\n readonly explicitDirected: boolean | null;\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 /** A column named like Type (any case) left as an attribute, or -1; its direction words are reported. */\n readonly typeHint: number;\n readonly id: number;\n readonly label: number;\n readonly attributes: readonly number[];\n /** Whether a row may omit the last cell (the header ends in a delimiter). */\n readonly optionalLast: boolean;\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 /** Whether a row may omit the last cell (the header ends in a delimiter). */\n readonly optionalLast: boolean;\n}\n\n/** An adjacency table: no columns, a node and its neighbors per row. */\ninterface AdjacencyPlan {\n readonly kind: \"adjacency\";\n readonly names: readonly string[];\n}\n\n/** Everything one import call shares between its tables. */\n/** A row read before the plan exists, kept to be pushed once it does. */\ninterface PendingRow {\n readonly row: string[];\n readonly quoted: readonly boolean[];\n readonly line: number;\n}\n\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 /** The comment line that set commentDirected. */\n commentText: string;\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/** A number written with a decimal comma: digits, one comma, digits, an optional exponent. */\nconst DECIMAL_COMMA = /^\\s*[+-]?\\d+,\\d+(?:[eE][+-]?\\d+)?\\s*$/;\n\n/**\n * The direction a SNAP or KONECT comment line 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). Only whole words count, so\n * `% symbols` or `# bipartite-ish` declare nothing.\n * @param comment - a leading comment line\n * @returns true / false when the line declares the direction, null otherwise\n */\nfunction commentDirection(comment: string): boolean | null {\n const text = comment.slice(1).trim().toLowerCase();\n if (/^(?:directed graph|asym)\\b/.test(text)) {\n return true;\n }\n if (/^(?:undirected graph|sym|bip)\\b/.test(text)) {\n return false;\n }\n return null;\n}\n\n/**\n * The name of a direction, for messages.\n * @param directed - the direction\n * @returns \"directed\" or \"undirected\"\n */\nfunction directionName(directed: boolean): string {\n return directed ? \"directed\" : \"undirected\";\n}\n\n/**\n * Whether an unquoted id cell has leading or trailing whitespace (kept as written, RFC 4180).\n * @param text - the cell text\n * @param quoted - whether the cell was quoted\n * @returns true when padded\n */\nfunction isPadded(text: string, quoted: boolean | undefined): boolean {\n return quoted !== true && text.trim().length !== text.length;\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 checkDialectOptions(o);\n for (const name of [\"sourceColumn\", \"targetColumn\", \"idColumn\", \"labelColumn\"] 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 checkAdjacencyOptions(o);\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 decimalComma: o.decimal === \",\",\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 labelColumn: o.labelColumn ?? null,\n nodes: nodesInput(o.nodes),\n rowNumberIds: o.rowNumberIds ?? false,\n explicitDirected: typeof options?.defaultDirected === \"boolean\" ? options.defaultDirected : null,\n };\n}\n\n/**\n * Check the dialect and table options: delimiter, decimal, header and table.\n * @param o - the caller's CSV options\n */\nfunction checkDialectOptions(o: CsvImportOptions): void {\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.decimal !== undefined && o.decimal !== \".\" && o.decimal !== \",\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'option decimal: expected \".\" or \",\"', {\n option: \"decimal\",\n found: o.decimal,\n });\n }\n if (o.decimal === \",\" && o.delimiter === \",\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", 'options delimiter and decimal are both \",\"; pass delimiter: \";\"', {\n option: \"decimal\",\n found: o.decimal,\n });\n }\n if (o.delimiter !== undefined) {\n // one character, not a line break, not the quote\n checkRecordSyntax({ delimiter: o.delimiter, quote: '\"' });\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}\n\n/**\n * Check the options an adjacency table has no use for: column options and rowNumberIds.\n * @param o - the caller's CSV options\n */\nfunction checkAdjacencyOptions(o: CsvImportOptions): void {\n for (const name of [\"sourceColumn\", \"targetColumn\", \"typeColumn\", \"idColumn\", \"labelColumn\"] 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\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/**\n * The number of set neighbor cells of an adjacency row (every cell after the first).\n * @param row - the cells\n * @param quoted - whether each cell was quoted\n * @returns the set neighbor cells\n */\nfunction setNeighborCells(row: readonly string[], quoted: readonly boolean[]): number {\n let neighbors = 0;\n for (let k = 1; k < row.length; k++) {\n if (!isUnset(row[k], quoted[k])) {\n neighbors++;\n }\n }\n return neighbors;\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 neighbor 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 * @category Plugin helpers\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 /** Whether a field-count error has already suggested `table: \"adjacency\"`. */\n private adjacencyHinted = false;\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 // a decimal comma is never the delimiter\n ...(state.csv.decimalComma ? { candidates: DELIMITER_CANDIDATES.filter((d) => d !== \",\") } : {}),\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 // an adjacency table has no header: an empty one is the empty graph\n allowEmpty: kind === \"adjacency\",\n // explicit dialect options say the caller knows the input is a table\n inspect: hasExplicitDialect(state.csv)\n ? undefined\n : (preview, line) => {\n this.refuseOtherFormat(preview, line);\n },\n };\n this.reader = new CsvRecordReader(input, state.report, readerOptions);\n }\n\n /**\n * Read every row; the reader is closed (and a stream canceled) 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 let first = await iterator.next();\n if (!first.done && (this.reader.sepDirective || this.isSepDirective(first.value))) {\n // Excel's `sep=;` line: the reader took the delimiter from it; it is not a row\n first = await iterator.next();\n }\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: PendingRow[] = [];\n const header = await this.decideHeader(\n iterator,\n { row: firstRow, quoted: firstQuoted, line: firstLine },\n pending,\n );\n const names = header ? uniqueNames(headerNames(firstRow), report, firstLine) : positionalNames(firstRow.length);\n const last = firstRow.length - 1;\n const optionalLast = header && last > 0 && isUnset(firstRow[last], firstQuoted[last]);\n this.applyCommentDirection();\n this.plan = this.resolvePlan(\n names,\n header,\n firstLine,\n pending.map((p) => p.row),\n optionalLast,\n );\n if (optionalLast) {\n report.warning(\n \"validation-error\",\n TRAILING_HEADER_DELIMITER_CODE,\n `the header ends in a delimiter; rows without the empty last cell are read as complete`,\n { line: firstLine, element: names[last] },\n );\n }\n this.reportCommentLikeRecords(names.length);\n this.reportSingleColumn(\n names.length,\n pending.map((p) => p.row),\n firstLine,\n );\n this.prepareColumns(firstLine);\n for (const { row, quoted, line } of pending) {\n this.processRow(row, quoted, line);\n }\n await this.readRemainingRows(iterator);\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 this.reportReaderFindings();\n }\n\n /**\n * Decide whether the first row is the header, reading the second row when the header option is\n * \"auto\", and queue the rows read so far that are data.\n * @param iterator - the record iterator, past the first row\n * @param first - the first row\n * @param pending - receives the data rows read so far, in order\n * @returns whether the first row is the header\n */\n private async decideHeader(\n iterator: AsyncGenerator<string[], void, undefined>,\n first: PendingRow,\n pending: PendingRow[],\n ): Promise<boolean> {\n const mode = this.headerMode();\n let second: PendingRow | null = null;\n let header: boolean;\n if (mode === \"auto\") {\n const next = await iterator.next();\n second = next.done\n ? null\n : { row: next.value, quoted: this.reader.quoted.slice(0, next.value.length), line: this.reader.line };\n // a column option naming a cell of the first row makes that row the header\n header = looksLikeHeader(first.row, second?.row ?? null) || this.namesExplicitColumn(first.row);\n } else {\n header = mode;\n }\n if (!header) {\n pending.push(first);\n }\n if (second !== null) {\n pending.push(second);\n }\n if (this.rowNumbers && !header) {\n this.state.report.warning(\n \"unsupported\",\n OPTION_IGNORED_CODE,\n \"option rowNumberIds ignored: a headerless node table takes its ids from column 0\",\n { line: first.line, element: \"rowNumberIds\" },\n );\n }\n return header;\n }\n\n /**\n * The header option as this table reads it: an adjacency table has no header unless the caller\n * says so (its rows vary in width); a table numbered by rowNumberIds has one, since a headerless\n * table takes its ids from column 0.\n * @returns true, false or \"auto\"\n */\n private headerMode(): boolean | \"auto\" {\n const mode = this.state.csv.header;\n if (mode === \"auto\" && this.kind === \"adjacency\") {\n return false;\n }\n if (mode === \"auto\" && this.rowNumbers) {\n return true;\n }\n return mode;\n }\n\n /**\n * Push every row after the header and the rows read to decide it, then finish the column writers.\n * @param iterator - the record iterator\n */\n private async readRemainingRows(iterator: AsyncGenerator<string[], void, undefined>): Promise<void> {\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 }\n\n /**\n * Whether a row is Excel's delimiter directive (`sep=;`) under a delimiter the caller gave (a\n * sniffed one comes from the directive itself: `reader.sepDirective`).\n * @param row - the first row\n * @returns true for the directive\n */\n private isSepDirective(row: readonly string[]): boolean {\n return (\n this.state.csv.delimiter !== null &&\n !this.reader.quoted[0] &&\n /^sep=.$/i.test(row.join(this.state.csv.delimiter))\n );\n }\n\n /**\n * Fail when the input opens like another format (an HTML error page, a JSON, GML, DOT or Pajek\n * file handed to the CSV importer), which any delimiter would otherwise read as rows of text.\n * Runs on the raw preview before any record is parsed, so a quoting error in the other\n * format's syntax cannot abort first.\n * @param preview - the start of the input, leading comments removed\n * @param line - the line it starts on\n */\n private refuseOtherFormat(preview: string, line: number): void {\n if (OTHER_FORMATS.some((r) => r.test(preview))) {\n this.state.report.fail(\n OTHER_FORMAT_CODE,\n `the input opens like another format (XML / HTML, JSON, GML, DOT or Pajek), not CSV: ${JSON.stringify(preview.trimStart().slice(0, 60))}`,\n { line },\n );\n }\n }\n\n /**\n * Whether a column option names a cell of the first row by text.\n * @param row - the first row\n * @returns true when sourceColumn, targetColumn, idColumn or typeColumn names one of its cells\n */\n private namesExplicitColumn(row: readonly string[]): boolean {\n const { csv } = this.state;\n const names = headerNames(row);\n return [csv.sourceColumn, csv.targetColumn, csv.idColumn, csv.typeColumn, csv.labelColumn].some(\n (ref) => typeof ref === \"string\" && findColumn(names, [ref]) >= 0,\n );\n }\n\n /**\n * Take the file-level direction from the leading SNAP / KONECT comments of this table: the first\n * declaring line wins; a later line declaring the other direction, and a declaration that\n * overrides the caller's explicit defaultDirected, are reported.\n */\n private applyCommentDirection(): void {\n const { state } = this;\n const { leadingComments, leadingCommentLines } = this.reader;\n for (let i = 0; i < leadingComments.length; i++) {\n const comment = leadingComments[i];\n const directed = commentDirection(comment);\n if (directed === null) {\n continue;\n }\n const where = { line: leadingCommentLines[i], element: comment };\n if (state.commentDirected === null) {\n state.commentDirected = directed;\n state.commentText = comment;\n const explicit = state.csv.explicitDirected;\n if (explicit !== null && explicit !== directed) {\n state.report.warning(\n \"validation-error\",\n COMMENT_DIRECTION_CODE,\n `${JSON.stringify(comment)} declares a ${directionName(directed)} graph, which overrides defaultDirected: ${explicit}`,\n where,\n );\n }\n } else if (state.commentDirected !== directed) {\n state.report.warning(\n \"validation-error\",\n COMMENT_DIRECTION_CODE,\n `${JSON.stringify(comment)} declares a ${directionName(directed)} graph but ${JSON.stringify(state.commentText)} declared a ${directionName(state.commentDirected)} one; the first applies`,\n where,\n );\n }\n }\n }\n\n /**\n * Report a leading `#` / `%` line that has the fields of a record (a first id starting with `#`,\n * swallowed as a comment): its text directly follows the comment character and splits into as\n * many cells as the table has columns. SNAP and KONECT comment lines put a space after it.\n * @param width - the number of columns\n */\n private reportCommentLikeRecords(width: number): void {\n const { delimiter, leadingComments, leadingCommentLines } = this.reader;\n if (width < 2 || delimiter === null) {\n return;\n }\n for (let i = 0; i < leadingComments.length; i++) {\n const comment = leadingComments[i];\n if (comment.length > 1 && comment[1].trim().length > 0 && comment.split(delimiter).length === width) {\n this.state.report.warnOnce(\n \"parse-error\",\n COMMENT_LIKE_RECORD_CODE,\n `${JSON.stringify(comment)} was skipped as a leading comment but has the fields of a record; quote an id that starts with # or %`,\n { line: leadingCommentLines[i], element: comment },\n );\n }\n }\n }\n\n /**\n * Report a one-column table whose cells another delimiter would split consistently (a delimiter\n * option that does not match the file).\n * @param width - the number of columns\n * @param rows - the data rows read so far\n * @param line - the first line\n */\n private reportSingleColumn(width: number, rows: readonly (readonly string[])[], line: number): void {\n if (width !== 1 || rows.length === 0 || this.plan?.kind === \"adjacency\") {\n return;\n }\n const candidates = DELIMITER_CANDIDATES.filter((d) => d !== this.reader.delimiter);\n const other = sniffDelimiter(`${rows.map((r) => r[0]).join(\"\\n\")}\\n`, \"\\n\", candidates);\n if (other !== null) {\n this.state.report.warning(\n \"parse-error\",\n SINGLE_COLUMN_CODE,\n `every row read so far is one cell that ${JSON.stringify(other)} splits consistently; pass delimiter: ${JSON.stringify(other)} if that is the file's delimiter`,\n { line },\n );\n }\n }\n\n /** Report what the record reader noticed while reading: a stray quote. */\n private reportReaderFindings(): void {\n const { report } = this.state;\n const { strayQuoteLine } = this.reader;\n if (strayQuoteLine > 0) {\n report.warnOnce(\n \"parse-error\",\n STRAY_QUOTE_CODE,\n `a quote inside an unquoted field is kept as text (RFC 4180 quotes a whole field)`,\n { line: strayQuoteLine },\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 * @param sample - the data rows read so far\n * @param optionalLast - whether a row may omit the last cell\n * @returns the plan; the import aborts when no endpoints (or id) resolve\n */\n private resolvePlan(\n names: readonly string[],\n header: boolean,\n line: number,\n sample: readonly (readonly string[])[],\n optionalLast: boolean,\n ): EdgePlan | NodePlan | AdjacencyPlan {\n const { csv } = this.state;\n if (this.kind === \"adjacency\") {\n return { kind: \"adjacency\", names: [] };\n }\n let source = -1;\n let target = -1;\n if (this.kind !== \"nodes\") {\n source = endpointColumn(names, header, csv.sourceColumn, \"sourceColumn\", SOURCE_NAMES, 0);\n target = endpointColumn(names, header, csv.targetColumn, \"targetColumn\", TARGET_NAMES, 1);\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, line, sample, optionalLast);\n }\n this.refuseWithoutEndpoints(names, header, source, target, line);\n return this.nodePlan(names, header, line, optionalLast);\n }\n\n /**\n * Refuse a table without both endpoint columns, unless it can be a node table: an edge table\n * was asked for, a single endpoint column was found, or nothing names an id.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param source - the source column, or -1\n * @param target - the target column, or -1\n * @param line - the header line\n */\n private refuseWithoutEndpoints(\n names: readonly string[],\n header: boolean,\n source: number,\n target: number,\n line: number,\n ): void {\n const { csv, report } = this.state;\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n if (source >= 0 || target >= 0) {\n // one endpoint column is a broken edge table, never a node table\n const [has, missing] = source >= 0 ? [\"source\", \"target\"] : [\"target\", \"source\"];\n report.fail(\n NO_ENDPOINT_COLUMNS_CODE,\n `the header has a ${has} column (${JSON.stringify(names[Math.max(source, target)])}) but no ${missing} column (${shown})`,\n { line },\n { columns: [...names] },\n );\n }\n if (\n this.kind === \"edges\" ||\n (this.kind === \"auto\" && (csv.sourceColumn !== null || csv.targetColumn !== null || csv.nodes !== 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 : names.length >= 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 }\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 * @param line - the header (or first) line\n * @param sample - the data rows read so far\n * @param optionalLast - whether a row may omit the last cell\n * @returns the plan\n */\n private edgePlan(\n names: readonly string[],\n header: boolean,\n source: number,\n target: number,\n line: number,\n sample: readonly (readonly string[])[],\n optionalLast: boolean,\n ): EdgePlan {\n const { csv } = this.state;\n const claimed = new Set<number>([source, target]);\n const weight = this.weightColumn(names, header, claimed, line, sample);\n if (weight >= 0) {\n claimed.add(weight);\n }\n if (header) {\n this.reportEndpointRivals(names, source, target, weight, line);\n }\n const type = this.typeColumn(names, header, source, target, claimed);\n if (type >= 0) {\n claimed.add(type);\n }\n const typeHint =\n header && type < 0 && csv.typeColumn === undefined ? findFree(names, [TYPE_NAME], claimed) : -1;\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 {\n kind: \"edges\",\n names,\n width: names.length,\n source,\n target,\n weight,\n type,\n typeHint,\n id,\n label,\n attributes,\n optionalLast,\n };\n }\n\n /**\n * The weight column of an edge table: the weightFrom column of a header (a warning when it was\n * asked for and is missing), else the third column of a headerless edge list unless it holds text.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param claimed - the endpoint columns\n * @param line - the header (or first) line\n * @param sample - the data rows read so far\n * @returns the column index, or -1\n */\n private weightColumn(\n names: readonly string[],\n header: boolean,\n claimed: ReadonlySet<number>,\n line: number,\n sample: readonly (readonly string[])[],\n ): number {\n const { common, report } = this.state;\n if (common.weightFrom === null) {\n return -1;\n }\n if (header) {\n const 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, element: common.weightFrom },\n );\n }\n return weight;\n }\n if (names.length < 3 || claimed.has(2)) {\n return -1;\n }\n if (isTextTriple(names.length, sample)) {\n report.warnOnce(\n \"validation-error\",\n WEIGHT_AS_ATTRIBUTE_CODE,\n `the third column holds text in the first rows, so it is read as the attribute ${names[2]}, not the weight`,\n { line, element: names[2] },\n );\n return -1;\n }\n return 2;\n }\n\n /**\n * Warn about header columns that name the source, target or weight beside the chosen one.\n * @param names - the header names\n * @param source - the source column\n * @param target - the target column\n * @param weight - the weight column, or -1\n * @param line - the header line\n */\n private reportEndpointRivals(\n names: readonly string[],\n source: number,\n target: number,\n weight: number,\n line: number,\n ): void {\n const { csv, common } = this.state;\n if (csv.sourceColumn === null) {\n this.reportRivals(names, SOURCE_NAMES, source, \"source\", line);\n }\n if (csv.targetColumn === null) {\n this.reportRivals(names, TARGET_NAMES, target, \"target\", line);\n }\n if (weight >= 0 && common.weightFrom !== null) {\n this.reportRivals(names, [common.weightFrom], weight, \"weight\", line);\n }\n }\n\n /**\n * The edge type column: the typeColumn option, else Gephi's `Type` beside `Source` and `Target`.\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 * @param claimed - the columns other roles took\n * @returns the column index, or -1\n */\n private typeColumn(\n names: readonly string[],\n header: boolean,\n source: number,\n target: number,\n claimed: ReadonlySet<number>,\n ): number {\n const { typeColumn } = this.state.csv;\n if (typeColumn === null) {\n return -1;\n }\n if (typeColumn === undefined) {\n const gephi = header && names[source] === \"Source\" && names[target] === \"Target\";\n const type = gephi ? findFree(names, [TYPE_NAME], claimed) : -1;\n return type >= 0 && names[type] === TYPE_NAME ? type : -1;\n }\n const type = resolveColumnRef(names, 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 return type;\n }\n\n /**\n * Warn about header columns that name a role another column was chosen for (`Source,source`,\n * `from,to,source`, `Weight,weight`): the one not chosen is read as a plain attribute.\n * @param names - the header names\n * @param candidates - the names of the role\n * @param chosen - the column chosen\n * @param role - the role, for the message\n * @param line - the header line\n */\n private reportRivals(\n names: readonly string[],\n candidates: readonly string[],\n chosen: number,\n role: string,\n line: number,\n ): void {\n const lower = new Set(candidates.map((c) => c.toLowerCase()));\n names.forEach((name, i) => {\n if (i !== chosen && lower.has(name.toLowerCase())) {\n this.state.report.warning(\n \"validation-error\",\n AMBIGUOUS_COLUMN_CODE,\n `columns ${JSON.stringify(names[chosen])} and ${JSON.stringify(name)} both name the ${role}; ${JSON.stringify(names[chosen])} is used and ${JSON.stringify(name)} is a plain attribute`,\n { line, element: name },\n );\n }\n });\n }\n\n /**\n * The label column of a node table: the labelColumn option, else a header column named `label`.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param claimed - columns another role took\n * @returns the column index, or -1\n */\n private labelOf(names: readonly string[], header: boolean, claimed: ReadonlySet<number>): number {\n const { labelColumn } = this.state.csv;\n if (labelColumn !== null) {\n return resolveColumnRef(names, labelColumn, \"labelColumn\");\n }\n return header ? findFree(names, LABEL_NAMES, claimed) : -1;\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 * @param optionalLast - whether a row may omit the last cell\n * @returns the plan; the import aborts when no id column resolves\n */\n private nodePlan(names: readonly string[], header: boolean, line: number, optionalLast: boolean): NodePlan {\n const { common } = this.state;\n const claimed = new Set<number>();\n const idColumn = this.idColumnOf(names, header);\n const label = this.labelOf(names, header, new Set(idColumn >= 0 ? [idColumn] : []));\n const id = this.nodeIdColumn(names, idColumn, label, line);\n if (id >= 0) {\n claimed.add(id);\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, optionalLast };\n }\n\n /**\n * The id column of a node table: the idColumn option, else a header column with an id name,\n * else the first column of a headerless table.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @returns the column index, or -1\n */\n private idColumnOf(names: readonly string[], header: boolean): number {\n const { idColumn } = this.state.csv;\n if (idColumn !== null) {\n return resolveColumnRef(names, idColumn, \"idColumn\");\n }\n if (header) {\n return findColumn(names, ID_NAMES);\n }\n return names.length >= 1 ? 0 : -1;\n }\n\n /**\n * The column node ids come from, as nodeIdFrom says: the label, none (row numbers), or the id\n * column. The import aborts when that column is missing.\n * @param names - the column names\n * @param idColumn - the id column, or -1\n * @param label - the label column, or -1\n * @param line - the header line\n * @returns the column index, or -1 for row numbers\n */\n private nodeIdColumn(names: readonly string[], idColumn: number, label: number, line: number): number {\n const { common, report } = this.state;\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n if (common.nodeIdFrom === \"index\") {\n return -1;\n }\n if (common.nodeIdFrom === \"label\") {\n if (label < 0) {\n report.fail(\n NO_ID_COLUMN_CODE,\n `nodeIdFrom is \"label\", but the node table has no column named \"label\" (${shown}); pass labelColumn to name the column that holds the labels`,\n { line },\n { columns: [...names] },\n );\n }\n return label;\n }\n if (idColumn < 0 && !this.rowNumbers) {\n report.fail(\n NO_ID_COLUMN_CODE,\n `no id column in the node table header (${shown})`,\n { line },\n { columns: [...names] },\n );\n }\n return idColumn;\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 (this.state.csv.decimalComma) {\n for (let i = 0; i < row.length; i++) {\n if (DECIMAL_COMMA.test(row[i])) {\n row[i] = row[i].replace(\",\", \".\");\n }\n }\n }\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 neighbor 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 const neighbors = setNeighborCells(row, quoted);\n if (isUnset(row[0], quoted[0])) {\n report.error(\"missing-value\", MISSING_ID_CODE, `blank node cell`, { line });\n counts.skippedNodes++;\n counts.skippedEdges += neighbors;\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 += neighbors;\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 cellQuoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processEdgeRow(plan: EdgePlan, row: string[], cellQuoted: readonly boolean[], line: number): void {\n const { report } = this.state;\n const { counts } = report;\n const quoted = completeRow(plan, row, cellQuoted, report, line);\n if (row.length !== plan.width) {\n this.reportRowWidth(plan, row, line);\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(\"missing-value\", MISSING_ENDPOINT_CODE, `blank ${sourceMissing ? \"source\" : \"target\"} cell`, {\n line,\n });\n counts.skippedEdges++;\n return;\n }\n const kind = this.rowKind(plan, row, line);\n if (kind === null) {\n counts.skippedEdges++;\n return;\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 && this.edgeIds.has(idText)) {\n report.error(\n \"validation-error\",\n DUPLICATE_EDGE_ID_CODE,\n `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.checkPadded(sourceText, quoted[plan.source], line);\n this.checkPadded(targetText, quoted[plan.target], line);\n let edge: number;\n try {\n edge = this.addEdge(sourceText, targetText, kind, plan.weight >= 0 ? row[plan.weight] : null);\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 // only an imported edge holds its id: a skipped row leaves it free\n this.edgeIds.add(idText);\n this.writeRole(this.idHandle, \"edge\", edge, idText, plan.names[plan.id], line);\n }\n this.warnTypeHint(plan, row, line);\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 * Warn, once, when a column that is not the type column holds a direction word.\n * @param plan - the edge plan\n * @param row - the cells\n * @param line - the row's line\n */\n private warnTypeHint(plan: EdgePlan, row: readonly string[], line: number): void {\n if (plan.typeHint < 0 || typeof parseKind(row[plan.typeHint]) !== \"string\") {\n return;\n }\n this.state.report.warnOnce(\n \"unsupported\",\n TYPE_COLUMN_IGNORED_CODE,\n `column ${JSON.stringify(plan.names[plan.typeHint])} holds the direction word ${JSON.stringify(row[plan.typeHint])} but is a plain attribute; the per-row direction is read from an exact \"Type\" column beside \"Source\" and \"Target\", or from the typeColumn option`,\n { line, element: plan.names[plan.typeHint] },\n );\n }\n\n /**\n * Report a row with the wrong number of cells. The first row too long for an auto table says\n * what an adjacency list needs, since one reads this way.\n * @param plan - the edge plan\n * @param row - the cells\n * @param line - the row's line\n */\n private reportRowWidth(plan: EdgePlan, row: readonly string[], line: number): void {\n const hint =\n this.state.csv.table === \"auto\" && row.length > plan.width && !this.adjacencyHinted\n ? '; if each line is a node followed by its neighbors, pass table: \"adjacency\"'\n : \"\";\n this.adjacencyHinted ||= hint !== \"\";\n this.state.report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `${row.length} field${plural(row.length)}, expected ${plan.width}${hint}`,\n { line },\n );\n }\n\n /**\n * The direction of an edge row: its Type cell, else the file's or the default direction.\n * @param plan - the edge plan\n * @param row - the cells\n * @param line - the row's line\n * @returns the kind, or null when the Type cell is not a direction (reported)\n */\n private rowKind(plan: EdgePlan, row: readonly string[], line: number): EdgeKind | null {\n const directed = this.state.commentDirected ?? this.state.common.defaultDirected;\n const kind: EdgeKind = directed ? \"directed\" : \"undirected\";\n if (plan.type < 0) {\n return kind;\n }\n const parsed = parseKind(row[plan.type]);\n if (parsed === null) {\n this.state.report.error(\n \"validation-error\",\n BAD_TYPE_CODE,\n `Type ${JSON.stringify(row[plan.type])} is not Directed, Undirected or Mutual`,\n { line },\n );\n return null;\n }\n return parsed ?? kind;\n }\n\n /**\n * Add an edge row's edge, counting the edge and the nodes it brings in.\n * @param sourceText - the source cell\n * @param targetText - the target cell\n * @param kind - the direction\n * @param weightText - the weight cell, or null\n * @returns the edge's index\n */\n private addEdge(sourceText: string, targetText: string, kind: EdgeKind, weightText: string | null): number {\n const { report, sink, resolver } = this.state;\n const { where } = this;\n const source = this.coerce(sourceText);\n const target = this.coerce(targetText);\n const weight = weightText === null ? undefined : parseWeightText(weightText, report);\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 const edge = resolver.addEdge(source, target, kind, weight, where);\n report.counts.edges += sink.edgeCount - before;\n report.counts.nodes += (sourceNew ? 1 : 0) + (targetNew ? 1 : 0);\n return edge;\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 cellQuoted - whether each cell was quoted\n * @param line - the row's line\n */\n private processNodeRow(plan: NodePlan, row: string[], cellQuoted: readonly boolean[], line: number): void {\n const { report, sink } = this.state;\n const { counts } = report;\n const ordinal = this.nodeOrdinal++;\n const quoted = completeRow(plan, row, cellQuoted, report, line);\n if (row.length !== plan.width) {\n report.error(\n \"validation-error\",\n FIELD_COUNT_CODE,\n `${row.length} field${plural(row.length)}, expected ${plan.width}`,\n {\n line,\n },\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, `blank id cell`, { line });\n counts.skippedNodes++;\n return;\n }\n if (plan.id >= 0) {\n this.checkPadded(idText, quoted[plan.id], line);\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 `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 * Warn once per import about an unquoted id cell with leading or trailing whitespace, which is\n * a different id from the bare text (RFC 4180 keeps spaces).\n * @param text - the cell text\n * @param quoted - whether the cell was quoted\n * @param line - the row's line\n */\n private checkPadded(text: string, quoted: boolean | undefined, line: number): void {\n if (isPadded(text, quoted)) {\n this.state.report.warnOnce(\n \"validation-error\",\n PADDED_ID_CODE,\n `id ${JSON.stringify(text)} has leading or trailing whitespace and is kept as written, a different id from the trimmed text`,\n { line, element: text },\n );\n }\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\n/**\n * Whether a headerless table is a triple list whose third column is text (`a b knows`): every row\n * read so far has exactly three cells and a third cell that is not a number. That column is then an\n * attribute rather than the positional weight. A wider or ragged table keeps the positional weight,\n * so prose or an adjacency list read as edges still fails per row instead of importing.\n * @param width - the number of columns\n * @param sample - the data rows read so far\n * @returns true for a text triple list\n */\nfunction isTextTriple(width: number, sample: readonly (readonly string[])[]): boolean {\n return (\n width === 3 &&\n sample.length > 0 &&\n sample.every((row) => row.length === 3 && !isBlank(row[2]) && numberOrNull(row[2]) === null)\n );\n}\n\n/**\n * Whether the caller fixed how the input is read (a delimiter, the header, the table kind or a\n * column): it is then a table, and its first line is never refused as another format.\n * @param csv - the resolved options\n * @returns true when any of them is explicit\n */\nfunction hasExplicitDialect(csv: ResolvedCsvOptions): boolean {\n return (\n csv.delimiter !== null ||\n csv.header !== \"auto\" ||\n csv.table !== \"auto\" ||\n csv.sourceColumn !== null ||\n csv.targetColumn !== null ||\n csv.idColumn !== null ||\n (csv.typeColumn !== undefined && csv.typeColumn !== null)\n );\n}\n\n/**\n * Fit a row whose width differs from the header's by one empty last cell: a row that omits the\n * empty last cell of a header ending in a delimiter gets an unset cell appended, and a row ending\n * in a delimiter the header does not have (Excel, pandas) loses that empty cell, reported once.\n * @param plan - the plan\n * @param row - the cells (changed in place)\n * @param quoted - whether each cell was quoted\n * @param report - the report\n * @param line - the row's line\n * @returns the quoted flags of the fitted row\n */\nfunction completeRow(\n plan: EdgePlan | NodePlan,\n row: string[],\n quoted: readonly boolean[],\n report: ImportReportBuilder,\n line: number,\n): readonly boolean[] {\n if (row.length === plan.width + 1 && isUnset(row[plan.width], quoted[plan.width])) {\n row.pop();\n report.warnOnce(\n \"validation-error\",\n TRAILING_DELIMITER_CODE,\n `the row ends in a delimiter the header does not have; the empty last cell is dropped`,\n { line },\n );\n return quoted;\n }\n if (!plan.optionalLast || row.length !== plan.width - 1) {\n return quoted;\n }\n const flags = quoted.slice(0, row.length);\n row.push(\"\");\n flags.push(false);\n return flags;\n}\n\nconst HEAD_BYTES = 4096;\n\n/**\n * The openings of the formats a CSV reader would otherwise split into rows of text: an XML / HTML\n * tag or declaration (not an IRI such as `<http://a>`, nor a bracketed id such as `<alice smith>`\n * or `<a>,b`: a tag needs an attribute, or another tag after it), a JSON object (`{\"` or `{}`) or\n * array, a DOT graph, a Pajek section, GML's `Creator \"...\"` or `graph [`. Matched against the\n * start of the input.\n */\n/**\n * An endpoint column: the one the option names, else (with a header) the first with a known\n * endpoint name, else (without one) the column at its position in a headerless edge list.\n * @param names - the column names\n * @param header - whether the file has a header row\n * @param option - the sourceColumn / targetColumn option, or null\n * @param optionName - the option's name, for its error\n * @param known - the endpoint names to look for\n * @param position - the column of this endpoint in a headerless edge list\n * @returns the column index, or -1\n */\nfunction endpointColumn(\n names: readonly string[],\n header: boolean,\n option: CsvColumnRef | null,\n optionName: string,\n known: readonly string[],\n position: number,\n): number {\n if (option !== null) {\n return resolveColumnRef(names, option, optionName);\n }\n if (header) {\n return findColumn(names, known);\n }\n return names.length >= 2 ? position : -1;\n}\n\nconst TAG_NAME = String.raw`<[a-z_][\\w.-]*(?::[a-z_][\\w.-]*)?`;\nconst OTHER_FORMATS: readonly RegExp[] = [\n /^\\s*<(?:\\?xml|!doctype|!--)/i, // an XML declaration, a doctype or a comment\n new RegExp(String.raw`^\\s*${TAG_NAME}\\s*\\/?>\\s*(?:[^,;|\\s]*<|$)`, \"i\"), // a tag, then another tag\n new RegExp(String.raw`^\\s*${TAG_NAME}\\s+[a-z_][\\w.:-]*\\s*=`, \"i\"), // a tag with an attribute\n /^\\s*\\/[*/]/, // a C comment (DOT)\n /^\\s*\\{\\s*(?:[\"}]|$)/, // a JSON object\n /^\\s*\\[\\s*(?:[[{\"\\]\\d-]|$)/, // a JSON array\n /^\\s*(?:strict\\s+)?(?:di)?graph(?:\\s+\\S+)?\\s*\\{/i, // a DOT graph\n /^\\s*\\*(?:vertices|network|arcs|edges)\\b/i, // a Pajek section\n /^\\s*(?:creator\\s+\"|graph\\s*\\[)/i, // GML\n];\n\n/**\n * Sniff confidence for the registry: 0 for XML, JSON, GML, DOT (also a leading C comment) and Pajek\n * openings after the leading `#` / `%` comment lines the reader skips, and 0 for a head that is\n * only such comments (the comments of another format, too long to see past), and 0 for text\n * holding a C0 control character (hasControlCharacter); otherwise a\n * delimited first row with endpoint headers is 0.9 (even when a later row has the wrong width, which\n * the importer reports), with an id header 0.6, and other consistently delimited rows 0.3, except\n * rows of more than three space-separated words, which is plain text such as a sentence, not a\n * headerless edge list (source, target, weight), so it is 0.\n * @param head - the first bytes of the input\n * @returns a confidence in 0..1\n */\nfunction sniff(head: Uint8Array): number {\n let label = \"utf-8\";\n if (head[0] === 0xff && head[1] === 0xfe) {\n label = \"utf-16le\";\n } else if (head[0] === 0xfe && head[1] === 0xff) {\n label = \"utf-16be\";\n }\n // the decoder drops the BOM it was chosen by; the reader skips leading comment lines\n const body = stripLeadingComments(new TextDecoder(label).decode(head.subarray(0, HEAD_BYTES)), COMMENT_CHARS);\n if (body.trim().length === 0 || hasControlCharacter(body) || OTHER_FORMATS.some((r) => r.test(body))) {\n return 0;\n }\n const newline = sniffNewline(body);\n const delimiter = sniffDelimiter(body, newline, DELIMITER_CANDIDATES, '\"', false, true);\n if (delimiter === null) {\n // a header naming both endpoints is evidence enough; a short or long row later is the\n // importer's to report, not a reason to refuse the file\n const first = body.split(newline, 1)[0];\n return DELIMITER_CANDIDATES.some((d) => hasEndpointHeader(first, d)) ? 0.9 : 0;\n }\n // the first record, honoring quotes (R and pandas quote every header name)\n const records = splitRecords(body, delimiter, '\"', 2, false, true) ?? [];\n const names = headerNames(records[0] ?? []);\n if (findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0) {\n return 0.9;\n }\n // a space-split row naming an id is as likely a line of text (\"1 First node\") as a header\n if (delimiter !== \" \" && findColumn(names, ID_NAMES) >= 0) {\n return 0.6;\n }\n if (delimiter === \" \" && names.length > 3) {\n return 0;\n }\n return looksLikeProse(body, newline, delimiter) ? 0 : 0.3;\n}\n\n/**\n * Whether text holds a C0 control character other than TAB, LF and CR (a final Ctrl-Z, the DOS\n * end-of-file marker, aside): such text is binary data or another format, never a CSV a reader\n * means to load, so it is only read as CSV when the caller names the format.\n * @param text - the decoded head\n * @returns whether it holds one\n */\nfunction hasControlCharacter(text: string): boolean {\n const last = text.length - 1;\n for (let i = 0; i <= last; i++) {\n const c = text.codePointAt(i) ?? 0;\n if (c < 0x20 && c !== 0x09 && c !== 0x0a && c !== 0x0d && !(c === 0x1a && i === last)) {\n return true;\n }\n }\n return false;\n}\n\n/** A cell that reads as part of a sentence: words with a sentence end after a letter (`Monday.`, `world. Thanks`). */\nconst SENTENCE = /[A-Za-z]{2}[.!?](?:\\s|$)/;\n\n/**\n * Whether headerless delimited text is more likely a line of prose than an edge list: a single line\n * (\"hello world\", \"Dear team, the meeting is on Monday.\"), or a cell of more than one word that\n * ends a sentence. An edge list a reader means to load has more than one row and ids, not sentences;\n * such input is still read as CSV when the caller names the format.\n * @param body - the head, decoded, without its leading comments\n * @param newline - the head's newline\n * @param delimiter - the sniffed delimiter\n * @returns true for prose\n */\nfunction looksLikeProse(body: string, newline: string, delimiter: string): boolean {\n const lines = body.split(newline).filter((l) => l.trim() !== \"\");\n if (lines.length < 2) {\n return true;\n }\n const rows = splitRecords(body, delimiter, '\"', 8, false, true) ?? [];\n return rows.some((row) => row.some((cell) => /\\S\\s+\\S/.test(cell.trim()) && SENTENCE.test(cell.trim())));\n}\n\n/**\n * Whether one line, split on a delimiter, names a source and a target column.\n * @param line - the first line of the input\n * @param delimiter - the delimiter to split on\n * @returns true for an edge table header\n */\nfunction hasEndpointHeader(line: string, delimiter: string): boolean {\n const names = headerNames(splitRecords(line, delimiter, '\"', 1, false, true)?.[0] ?? []);\n return names.length > 1 && findColumn(names, SOURCE_NAMES) >= 0 && findColumn(names, TARGET_NAMES) >= 0;\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 commentText: \"\",\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${plural(state.coercer.mergeCount)} merged into ids other cells already produced under ids: \"number\"`,\n );\n }\n throwIfAborted(common.signal);\n return report.finish();\n}\n\n/**\n * The CSV / TSV importer plugin (subpath `@graphty/graph-io/csv`).\n * @category Built-in formats\n */\nexport const csvImporter: GraphImporter<CsvImportOptions> = Object.freeze({\n format: \"csv\",\n options: Object.freeze([\n \"decimal\",\n \"delimiter\",\n \"header\",\n \"idColumn\",\n \"labelColumn\",\n \"nodes\",\n \"rowNumberIds\",\n \"sourceColumn\",\n \"table\",\n \"targetColumn\",\n \"typeColumn\",\n ]),\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/**\n * The `nodes` option as an input: a Blob or File is read through its stream. Checked by shape, so a\n * Blob from another realm or from Node's `fs.openAsBlob()` counts too.\n * @param value - the option value\n * @returns the input, or null when there is no node table\n */\nfunction nodesInput(value: ImportInput | Blob | undefined): ImportInput | null {\n if (value === undefined) {\n return null;\n }\n const object = value as { stream?: unknown; size?: unknown };\n const blob = typeof value === \"object\" && typeof object.stream === \"function\" && typeof object.size === \"number\";\n return blob ? (value as Blob).stream() : (value as ImportInput);\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 * neighbors 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 neighbor 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 {\n type Column,\n type F32Column,\n type F64Column,\n GraphFormatError,\n type GraphSnapshot,\n type NodeId,\n} 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 { otherFormatDialect, type ResolvedExportOptions, resolveExportOptions } from \"../../common/options.js\";\nimport { agree, plural } from \"../../common/plural.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/**\n * The format-specific options of the CSV exporter.\n * @category Built-in formats\n */\nexport interface CsvExportOptions extends CommonExportOptions {\n /**\n * The header spelling: \"gephi\" writes `Source,Target,Type,...,Weight`, with each edge's\n * direction in `Type`; \"generic\" writes `source,target,...,weight` and no direction column. A JSON\n * dialect name, from an options object shared with JSON saves, is ignored.\n * @defaultValue \"gephi\"\n */\n dialect?: \"gephi\" | \"generic\" | undefined;\n /**\n * Which table to write. \"edges\": one edge per row, with the edge attributes; isolated nodes and\n * node attributes are not in it. \"nodes\": one node per row, with the node attributes and no\n * edges. \"adjacency\": a node and its neighbors per row, which keeps every node and the node\n * order but no attributes (read it back with `table: \"adjacency\"`). To keep both nodes and\n * edges, write the node table and the edge table to two files and read them back with the\n * `nodes` import option.\n * @defaultValue \"edges\"\n */\n table?: \"edges\" | \"nodes\" | \"adjacency\" | undefined;\n /**\n * The field delimiter.\n * @defaultValue \",\"\n */\n delimiter?: string | undefined;\n /**\n * The line terminator.\n * @defaultValue \"\\n\"\n */\n newline?: \"\\n\" | \"\\r\\n\" | undefined;\n /**\n * Whether to write the header row (an adjacency table never has one). A file without a header is\n * read back by position: source, target, weight, then unnamed columns. checkExport() returns\n * W_CSV_HEADERLESS when the table has other columns; for a plain headerless edge list, pass\n * `dialect: \"generic\"` and write a graph whose edges have only weights.\n * @defaultValue true\n */\n header?: boolean | undefined;\n}\n\n/**\n * The CSV loss-note codes of check(); the shared ones are LOSS's.\n * @category Built-in formats\n */\nexport const CSV_LOSS = Object.freeze({\n /**\n * Two node ids would be written as the same text (the number 5 and the text \"5\"); the save fails with\n * E_INVALID_ID.\n */\n ID_TEXT_COLLISION: LOSS.ID_TEXT_COLLISION,\n /** Ids that read back as a different type, such as the text \"7\" as the number 7. */\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 /**\n * An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and\n * reads back with that role.\n */\n ROLE_ASSUMED: LOSS.ROLE_ASSUMED,\n /** A role column (id, label) whose name the importer does not recognize; the role is lost. */\n ROLE_NAME: \"W_CSV_ROLE_NAME\",\n /** An id or label attribute that is not text reads back as text. */\n TEXT_ROLE: \"W_CSV_TEXT_ROLE\",\n /** NaN / Infinity in a numeric column read back as text. */\n NONFINITE: \"W_CSV_NONFINITE\",\n /**\n * A text value that reads back as a number or a boolean, because the format does not record that it was text (the\n * text \"42\" reads back as the number 42).\n */\n TEXT_INFERRED: LOSS.TEXT_INFERRED,\n /**\n * A text attribute reads back as a dictionary attribute, or the reverse, because the importer chooses by how often\n * its values repeat. The values are the same.\n */\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 /**\n * The edge table has no row for a node without edges, so isolated nodes are missing when the file is read back.\n * Write the node table too (`table: \"nodes\"`).\n */\n ISOLATED_NODES: \"W_CSV_ISOLATED_NODES\",\n /**\n * The edge table lists nodes in the order they first appear in an edge, so the node order changes when the file is\n * read back.\n */\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 * `header: false` with columns a file without a header cannot name: graph-io reads such a file by position\n * (source, target, weight, then columns it names column4, column5, ...), so these columns read back under other\n * names or in the wrong place (for example, an edge id in the weight's place). Write the header, or write only source,\n * target and weight with `dialect: \"generic\"`.\n */\n HEADERLESS: \"W_CSV_HEADERLESS\",\n});\n\n/**\n * What the CSV format keeps as declared.\n * @category Built-in formats\n */\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 (\n o.dialect !== undefined &&\n o.dialect !== \"gephi\" &&\n o.dialect !== \"generic\" &&\n !otherFormatDialect(\"csv\", o.dialect)\n ) {\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 // another format's dialect (a JSON \"d3\" in a shared options object) leaves CSV's default\n dialect: DIALECTS[o.dialect === \"generic\" ? \"generic\" : \"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 * W_CSV_NODE_TABLE: the node columns an edge-table or adjacency export leaves for a second, node-table export.\n * @param names - the node columns, label first\n * @param table - the table being written (\"edges\" or \"adjacency\")\n * @param notes - receives the note\n */\nfunction nodeTableNote(names: readonly string[], table: string, notes: LossNote[]): void {\n const written = names.length;\n if (written > 0) {\n const shown =\n names\n .slice(0, 3)\n .map((n) => JSON.stringify(n))\n .join(\", \") + (written > 3 ? \", ...\" : \"\");\n notes.push(\n Object.freeze({\n code: CSV_LOSS.NODE_TABLE,\n message: `the ${table === \"adjacency\" ? \"adjacency\" : \"edge\"} table has no room for node attributes: ${written} node column${plural(written)} (${shown}) ${agree(written, \"is\", \"are\")} written only by a second export with table: \"nodes\"`,\n column: null,\n count: written,\n columns: Object.freeze([...names]),\n }),\n );\n }\n}\n\n/**\n * The number of node ids whose text an earlier id already has (a number and a string).\n * @param ids - the snapshot's ids\n * @returns the collisions\n */\nfunction idTextCollisions(ids: GraphSnapshot[\"ids\"]): number {\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 return collisions;\n}\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 collisions = idTextCollisions(ids);\n if (collisions > 0) {\n note(\n CSV_LOSS.ID_TEXT_COLLISION,\n `${collisions} node id${plural(collisions)} ${agree(collisions, \"shares\", \"share\")} their text with another id (a number and a string); the save fails with 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${plural(changed)} ${agree(changed, \"reads\", \"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\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 */\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 directionNotes(snapshot, csv, edgeRows.length, note);\n if (folding.mutualCount > 0 && csv.table !== \"nodes\") {\n note(\n CSV_LOSS.MUTUAL_EXPANDED,\n `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, \"is\", \"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 const { edgeId, edgeLabel, edgeColumns } = planEdgeColumns(snapshot, csv, edgeRows, values, note);\n const { nodeLabel, nodeColumns } = planNodeColumns(snapshot, csv, values, note);\n if (csv.table !== \"nodes\") {\n nodeTableNote(\n [...(nodeLabel === null ? [] : [nodeLabel.meta.name]), ...nodeColumns.map((c) => c.column.meta.name)],\n csv.table,\n notes,\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/** Records one loss note. */\ntype NoteFn = (code: string, message: string, column?: string | null, count?: number | null) => void;\n\n/**\n * W_CSV_DIRECTION_DROPPED: a table with no direction column (the generic dialect, an adjacency\n * table) loses the direction of undirected edges, and the Gephi edge table of an undirected graph\n * without edges has no Type cell to carry it. A node table holds no edge, so it gets no note.\n * @param snapshot - the snapshot\n * @param csv - the resolved CSV options\n * @param edgeRowCount - how many edge rows are written\n * @param note - the recorder\n */\nfunction directionNotes(\n snapshot: GraphSnapshot,\n csv: ResolvedCsvExportOptions,\n edgeRowCount: number,\n note: NoteFn,\n): void {\n if (csv.table !== \"nodes\" && (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${plural(snapshot.edgeCount)} ${agree(snapshot.edgeCount, \"reads\", \"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${plural(mixed)} 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 && edgeRowCount === 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}\n\n/**\n * The edge table's columns, with their notes: the attribute columns' names (edge table only), the\n * columns an adjacency table drops, and the values of every column the edge table writes.\n * @param snapshot - the snapshot\n * @param csv - the resolved CSV options\n * @param edgeRows - the edge rows written\n * @param values - whether the values are inspected\n * @param note - the recorder\n * @returns the id and label role columns and the attribute columns\n */\nfunction planEdgeColumns(\n snapshot: GraphSnapshot,\n csv: ResolvedCsvExportOptions,\n edgeRows: readonly number[],\n values: boolean,\n note: NoteFn,\n): { edgeId: Column | null; edgeLabel: Column | null; edgeColumns: ColumnOut[] } {\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${plural(dropped)} ${agree(dropped, \"is\", \"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 return { edgeId, edgeLabel, edgeColumns };\n}\n\n/**\n * The node table's columns, with their notes when the node table is the one written.\n * @param snapshot - the snapshot\n * @param csv - the resolved CSV options\n * @param values - whether the values are inspected\n * @param note - the recorder\n * @returns the label role column and the attribute columns\n */\nfunction planNodeColumns(\n snapshot: GraphSnapshot,\n csv: ResolvedCsvExportOptions,\n values: boolean,\n note: NoteFn,\n): { nodeLabel: Column | null; nodeColumns: ColumnOut[] } {\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\" && 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 return { nodeLabel, nodeColumns };\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${plural(isolated)} ${agree(isolated, \"has\", \"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${plural(reordered)} ${agree(reordered, \"is\", \"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 set = column.length - column.nullCount;\n if (isReservedName(name, domain, reserved, idColumn, set, note)) {\n continue;\n }\n if (findColumn([name], LABEL_NAMES) >= 0 && !labelNameWritten(name, domain, labelColumn, set, note)) {\n continue;\n }\n out.push({ column, header: name });\n }\n return out;\n}\n\n/**\n * Whether an attribute column's name is reserved for a structural column (a dialect header, an\n * edge id name, the id column's name), with its note.\n * @param name - the column name\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 set - the column's set cells\n * @param note - the recorder, or null when this table's notes are not wanted\n * @returns true when the column is not written\n */\nfunction isReservedName(\n name: string,\n domain: \"node\" | \"edge\",\n reserved: ReadonlySet<string>,\n idColumn: Column | null,\n set: number,\n note: ((code: string, message: string, column?: string | null, count?: number | null) => void) | null,\n): boolean {\n const low = lower(name);\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 return true;\n }\n return false;\n}\n\n/**\n * Whether an attribute column with a label header name is written, with its note: not when the\n * table has a label column (the name is reserved for it), else it reads back with the label role.\n * @param name - the column name\n * @param domain - node or edge, for messages\n * @param labelColumn - the table's label role column, or null\n * @param set - the column's set cells\n * @param note - the recorder, or null when this table's notes are not wanted\n * @returns true when the column is written\n */\nfunction labelNameWritten(\n name: string,\n domain: \"node\" | \"edge\",\n labelColumn: Column | null,\n set: number,\n note: ((code: string, message: string, column?: string | null, count?: number | null) => void) | null,\n): boolean {\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 return false;\n }\n note?.(CSV_LOSS.ROLE_ASSUMED, `${domain} column \"${name}\" reads back with the label role (string)`, name, set);\n return true;\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: NoteFn,\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 const nonFinite = countNonFinite(column, rows);\n if (nonFinite > 0) {\n note(\n CSV_LOSS.NONFINITE,\n `${label}: ${nonFinite} non-finite value${plural(nonFinite)} ${agree(nonFinite, \"reads\", \"read\")} back as text`,\n name,\n nonFinite,\n );\n }\n return;\n }\n if (dtype !== \"string\" && dtype !== \"dict\") {\n return;\n }\n const scan = scanText(column, rows);\n if (roleColumn || scan.widest < 0) {\n return;\n }\n const readsAsDict = scan.candidate && scan.heuristic.decide() === \"dict\";\n if (scan.widest < WIDENING.string && !readsAsDict) {\n note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[scan.widest]}`, name, scan.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 rows of a float column holding at least one non-finite component.\n * @param column - the f32 or f64 column\n * @param rows - the rows written\n * @returns how many rows hold one\n */\nfunction countNonFinite(column: F32Column | F64Column, rows: readonly number[]): number {\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 return nonFinite;\n}\n\n/** What a text column's written values read back as. */\ninterface TextScan {\n /** The widest dtype the values infer to (WIDENING), -1 when no value is set. */\n readonly widest: number;\n /** Whether every value the dict heuristic sampled infers to string. */\n readonly candidate: boolean;\n /** How many rows hold a string. */\n readonly set: number;\n /** The dict heuristic over the sampled values. */\n readonly heuristic: DictHeuristic;\n}\n\n/**\n * Infer the dtype every written value of a text column reads back as, and sample the values for\n * the dict heuristic.\n * @param column - the string or dict column\n * @param rows - the rows written\n * @returns the scan\n */\nfunction scanText(column: Column, rows: readonly number[]): TextScan {\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 const text = column.isSet(row) ? column.value(row) : null;\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 candidate &&= kind === \"string\";\n heuristic.observe(text);\n }\n }\n return { widest, candidate, set, heuristic };\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 neighbor 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 neighbor'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 end of the run of consecutive edge rows that share the source of row `r`.\n * @param src - the edge sources by edge index\n * @param edgeRows - the edges in write order\n * @param r - the run's first row\n * @returns the index after the run's last row\n */\nfunction sourceRunEnd(src: ArrayLike<number>, edgeRows: ArrayLike<number>, r: number): number {\n const source = src[edgeRows[r]];\n let end = r;\n while (end < edgeRows.length && src[edgeRows[end]] === source) {\n end++;\n }\n return end;\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.replaceAll('\"', '\"\"')}\"`;\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 const end = sourceRunEnd(list.src, edgeRows, r);\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 if (plan.csv.table === \"adjacency\") {\n yield* adjacencyLines(snapshot, plan);\n } else if (plan.csv.table === \"nodes\") {\n yield* nodeLines(snapshot, plan);\n } else {\n yield* edgeLines(snapshot, plan);\n }\n}\n\n/**\n * The cell writers of a delimiter.\n * @param delimiter - the field delimiter\n * @returns `quote` for a text and `cell` for a column value (an unset value is the empty cell)\n */\nfunction cellWriters(delimiter: string): {\n quote: (text: string) => string;\n cell: (column: Column, row: number) => string;\n} {\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 return { quote, cell };\n}\n\n/**\n * The lines of the node table: the id, the label and the attribute columns of every node.\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @yields one line at a time\n * @returns nothing\n */\nfunction* nodeLines(snapshot: GraphSnapshot, plan: Plan): Generator<string, void, undefined> {\n const { csv } = plan;\n const { delimiter, newline } = csv;\n const { quote, cell } = cellWriters(delimiter);\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}\n\n/**\n * The lines of the edge table: one row per written edge.\n * @param snapshot - the snapshot\n * @param plan - the plan\n * @yields one line at a time\n * @returns nothing\n */\nfunction* edgeLines(snapshot: GraphSnapshot, plan: Plan): Generator<string, void, undefined> {\n const { csv } = plan;\n const { delimiter, newline } = csv;\n const { quote, cell } = cellWriters(delimiter);\n if (csv.header) {\n yield edgeHeaders(plan).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 * The header of the edge table.\n * @param plan - the plan\n * @returns the header names, unquoted\n */\nfunction edgeHeaders(plan: Plan): string[] {\n const { dialect } = plan.csv;\n const headers = [dialect.source, dialect.target];\n if (dialect.type !== null) {\n headers.push(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(dialect.weight);\n }\n for (const { header } of plan.edgeColumns) {\n headers.push(header);\n }\n return headers;\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 * The note of a table written without its header: a headerless file is read by position (source, target, weight,\n * then unnamed columns; a node table: id, then unnamed columns), so any other column reads back elsewhere or unnamed.\n * @param plan - the export plan\n * @returns the note, or nothing when the header is written or the positions match\n */\nfunction headerlessNotes(plan: Plan): LossNote[] {\n const { csv } = plan;\n if (csv.header || csv.table === \"adjacency\") {\n return [];\n }\n const names = csv.table === \"nodes\" ? headerlessNodeColumns(plan) : headerlessEdgeColumns(plan);\n if (names.length === 0) {\n return [];\n }\n const shown = names.map((n) => JSON.stringify(n)).join(\", \");\n return [\n Object.freeze({\n code: CSV_LOSS.HEADERLESS,\n message: `without a header the file is read back by position (${csv.table === \"nodes\" ? \"id\" : \"source, target, weight\"}, then unnamed columns), so ${names.length === 1 ? \"the column\" : \"the columns\"} ${shown} ${agree(names.length, \"reads\", \"read\")} back unnamed or in another column's place; write the header, or for an edge list pass dialect: \"generic\" and write only source, target and weight`,\n column: null,\n count: names.length,\n columns: Object.freeze([...names]),\n }),\n ];\n}\n\n/**\n * The node columns a headerless node table does not read back by position: every one after the id.\n * @param plan - the export plan\n * @returns their names\n */\nfunction headerlessNodeColumns(plan: Plan): string[] {\n const label = plan.nodeLabel === null ? [] : [plan.nodeLabel.meta.name];\n return [...label, ...plan.nodeColumns.map((c) => c.header)];\n}\n\n/**\n * The edge columns a headerless edge list does not read back by position: every one after the\n * endpoints and the weight, and the weight when another column comes before it.\n * @param plan - the export plan\n * @returns their names\n */\nfunction headerlessEdgeColumns(plan: Plan): string[] {\n const { dialect } = plan.csv;\n const names: string[] = [];\n if (dialect.type !== null) {\n names.push(dialect.type);\n }\n if (plan.edgeId !== null) {\n names.push(plan.edgeId.meta.name);\n }\n if (plan.edgeLabel !== null) {\n names.push(plan.edgeLabel.meta.name);\n }\n // a weight right after the target is where a headerless read expects it\n if (plan.weights.weighted && names.length > 0) {\n names.push(dialect.weight);\n }\n names.push(...plan.edgeColumns.map((c) => c.header));\n return names;\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, {\n roles: KEPT_ROLES,\n ...(plan.csv.table === \"nodes\" ? { tables: [\"node\"] as const } : {}),\n }),\n ...plan.notes,\n ...headerlessNotes(plan),\n ]);\n}\n\n/**\n * The CSV / TSV exporter plugin (subpath `@graphty/graph-io/csv`).\n * @category Built-in formats\n */\nexport const csvExporter: GraphExporter<CsvExportOptions> = Object.freeze({\n format: \"csv\",\n options: Object.freeze([\"delimiter\", \"dialect\", \"header\", \"newline\", \"table\"]),\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 * @module @graphty/graph-io/csv\n */\n\nimport {\n COLUMN_RENAMED_CODE,\n DIRECTION_FORCED_CODE,\n DIRECTION_REFUSED_CODE,\n ELEMENT_ISSUE,\n ENCODING_FALLBACK_CODE,\n INPUT_ISSUE,\n INVALID_ENCODING_CODE,\n INVALID_UTF8_CODE,\n MIXED_DIRECTION_CODE,\n OPTION_IGNORED_CODE,\n PRECISION_CODE,\n SINK_OPTION_CODE,\n UNKNOWN_ENCODING_CODE,\n} from \"../../common/codes.js\";\nimport { WIDENING_UNSUPPORTED_CODE } from \"../../common/text.js\";\nimport {\n AMBIGUOUS_COLUMN_CODE,\n BAD_TYPE_CODE,\n COLUMN_MISSING_CODE,\n COMMENT_DIRECTION_CODE,\n COMMENT_LIKE_RECORD_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 OTHER_FORMAT_CODE,\n PADDED_ID_CODE,\n ROLE_TAKEN_CODE,\n SINGLE_COLUMN_CODE,\n STRAY_QUOTE_CODE,\n TRAILING_DELIMITER_CODE,\n TRAILING_HEADER_DELIMITER_CODE,\n TYPE_COLUMN_IGNORED_CODE,\n WEIGHT_AS_ATTRIBUTE_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, by name: the codes shared with\n * the other importers and the CSV-specific ones. A key is the code without\n * its severity and format prefixes.\n * @category Built-in formats\n */\nexport const CSV_ISSUE = Object.freeze({\n ...INPUT_ISSUE,\n ...ELEMENT_ISSUE,\n /** An integer beyond 2^53 was stored as the nearest 64-bit float; reported once per column. */\n PRECISION: PRECISION_CODE,\n /** The input is empty. The import stops. */\n EMPTY_INPUT: EMPTY_INPUT_CODE,\n /** The input is not valid UTF-8. The import stops. */\n INVALID_UTF8: INVALID_UTF8_CODE,\n /**\n * Some bytes are not valid in the encoding that was chosen (by a byte order mark, the file's declaration or the\n * `encoding` option). The import stops.\n */\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 /**\n * The header names no source and target columns and no id column. Pass `sourceColumn` and `targetColumn` to name\n * the columns. The import stops.\n */\n NO_ENDPOINT_COLUMNS: NO_ENDPOINT_COLUMNS_CODE,\n /**\n * The node table has no id column. Pass `idColumn` to name it, or `rowNumberIds: true` to number the rows. The\n * import stops.\n */\n NO_ID_COLUMN: NO_ID_COLUMN_CODE,\n /**\n * A row with a different number of fields than the header; the row is skipped. An adjacency list read without\n * `table: \"adjacency\"` gives one per long row, and the first one's message suggests the option.\n */\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 /** A quoted field is never closed. The import stops. */\n UNCLOSED_QUOTE: UNCLOSED_QUOTE_CODE,\n /**\n * There is text after a closing quote, such as `\"a\"b`. Inside a quoted field, write a quote as two quotes. The\n * import stops.\n */\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 different id cells became the same number because `ids` is \"number\", so their nodes were merged. */\n ID_MERGED: ID_MERGED_CODE,\n /** An explicitly named weight column the file does not have. */\n COLUMN_MISSING: COLUMN_MISSING_CODE,\n /**\n * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as\n * a plain attribute.\n */\n ROLE_TAKEN: ROLE_TAKEN_CODE,\n /**\n * An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example a\n * repeated column header.\n */\n COLUMN_RENAMED: COLUMN_RENAMED_CODE,\n /** You set an option this format does not use; it had no effect. The message names the option. */\n OPTION_IGNORED: OPTION_IGNORED_CODE,\n /**\n * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,\n * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.\n */\n SINK_OPTION: SINK_OPTION_CODE,\n /**\n * You read into a graph builder whose direction is already set, or which already holds edges, so the file is read\n * with the builder's direction instead of its own.\n */\n DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,\n /** Edges of the other direction were read with the direction `onMixedDirection` chose. */\n DIRECTION_FORCED: DIRECTION_FORCED_CODE,\n /**\n * The graph has both directed and undirected edges and `onMixedDirection` is \"error\". An import stops; a save to a\n * format that holds one direction per file fails with E_DIRECTED. Pass \"directed\" or \"undirected\" to read or write\n * it anyway.\n */\n MIXED_DIRECTION: MIXED_DIRECTION_CODE,\n /**\n * Your graph builder cannot change an attribute's type after its first value, so a text column keeps the type of\n * its first values.\n */\n WIDENING_UNSUPPORTED: WIDENING_UNSUPPORTED_CODE,\n /** The input starts like another format (XML or HTML, JSON, GML, DOT or Pajek), not CSV. The import stops. */\n OTHER_FORMAT: OTHER_FORMAT_CODE,\n /**\n * A quote inside a field that does not start with a quote is kept as part of the text. Reported once per import.\n */\n STRAY_QUOTE: STRAY_QUOTE_CODE,\n /** A leading comment's direction disagrees with defaultDirected or an earlier comment. */\n COMMENT_DIRECTION: COMMENT_DIRECTION_CODE,\n /**\n * A column named like `Type` holds direction words (Directed, Undirected), but graph-io reads direction only from\n * a `Type` column beside `Source` and `Target`, or from the `typeColumn` option. Reported once per import.\n */\n TYPE_COLUMN_IGNORED: TYPE_COLUMN_IGNORED_CODE,\n /** The header ends in a delimiter; rows without the empty last cell are complete. */\n TRAILING_HEADER_DELIMITER: TRAILING_HEADER_DELIMITER_CODE,\n /** Every row is one cell another delimiter would split (a likely wrong delimiter option). */\n SINGLE_COLUMN: SINGLE_COLUMN_CODE,\n /** Several header columns name one role; the one not chosen is a plain attribute. */\n AMBIGUOUS_COLUMN: AMBIGUOUS_COLUMN_CODE,\n /**\n * An id that is not quoted has spaces at its start or end; they are kept, so it is a different id from the trimmed\n * text. Reported once per import.\n */\n PADDED_ID: PADDED_ID_CODE,\n /** A leading # / % line skipped as a comment has the fields of a record. */\n COMMENT_LIKE_RECORD: COMMENT_LIKE_RECORD_CODE,\n /** Every data row ends in one extra delimiter; the empty last cell is dropped. Reported once per import. */\n TRAILING_DELIMITER: TRAILING_DELIMITER_CODE,\n /**\n * In a file without a header, the third column holds text, so it is read as an attribute, not as the edge weight.\n * Reported once per import.\n */\n WEIGHT_AS_ATTRIBUTE: WEIGHT_AS_ATTRIBUTE_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","type","lines"],"mappings":"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"}
|