@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.
Files changed (210) hide show
  1. package/README.md +2 -0
  2. package/dist/chunks/{children-CL3Cy0ez.js → children-CA4coBi0.js} +36 -27
  3. package/dist/chunks/children-CA4coBi0.js.map +1 -0
  4. package/dist/chunks/{choose-CS3fvGT1.js → choose-DvpbdRuz.js} +2 -2
  5. package/dist/chunks/{choose-CS3fvGT1.js.map → choose-DvpbdRuz.js.map} +1 -1
  6. package/dist/chunks/{constants-DqKZT6cH.js → constants-fT3E8JYY.js} +2 -2
  7. package/dist/chunks/{constants-DqKZT6cH.js.map → constants-fT3E8JYY.js.map} +1 -1
  8. package/dist/chunks/{escape-BJRLd3vJ.js → escape-B62ZGM_a.js} +2 -2
  9. package/dist/chunks/{escape-BJRLd3vJ.js.map → escape-B62ZGM_a.js.map} +1 -1
  10. package/dist/chunks/{export-B-aVCwyG.js → export-rKLeGQMo.js} +404 -243
  11. package/dist/chunks/export-rKLeGQMo.js.map +1 -0
  12. package/dist/chunks/{exporter-6ocL_9x_.js → exporter-8hLNemFk.js} +4 -4
  13. package/dist/chunks/{exporter-6ocL_9x_.js.map → exporter-8hLNemFk.js.map} +1 -1
  14. package/dist/chunks/{exporter-BVXnV9-9.js → exporter-BWwEuOPT.js} +29 -17
  15. package/dist/chunks/exporter-BWwEuOPT.js.map +1 -0
  16. package/dist/chunks/{exporter-Bcp-l4Bo.js → exporter-CpRyHZ3H.js} +574 -475
  17. package/dist/chunks/exporter-CpRyHZ3H.js.map +1 -0
  18. package/dist/chunks/{exporter-SFYkDzD0.js → exporter-yLxKBvLE.js} +23 -20
  19. package/dist/chunks/{exporter-SFYkDzD0.js.map → exporter-yLxKBvLE.js.map} +1 -1
  20. package/dist/chunks/{importer-DcKB97kW.js → importer-BbNb7eNg.js} +111 -71
  21. package/dist/chunks/importer-BbNb7eNg.js.map +1 -0
  22. package/dist/chunks/{importer-D_srHyj6.js → importer-C98dpSH4.js} +5 -5
  23. package/dist/chunks/{importer-D_srHyj6.js.map → importer-C98dpSH4.js.map} +1 -1
  24. package/dist/chunks/{importer-BJlGAOht.js → importer-D794XUTe.js} +3 -3
  25. package/dist/chunks/{importer-BJlGAOht.js.map → importer-D794XUTe.js.map} +1 -1
  26. package/dist/chunks/{importer-AjwOJrn2.js → importer-Dz7tI8Sa.js} +202 -181
  27. package/dist/chunks/importer-Dz7tI8Sa.js.map +1 -0
  28. package/dist/chunks/{importer-DEp_fYjf.js → importer-Ht7LNg8L.js} +92 -64
  29. package/dist/chunks/importer-Ht7LNg8L.js.map +1 -0
  30. package/dist/chunks/{importer-CPqnIY8m.js → importer-WrGRZb0N.js} +42 -29
  31. package/dist/chunks/importer-WrGRZb0N.js.map +1 -0
  32. package/dist/chunks/{importer-BpuS3J8C.js → importer-xZ-3suY1.js} +74 -61
  33. package/dist/chunks/importer-xZ-3suY1.js.map +1 -0
  34. package/dist/chunks/{json-elements-DpO6AXIc.js → json-elements-DKSqpNW1.js} +3 -3
  35. package/dist/chunks/{json-elements-DpO6AXIc.js.map → json-elements-DKSqpNW1.js.map} +1 -1
  36. package/dist/chunks/{ontology-export-50L9DOV4.js → ontology-export-CtRXjg_i.js} +2 -2
  37. package/dist/chunks/{ontology-export-50L9DOV4.js.map → ontology-export-CtRXjg_i.js.map} +1 -1
  38. package/dist/chunks/{records-r3SFHz11.js → records-Cu4bxYOm.js} +69 -36
  39. package/dist/chunks/records-Cu4bxYOm.js.map +1 -0
  40. package/dist/chunks/{text-Bjfa1xYL.js → text-DHOJXnED.js} +20 -11
  41. package/dist/chunks/text-DHOJXnED.js.map +1 -0
  42. package/dist/csv.js +323 -241
  43. package/dist/csv.js.map +1 -1
  44. package/dist/cx.js +1 -1
  45. package/dist/cx2.js +2 -2
  46. package/dist/cys.js +1 -1
  47. package/dist/dot.js +1 -1
  48. package/dist/gexf.js +195 -97
  49. package/dist/gexf.js.map +1 -1
  50. package/dist/gml.js +170 -149
  51. package/dist/gml.js.map +1 -1
  52. package/dist/graph-io.js +27 -27
  53. package/dist/graphml.js +2 -2
  54. package/dist/json.js +1 -1
  55. package/dist/neo4j.js +103 -65
  56. package/dist/neo4j.js.map +1 -1
  57. package/dist/obo.js +1 -1
  58. package/dist/pajek.js +1 -1
  59. package/dist/src/children.d.ts.map +1 -1
  60. package/dist/src/children.js +50 -27
  61. package/dist/src/children.js.map +1 -1
  62. package/dist/src/common/attributes.d.ts.map +1 -1
  63. package/dist/src/common/attributes.js +86 -42
  64. package/dist/src/common/attributes.js.map +1 -1
  65. package/dist/src/common/input.d.ts.map +1 -1
  66. package/dist/src/common/input.js +358 -184
  67. package/dist/src/common/input.js.map +1 -1
  68. package/dist/src/common/lists.js +30 -15
  69. package/dist/src/common/lists.js.map +1 -1
  70. package/dist/src/common/text.d.ts +7 -0
  71. package/dist/src/common/text.d.ts.map +1 -1
  72. package/dist/src/common/text.js +15 -7
  73. package/dist/src/common/text.js.map +1 -1
  74. package/dist/src/common/zip.d.ts.map +1 -1
  75. package/dist/src/common/zip.js +36 -19
  76. package/dist/src/common/zip.js.map +1 -1
  77. package/dist/src/formats/csv/exporter.d.ts.map +1 -1
  78. package/dist/src/formats/csv/exporter.js +288 -146
  79. package/dist/src/formats/csv/exporter.js.map +1 -1
  80. package/dist/src/formats/csv/importer.d.ts.map +1 -1
  81. package/dist/src/formats/csv/importer.js +129 -80
  82. package/dist/src/formats/csv/importer.js.map +1 -1
  83. package/dist/src/formats/csv/records.d.ts.map +1 -1
  84. package/dist/src/formats/csv/records.js +78 -36
  85. package/dist/src/formats/csv/records.js.map +1 -1
  86. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  87. package/dist/src/formats/cys/tables.js +11 -8
  88. package/dist/src/formats/cys/tables.js.map +1 -1
  89. package/dist/src/formats/dot/tokenizer.d.ts +39 -0
  90. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
  91. package/dist/src/formats/dot/tokenizer.js +112 -66
  92. package/dist/src/formats/dot/tokenizer.js.map +1 -1
  93. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  94. package/dist/src/formats/gexf/exporter.js +65 -37
  95. package/dist/src/formats/gexf/exporter.js.map +1 -1
  96. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  97. package/dist/src/formats/gexf/importer.js +138 -50
  98. package/dist/src/formats/gexf/importer.js.map +1 -1
  99. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  100. package/dist/src/formats/gml/exporter.js +51 -35
  101. package/dist/src/formats/gml/exporter.js.map +1 -1
  102. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  103. package/dist/src/formats/gml/importer.js +103 -58
  104. package/dist/src/formats/gml/importer.js.map +1 -1
  105. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  106. package/dist/src/formats/gml/syntax.js +17 -8
  107. package/dist/src/formats/gml/syntax.js.map +1 -1
  108. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  109. package/dist/src/formats/graphml/exporter.js +34 -24
  110. package/dist/src/formats/graphml/exporter.js.map +1 -1
  111. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  112. package/dist/src/formats/graphml/importer.js +17 -13
  113. package/dist/src/formats/graphml/importer.js.map +1 -1
  114. package/dist/src/formats/graphml/tree.js +23 -14
  115. package/dist/src/formats/graphml/tree.js.map +1 -1
  116. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  117. package/dist/src/formats/graphml/yfiles.js +22 -10
  118. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  119. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  120. package/dist/src/formats/json/dialect.js +54 -20
  121. package/dist/src/formats/json/dialect.js.map +1 -1
  122. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  123. package/dist/src/formats/json/exporter.js +38 -21
  124. package/dist/src/formats/json/exporter.js.map +1 -1
  125. package/dist/src/formats/json/importer.d.ts.map +1 -1
  126. package/dist/src/formats/json/importer.js +536 -343
  127. package/dist/src/formats/json/importer.js.map +1 -1
  128. package/dist/src/formats/json/obographs.js +29 -17
  129. package/dist/src/formats/json/obographs.js.map +1 -1
  130. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  131. package/dist/src/formats/neo4j/exporter.js +73 -43
  132. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  133. package/dist/src/formats/neo4j/header.d.ts.map +1 -1
  134. package/dist/src/formats/neo4j/header.js +37 -16
  135. package/dist/src/formats/neo4j/header.js.map +1 -1
  136. package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
  137. package/dist/src/formats/neo4j/importer.js +21 -11
  138. package/dist/src/formats/neo4j/importer.js.map +1 -1
  139. package/dist/src/formats/obo/exporter.d.ts.map +1 -1
  140. package/dist/src/formats/obo/exporter.js +18 -7
  141. package/dist/src/formats/obo/exporter.js.map +1 -1
  142. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  143. package/dist/src/formats/pajek/exporter.js +142 -73
  144. package/dist/src/formats/pajek/exporter.js.map +1 -1
  145. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  146. package/dist/src/formats/pajek/importer.js +26 -15
  147. package/dist/src/formats/pajek/importer.js.map +1 -1
  148. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  149. package/dist/src/formats/pajek/syntax.js +85 -58
  150. package/dist/src/formats/pajek/syntax.js.map +1 -1
  151. package/dist/src/formats/xgmml/emit.d.ts +7 -0
  152. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  153. package/dist/src/formats/xgmml/emit.js +79 -40
  154. package/dist/src/formats/xgmml/emit.js.map +1 -1
  155. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  156. package/dist/src/formats/xgmml/exporter.js +38 -12
  157. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  158. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  159. package/dist/src/formats/xgmml/importer.js +38 -18
  160. package/dist/src/formats/xgmml/importer.js.map +1 -1
  161. package/dist/src/sniff.js +109 -57
  162. package/dist/src/sniff.js.map +1 -1
  163. package/dist/xgmml.js +2 -2
  164. package/package.json +2 -2
  165. package/src/children.ts +54 -27
  166. package/src/common/attributes.ts +118 -49
  167. package/src/common/input.ts +439 -197
  168. package/src/common/lists.ts +29 -15
  169. package/src/common/text.ts +24 -15
  170. package/src/common/zip.ts +47 -18
  171. package/src/formats/csv/exporter.ts +377 -175
  172. package/src/formats/csv/importer.ts +155 -84
  173. package/src/formats/csv/records.ts +90 -35
  174. package/src/formats/cys/tables.ts +11 -10
  175. package/src/formats/dot/tokenizer.ts +118 -68
  176. package/src/formats/gexf/exporter.ts +67 -36
  177. package/src/formats/gexf/importer.ts +173 -63
  178. package/src/formats/gml/exporter.ts +78 -82
  179. package/src/formats/gml/importer.ts +119 -68
  180. package/src/formats/gml/syntax.ts +18 -8
  181. package/src/formats/graphml/exporter.ts +44 -24
  182. package/src/formats/graphml/importer.ts +18 -11
  183. package/src/formats/graphml/tree.ts +22 -12
  184. package/src/formats/graphml/yfiles.ts +23 -10
  185. package/src/formats/json/dialect.ts +61 -22
  186. package/src/formats/json/exporter.ts +40 -21
  187. package/src/formats/json/importer.ts +701 -385
  188. package/src/formats/json/obographs.ts +35 -15
  189. package/src/formats/neo4j/exporter.ts +79 -41
  190. package/src/formats/neo4j/header.ts +50 -16
  191. package/src/formats/neo4j/importer.ts +21 -10
  192. package/src/formats/obo/exporter.ts +34 -17
  193. package/src/formats/pajek/exporter.ts +180 -117
  194. package/src/formats/pajek/importer.ts +27 -15
  195. package/src/formats/pajek/syntax.ts +99 -58
  196. package/src/formats/xgmml/emit.ts +88 -40
  197. package/src/formats/xgmml/exporter.ts +40 -12
  198. package/src/formats/xgmml/importer.ts +45 -23
  199. package/src/sniff.ts +115 -59
  200. package/dist/chunks/children-CL3Cy0ez.js.map +0 -1
  201. package/dist/chunks/export-B-aVCwyG.js.map +0 -1
  202. package/dist/chunks/exporter-BVXnV9-9.js.map +0 -1
  203. package/dist/chunks/exporter-Bcp-l4Bo.js.map +0 -1
  204. package/dist/chunks/importer-AjwOJrn2.js.map +0 -1
  205. package/dist/chunks/importer-BpuS3J8C.js.map +0 -1
  206. package/dist/chunks/importer-CPqnIY8m.js.map +0 -1
  207. package/dist/chunks/importer-DEp_fYjf.js.map +0 -1
  208. package/dist/chunks/importer-DcKB97kW.js.map +0 -1
  209. package/dist/chunks/records-r3SFHz11.js.map +0 -1
  210. package/dist/chunks/text-Bjfa1xYL.js.map +0 -1
package/dist/csv.js CHANGED
@@ -1,8 +1,8 @@
1
- import { b as plural, bU as DICT_SAMPLE_ROWS, ac as DictHeuristic, R as ROLE_TAKEN_CODE$1, I as ID_MERGED_CODE$1, m as DUPLICATE_EDGE_ID_CODE$1, n as DUPLICATE_NODE_CODE$1, t as MISSING_ID_CODE$1, s as MISSING_ENDPOINT_CODE$1, E as EMPTY_INPUT_CODE$1, al as resolveImportOptions, a8 as ImportReportBuilder, ao as reportSinkOptions, an as reportUnusedOptions, aq as DirectionResolver, ar as IdCoercer, am as throwIfAborted, O as OPTION_IGNORED_CODE, aK as parseWeightText, a0 as uniqueColumnName, ay as RENAMED_CODE, a1 as declareResolved, c as capabilities, L as LOSS, a as checkCapabilities, r as resolveExportOptions, p as pairFolding, bD as countMixedEdges, $ as agree, e as explicitWeights, b_ as otherFormatDialect, bS as joinListText, aG as formatInteger, aF as formatDecimal, aH as canonicalId, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, C as COLUMN_RENAMED_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, P as PRECISION_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE } from "./chunks/export-B-aVCwyG.js";
2
- import { i as inferTextDtype, T as TextCellWriter, p as parseTextCell, W as WIDENING_UNSUPPORTED_CODE } from "./chunks/text-Bjfa1xYL.js";
1
+ import { b as plural, bU as DICT_SAMPLE_ROWS, ac as DictHeuristic, R as ROLE_TAKEN_CODE$1, I as ID_MERGED_CODE$1, m as DUPLICATE_EDGE_ID_CODE$1, n as DUPLICATE_NODE_CODE$1, t as MISSING_ID_CODE$1, s as MISSING_ENDPOINT_CODE$1, E as EMPTY_INPUT_CODE$1, al as resolveImportOptions, a8 as ImportReportBuilder, ao as reportSinkOptions, an as reportUnusedOptions, aq as DirectionResolver, ar as IdCoercer, am as throwIfAborted, O as OPTION_IGNORED_CODE, aK as parseWeightText, a0 as uniqueColumnName, ay as RENAMED_CODE, a1 as declareResolved, c as capabilities, L as LOSS, a as checkCapabilities, r as resolveExportOptions, p as pairFolding, $ as agree, e as explicitWeights, b_ as otherFormatDialect, bD as countMixedEdges, bS as joinListText, aG as formatInteger, aF as formatDecimal, aH as canonicalId, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, C as COLUMN_RENAMED_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, P as PRECISION_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE } from "./chunks/export-rKLeGQMo.js";
2
+ import { i as inferTextDtype, T as TextCellWriter, p as parseTextCell, W as WIDENING_UNSUPPORTED_CODE } from "./chunks/text-DHOJXnED.js";
3
3
  import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
4
- import { s as stripLeadingComments, a as sniffNewline, b as sniffDelimiter, D as DELIMITER_CANDIDATES, c as checkRecordSyntax, C as CsvRecordReader, d as splitRecords, B as BAD_QUOTE_CODE, U as UNCLOSED_QUOTE_CODE } from "./chunks/records-r3SFHz11.js";
5
- import { o as quoteCsvCell } from "./chunks/escape-BJRLd3vJ.js";
4
+ import { s as stripLeadingComments, a as sniffNewline, b as sniffDelimiter, D as DELIMITER_CANDIDATES, C as CsvRecordReader, d as splitRecords, c as checkRecordSyntax, B as BAD_QUOTE_CODE, U as UNCLOSED_QUOTE_CODE } from "./chunks/records-Cu4bxYOm.js";
5
+ import { o as quoteCsvCell } from "./chunks/escape-B62ZGM_a.js";
6
6
  import { j as joinText, e as encodeChunks } from "./chunks/writer-DxjgHwSe.js";
7
7
  const SOURCE_NAMES = Object.freeze([
8
8
  "source",
@@ -336,6 +336,38 @@ function isPadded(text, quoted) {
336
336
  }
337
337
  function resolveCsvOptions(options) {
338
338
  const o = options ?? {};
339
+ checkDialectOptions(o);
340
+ for (const name of ["sourceColumn", "targetColumn", "idColumn", "labelColumn"]) {
341
+ checkColumnRef(name, o[name]);
342
+ }
343
+ if (o.typeColumn !== null) {
344
+ checkColumnRef("typeColumn", o.typeColumn);
345
+ }
346
+ if (o.table === "adjacency") {
347
+ checkAdjacencyOptions(o);
348
+ }
349
+ if (o.rowNumberIds !== void 0 && typeof o.rowNumberIds !== "boolean") {
350
+ throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: expected a boolean", {
351
+ option: "rowNumberIds",
352
+ found: o.rowNumberIds
353
+ });
354
+ }
355
+ return {
356
+ delimiter: o.delimiter ?? null,
357
+ decimalComma: o.decimal === ",",
358
+ header: o.header ?? "auto",
359
+ table: o.table ?? "auto",
360
+ sourceColumn: o.sourceColumn ?? null,
361
+ targetColumn: o.targetColumn ?? null,
362
+ typeColumn: o.typeColumn,
363
+ idColumn: o.idColumn ?? null,
364
+ labelColumn: o.labelColumn ?? null,
365
+ nodes: nodesInput(o.nodes),
366
+ rowNumberIds: o.rowNumberIds ?? false,
367
+ explicitDirected: typeof options?.defaultDirected === "boolean" ? options.defaultDirected : null
368
+ };
369
+ }
370
+ function checkDialectOptions(o) {
339
371
  if (o.delimiter !== void 0 && (typeof o.delimiter !== "string" || o.delimiter.length === 0)) {
340
372
  throw new GraphFormatError("E_UNSUPPORTED", "option delimiter: expected a non-empty string", {
341
373
  option: "delimiter",
@@ -369,48 +401,22 @@ function resolveCsvOptions(options) {
369
401
  found: o.table
370
402
  });
371
403
  }
372
- for (const name of ["sourceColumn", "targetColumn", "idColumn", "labelColumn"]) {
373
- checkColumnRef(name, o[name]);
374
- }
375
- if (o.typeColumn !== null) {
376
- checkColumnRef("typeColumn", o.typeColumn);
377
- }
378
- if (o.table === "adjacency") {
379
- for (const name of ["sourceColumn", "targetColumn", "typeColumn", "idColumn", "labelColumn"]) {
380
- if (o[name] !== void 0) {
381
- throw new GraphFormatError("E_UNSUPPORTED", `option ${name}: an adjacency table has no columns`, {
382
- option: name,
383
- found: o[name]
384
- });
385
- }
386
- }
387
- if (o.rowNumberIds === true) {
388
- throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: an adjacency table names its nodes", {
389
- option: "rowNumberIds",
390
- found: o.rowNumberIds
404
+ }
405
+ function checkAdjacencyOptions(o) {
406
+ for (const name of ["sourceColumn", "targetColumn", "typeColumn", "idColumn", "labelColumn"]) {
407
+ if (o[name] !== void 0) {
408
+ throw new GraphFormatError("E_UNSUPPORTED", `option ${name}: an adjacency table has no columns`, {
409
+ option: name,
410
+ found: o[name]
391
411
  });
392
412
  }
393
413
  }
394
- if (o.rowNumberIds !== void 0 && typeof o.rowNumberIds !== "boolean") {
395
- throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: expected a boolean", {
414
+ if (o.rowNumberIds === true) {
415
+ throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: an adjacency table names its nodes", {
396
416
  option: "rowNumberIds",
397
417
  found: o.rowNumberIds
398
418
  });
399
419
  }
400
- return {
401
- delimiter: o.delimiter ?? null,
402
- decimalComma: o.decimal === ",",
403
- header: o.header ?? "auto",
404
- table: o.table ?? "auto",
405
- sourceColumn: o.sourceColumn ?? null,
406
- targetColumn: o.targetColumn ?? null,
407
- typeColumn: o.typeColumn,
408
- idColumn: o.idColumn ?? null,
409
- labelColumn: o.labelColumn ?? null,
410
- nodes: nodesInput(o.nodes),
411
- rowNumberIds: o.rowNumberIds ?? false,
412
- explicitDirected: typeof options?.defaultDirected === "boolean" ? options.defaultDirected : null
413
- };
414
420
  }
415
421
  function checkColumnRef(name, value) {
416
422
  if (value === void 0) {
@@ -433,6 +439,15 @@ function isBlank(text) {
433
439
  function isUnset(text, quoted) {
434
440
  return quoted !== true && isBlank(text);
435
441
  }
442
+ function setNeighborCells(row, quoted) {
443
+ let neighbors = 0;
444
+ for (let k = 1; k < row.length; k++) {
445
+ if (!isUnset(row[k], quoted[k])) {
446
+ neighbors++;
447
+ }
448
+ }
449
+ return neighbors;
450
+ }
436
451
  const ABORT_CHECK_INTERVAL = 64;
437
452
  function uniqueNames(names, report, line) {
438
453
  const seen = /* @__PURE__ */ new Set();
@@ -564,39 +579,11 @@ class TableReader {
564
579
  const firstLine = this.reader.line;
565
580
  const firstQuoted = this.reader.quoted.slice(0, firstRow.length);
566
581
  const pending = [];
567
- let header;
568
- let mode = this.state.csv.header;
569
- if (mode === "auto" && this.kind === "adjacency") {
570
- mode = false;
571
- } else if (mode === "auto" && this.rowNumbers) {
572
- mode = true;
573
- }
574
- if (mode === "auto") {
575
- const second = await iterator.next();
576
- const secondRow = second.done ? null : second.value;
577
- const secondLine = this.reader.line;
578
- const secondQuoted = this.reader.quoted.slice(0, secondRow?.length ?? 0);
579
- header = looksLikeHeader(firstRow, secondRow) || this.namesExplicitColumn(firstRow);
580
- if (!header) {
581
- pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });
582
- }
583
- if (secondRow !== null) {
584
- pending.push({ row: secondRow, quoted: secondQuoted, line: secondLine });
585
- }
586
- } else {
587
- header = mode;
588
- if (!header) {
589
- pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });
590
- }
591
- }
592
- if (this.rowNumbers && !header) {
593
- report.warning(
594
- "unsupported",
595
- OPTION_IGNORED_CODE,
596
- "option rowNumberIds ignored: a headerless node table takes its ids from column 0",
597
- { line: firstLine, element: "rowNumberIds" }
598
- );
599
- }
582
+ const header = await this.decideHeader(
583
+ iterator,
584
+ { row: firstRow, quoted: firstQuoted, line: firstLine },
585
+ pending
586
+ );
600
587
  const names = header ? uniqueNames(headerNames(firstRow), report, firstLine) : positionalNames(firstRow.length);
601
588
  const last = firstRow.length - 1;
602
589
  const optionalLast = header && last > 0 && isUnset(firstRow[last], firstQuoted[last]);
@@ -626,6 +613,70 @@ class TableReader {
626
613
  for (const { row, quoted, line } of pending) {
627
614
  this.processRow(row, quoted, line);
628
615
  }
616
+ await this.readRemainingRows(iterator);
617
+ if (this.dataRows === 0 && header) {
618
+ report.warning("missing-value", NO_DATA_ROWS_CODE, "the table has a header and no data rows", {
619
+ line: firstLine
620
+ });
621
+ }
622
+ this.reportReaderFindings();
623
+ }
624
+ /**
625
+ * Decide whether the first row is the header, reading the second row when the header option is
626
+ * "auto", and queue the rows read so far that are data.
627
+ * @param iterator - the record iterator, past the first row
628
+ * @param first - the first row
629
+ * @param pending - receives the data rows read so far, in order
630
+ * @returns whether the first row is the header
631
+ */
632
+ async decideHeader(iterator, first, pending) {
633
+ const mode = this.headerMode();
634
+ let second = null;
635
+ let header;
636
+ if (mode === "auto") {
637
+ const next = await iterator.next();
638
+ second = next.done ? null : { row: next.value, quoted: this.reader.quoted.slice(0, next.value.length), line: this.reader.line };
639
+ header = looksLikeHeader(first.row, second?.row ?? null) || this.namesExplicitColumn(first.row);
640
+ } else {
641
+ header = mode;
642
+ }
643
+ if (!header) {
644
+ pending.push(first);
645
+ }
646
+ if (second !== null) {
647
+ pending.push(second);
648
+ }
649
+ if (this.rowNumbers && !header) {
650
+ this.state.report.warning(
651
+ "unsupported",
652
+ OPTION_IGNORED_CODE,
653
+ "option rowNumberIds ignored: a headerless node table takes its ids from column 0",
654
+ { line: first.line, element: "rowNumberIds" }
655
+ );
656
+ }
657
+ return header;
658
+ }
659
+ /**
660
+ * The header option as this table reads it: an adjacency table has no header unless the caller
661
+ * says so (its rows vary in width); a table numbered by rowNumberIds has one, since a headerless
662
+ * table takes its ids from column 0.
663
+ * @returns true, false or "auto"
664
+ */
665
+ headerMode() {
666
+ const mode = this.state.csv.header;
667
+ if (mode === "auto" && this.kind === "adjacency") {
668
+ return false;
669
+ }
670
+ if (mode === "auto" && this.rowNumbers) {
671
+ return true;
672
+ }
673
+ return mode;
674
+ }
675
+ /**
676
+ * Push every row after the header and the rows read to decide it, then finish the column writers.
677
+ * @param iterator - the record iterator
678
+ */
679
+ async readRemainingRows(iterator) {
629
680
  const { signal } = this.state.common;
630
681
  let sinceCheck = 0;
631
682
  for (; ; ) {
@@ -642,12 +693,6 @@ class TableReader {
642
693
  for (const writer of this.writers) {
643
694
  writer?.finish();
644
695
  }
645
- if (this.dataRows === 0 && header) {
646
- report.warning("missing-value", NO_DATA_ROWS_CODE, "the table has a header and no data rows", {
647
- line: firstLine
648
- });
649
- }
650
- this.reportReaderFindings();
651
696
  }
652
697
  /**
653
698
  * Whether a row is Excel's delimiter directive (`sep=;`) under a delimiter the caller gave (a
@@ -1199,12 +1244,7 @@ class TableReader {
1199
1244
  const { where } = this;
1200
1245
  where.line = line;
1201
1246
  where.element = null;
1202
- let neighbors = 0;
1203
- for (let k = 1; k < row.length; k++) {
1204
- if (!isUnset(row[k], quoted[k])) {
1205
- neighbors++;
1206
- }
1207
- }
1247
+ const neighbors = setNeighborCells(row, quoted);
1208
1248
  if (isUnset(row[0], quoted[0])) {
1209
1249
  report.error("missing-value", MISSING_ID_CODE, `blank node cell`, { line });
1210
1250
  counts.skippedNodes++;
@@ -1926,19 +1966,23 @@ function nodeTableNote(names, table, notes) {
1926
1966
  );
1927
1967
  }
1928
1968
  }
1969
+ function idTextCollisions(ids) {
1970
+ const seen = /* @__PURE__ */ new Set();
1971
+ let collisions = 0;
1972
+ for (let i = 0; i < ids.size; i++) {
1973
+ const text = String(ids.idOf(i));
1974
+ if (seen.has(text)) {
1975
+ collisions++;
1976
+ } else {
1977
+ seen.add(text);
1978
+ }
1979
+ }
1980
+ return collisions;
1981
+ }
1929
1982
  function idNotes(snapshot, values, note) {
1930
1983
  const { ids } = snapshot;
1931
1984
  if (ids.kind === "mixed") {
1932
- const seen = /* @__PURE__ */ new Set();
1933
- let collisions = 0;
1934
- for (let i = 0; i < ids.size; i++) {
1935
- const text = String(ids.idOf(i));
1936
- if (seen.has(text)) {
1937
- collisions++;
1938
- } else {
1939
- seen.add(text);
1940
- }
1941
- }
1985
+ const collisions = idTextCollisions(ids);
1942
1986
  if (collisions > 0) {
1943
1987
  note(
1944
1988
  CSV_LOSS.ID_TEXT_COLLISION,
@@ -1983,6 +2027,44 @@ function planExport(snapshot, options, values) {
1983
2027
  edgeRows.push(e);
1984
2028
  }
1985
2029
  }
2030
+ directionNotes(snapshot, csv, edgeRows.length, note);
2031
+ if (folding.mutualCount > 0 && csv.table !== "nodes") {
2032
+ note(
2033
+ CSV_LOSS.MUTUAL_EXPANDED,
2034
+ `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two directed rows; the mutual mark is lost`,
2035
+ null,
2036
+ folding.mutualCount
2037
+ );
2038
+ }
2039
+ if (csv.table === "edges") {
2040
+ noteNodeCoverage(snapshot, edgeRows, note);
2041
+ }
2042
+ const weights = explicitWeights(snapshot);
2043
+ const { edgeId, edgeLabel, edgeColumns } = planEdgeColumns(snapshot, csv, edgeRows, values, note);
2044
+ const { nodeLabel, nodeColumns } = planNodeColumns(snapshot, csv, values, note);
2045
+ if (csv.table !== "nodes") {
2046
+ nodeTableNote(
2047
+ [...nodeLabel === null ? [] : [nodeLabel.meta.name], ...nodeColumns.map((c) => c.column.meta.name)],
2048
+ csv.table,
2049
+ notes
2050
+ );
2051
+ }
2052
+ return {
2053
+ csv,
2054
+ common,
2055
+ notes,
2056
+ edgeRows,
2057
+ folding,
2058
+ edgeId,
2059
+ edgeLabel,
2060
+ weights,
2061
+ edgeColumns,
2062
+ nodeLabel,
2063
+ nodeColumns,
2064
+ idText
2065
+ };
2066
+ }
2067
+ function directionNotes(snapshot, csv, edgeRowCount, note) {
1986
2068
  if (csv.table !== "nodes" && (csv.dialect.type === null || csv.table === "adjacency")) {
1987
2069
  const where = csv.table === "adjacency" ? "an adjacency table" : "the generic dialect";
1988
2070
  const mixed = countMixedEdges(snapshot);
@@ -2001,7 +2083,7 @@ function planExport(snapshot, options, values) {
2001
2083
  mixed
2002
2084
  );
2003
2085
  }
2004
- } else if (csv.table === "edges" && !snapshot.directed && edgeRows.length === 0) {
2086
+ } else if (csv.table === "edges" && !snapshot.directed && edgeRowCount === 0) {
2005
2087
  note(
2006
2088
  CSV_LOSS.DIRECTION_DROPPED,
2007
2089
  "the direction lives in the Type cell of each edge row; an undirected graph without edges reads back as directed",
@@ -2009,18 +2091,8 @@ function planExport(snapshot, options, values) {
2009
2091
  0
2010
2092
  );
2011
2093
  }
2012
- if (folding.mutualCount > 0 && csv.table !== "nodes") {
2013
- note(
2014
- CSV_LOSS.MUTUAL_EXPANDED,
2015
- `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two directed rows; the mutual mark is lost`,
2016
- null,
2017
- folding.mutualCount
2018
- );
2019
- }
2020
- if (csv.table === "edges") {
2021
- noteNodeCoverage(snapshot, edgeRows, note);
2022
- }
2023
- const weights = explicitWeights(snapshot);
2094
+ }
2095
+ function planEdgeColumns(snapshot, csv, edgeRows, values, note) {
2024
2096
  const edgeId = snapshot.edges.byRole("id");
2025
2097
  const edgeLabel = snapshot.edges.byRole("label");
2026
2098
  const edgeReserved = new Set([csv.dialect.source, csv.dialect.target, csv.dialect.weight].map(lower));
@@ -2053,6 +2125,9 @@ function planExport(snapshot, options, values) {
2053
2125
  checkValues(column, "edge", edgeRows, note);
2054
2126
  }
2055
2127
  }
2128
+ return { edgeId, edgeLabel, edgeColumns };
2129
+ }
2130
+ function planNodeColumns(snapshot, csv, values, note) {
2056
2131
  const nodeLabel = snapshot.nodes.byRole("label");
2057
2132
  const nodeReserved = /* @__PURE__ */ new Set([lower(csv.dialect.id)]);
2058
2133
  const nodeRows = [];
@@ -2069,34 +2144,13 @@ function planExport(snapshot, options, values) {
2069
2144
  nodeLabel,
2070
2145
  csv.table === "nodes" ? note : null
2071
2146
  );
2072
- if (csv.table === "nodes") {
2073
- if (values) {
2074
- checkRoleColumn(nodeLabel, "label", LABEL_NAMES, "node", nodeRows, note);
2075
- for (const { column } of nodeColumns) {
2076
- checkValues(column, "node", nodeRows, note);
2077
- }
2147
+ if (csv.table === "nodes" && values) {
2148
+ checkRoleColumn(nodeLabel, "label", LABEL_NAMES, "node", nodeRows, note);
2149
+ for (const { column } of nodeColumns) {
2150
+ checkValues(column, "node", nodeRows, note);
2078
2151
  }
2079
- } else {
2080
- nodeTableNote(
2081
- [...nodeLabel === null ? [] : [nodeLabel.meta.name], ...nodeColumns.map((c) => c.column.meta.name)],
2082
- csv.table,
2083
- notes
2084
- );
2085
2152
  }
2086
- return {
2087
- csv,
2088
- common,
2089
- notes,
2090
- edgeRows,
2091
- folding,
2092
- edgeId,
2093
- edgeLabel,
2094
- weights,
2095
- edgeColumns,
2096
- nodeLabel,
2097
- nodeColumns,
2098
- idText
2099
- };
2153
+ return { nodeLabel, nodeColumns };
2100
2154
  }
2101
2155
  function noteNodeCoverage(snapshot, edgeRows, note) {
2102
2156
  const { nodeCount } = snapshot;
@@ -2152,39 +2206,44 @@ function attributeColumns(table, domain, reserved, idColumn, labelColumn, note)
2152
2206
  if (role !== null && SKIPPED_ROLES.has(role)) {
2153
2207
  continue;
2154
2208
  }
2155
- const low = lower(name);
2156
2209
  const set = column.length - column.nullCount;
2157
- const isIdName = domain === "edge" ? findColumn([name], EDGE_ID_NAMES) >= 0 : false;
2158
- if (reserved.has(low) || isIdName || idColumn !== null && lower(idColumn.meta.name) === low) {
2159
- note?.(
2160
- CSV_LOSS.RESERVED_NAME,
2161
- `${domain} column "${name}" is not written: the name is reserved for the ${isIdName ? "edge id" : low} column`,
2162
- name,
2163
- set
2164
- );
2210
+ if (isReservedName(name, domain, reserved, idColumn, set, note)) {
2165
2211
  continue;
2166
2212
  }
2167
- if (findColumn([name], LABEL_NAMES) >= 0) {
2168
- if (labelColumn !== null) {
2169
- note?.(
2170
- CSV_LOSS.RESERVED_NAME,
2171
- `${domain} column "${name}" is not written: the name is reserved for the label column`,
2172
- name,
2173
- set
2174
- );
2175
- continue;
2176
- }
2177
- note?.(
2178
- CSV_LOSS.ROLE_ASSUMED,
2179
- `${domain} column "${name}" reads back with the label role (string)`,
2180
- name,
2181
- set
2182
- );
2213
+ if (findColumn([name], LABEL_NAMES) >= 0 && !labelNameWritten(name, domain, labelColumn, set, note)) {
2214
+ continue;
2183
2215
  }
2184
2216
  out.push({ column, header: name });
2185
2217
  }
2186
2218
  return out;
2187
2219
  }
2220
+ function isReservedName(name, domain, reserved, idColumn, set, note) {
2221
+ const low = lower(name);
2222
+ const isIdName = domain === "edge" ? findColumn([name], EDGE_ID_NAMES) >= 0 : false;
2223
+ if (reserved.has(low) || isIdName || idColumn !== null && lower(idColumn.meta.name) === low) {
2224
+ note?.(
2225
+ CSV_LOSS.RESERVED_NAME,
2226
+ `${domain} column "${name}" is not written: the name is reserved for the ${isIdName ? "edge id" : low} column`,
2227
+ name,
2228
+ set
2229
+ );
2230
+ return true;
2231
+ }
2232
+ return false;
2233
+ }
2234
+ function labelNameWritten(name, domain, labelColumn, set, note) {
2235
+ if (labelColumn !== null) {
2236
+ note?.(
2237
+ CSV_LOSS.RESERVED_NAME,
2238
+ `${domain} column "${name}" is not written: the name is reserved for the label column`,
2239
+ name,
2240
+ set
2241
+ );
2242
+ return false;
2243
+ }
2244
+ note?.(CSV_LOSS.ROLE_ASSUMED, `${domain} column "${name}" reads back with the label role (string)`, name, set);
2245
+ return true;
2246
+ }
2188
2247
  function checkRoleColumn(column, role, names, domain, rows, note) {
2189
2248
  if (column === null) {
2190
2249
  return;
@@ -2223,20 +2282,7 @@ function checkValues(column, domain, rows, note, roleColumn = false) {
2223
2282
  const { name, dtype } = column.meta;
2224
2283
  const label = `${domain} column "${name}"`;
2225
2284
  if (column.dtype === "f32" || column.dtype === "f64") {
2226
- let nonFinite = 0;
2227
- const { components } = column.meta;
2228
- const { data } = column;
2229
- for (const row of rows) {
2230
- if (!column.isSet(row)) {
2231
- continue;
2232
- }
2233
- for (let k = 0; k < components; k++) {
2234
- if (!Number.isFinite(data[row * components + k])) {
2235
- nonFinite++;
2236
- break;
2237
- }
2238
- }
2239
- }
2285
+ const nonFinite = countNonFinite(column, rows);
2240
2286
  if (nonFinite > 0) {
2241
2287
  note(
2242
2288
  CSV_LOSS.NONFINITE,
@@ -2250,15 +2296,50 @@ function checkValues(column, domain, rows, note, roleColumn = false) {
2250
2296
  if (dtype !== "string" && dtype !== "dict") {
2251
2297
  return;
2252
2298
  }
2299
+ const scan = scanText(column, rows);
2300
+ if (roleColumn || scan.widest < 0) {
2301
+ return;
2302
+ }
2303
+ const readsAsDict = scan.candidate && scan.heuristic.decide() === "dict";
2304
+ if (scan.widest < WIDENING.string && !readsAsDict) {
2305
+ note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[scan.widest]}`, name, scan.set);
2306
+ return;
2307
+ }
2308
+ if (dtype === "dict" && !readsAsDict) {
2309
+ note(
2310
+ CSV_LOSS.STORAGE_CLASS_CHANGED,
2311
+ `${label}: reads back as string (cardinality too high for a dict)`,
2312
+ name,
2313
+ null
2314
+ );
2315
+ } else if (dtype === "string" && readsAsDict) {
2316
+ note(CSV_LOSS.STORAGE_CLASS_CHANGED, `${label}: reads back as dict (low cardinality)`, name, null);
2317
+ }
2318
+ }
2319
+ function countNonFinite(column, rows) {
2320
+ let nonFinite = 0;
2321
+ const { components } = column.meta;
2322
+ const { data } = column;
2323
+ for (const row of rows) {
2324
+ if (!column.isSet(row)) {
2325
+ continue;
2326
+ }
2327
+ for (let k = 0; k < components; k++) {
2328
+ if (!Number.isFinite(data[row * components + k])) {
2329
+ nonFinite++;
2330
+ break;
2331
+ }
2332
+ }
2333
+ }
2334
+ return nonFinite;
2335
+ }
2336
+ function scanText(column, rows) {
2253
2337
  let widest = -1;
2254
2338
  let candidate = true;
2255
2339
  let set = 0;
2256
2340
  const heuristic = new DictHeuristic(DICT_SAMPLE_ROWS);
2257
2341
  for (const row of rows) {
2258
- if (!column.isSet(row)) {
2259
- continue;
2260
- }
2261
- const text = column.value(row);
2342
+ const text = column.isSet(row) ? column.value(row) : null;
2262
2343
  if (typeof text !== "string") {
2263
2344
  continue;
2264
2345
  }
@@ -2266,30 +2347,11 @@ function checkValues(column, domain, rows, note, roleColumn = false) {
2266
2347
  const kind = inferTextDtype(text);
2267
2348
  widest = Math.max(widest, WIDENING[kind]);
2268
2349
  if (!heuristic.decided) {
2269
- if (kind !== "string") {
2270
- candidate = false;
2271
- }
2350
+ candidate &&= kind === "string";
2272
2351
  heuristic.observe(text);
2273
2352
  }
2274
2353
  }
2275
- if (roleColumn || widest < 0) {
2276
- return;
2277
- }
2278
- const readsAsDict = candidate && heuristic.decide() === "dict";
2279
- if (widest < WIDENING.string && !readsAsDict) {
2280
- note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[widest]}`, name, set);
2281
- return;
2282
- }
2283
- if (dtype === "dict" && !readsAsDict) {
2284
- note(
2285
- CSV_LOSS.STORAGE_CLASS_CHANGED,
2286
- `${label}: reads back as string (cardinality too high for a dict)`,
2287
- name,
2288
- null
2289
- );
2290
- } else if (dtype === "string" && readsAsDict) {
2291
- note(CSV_LOSS.STORAGE_CLASS_CHANGED, `${label}: reads back as dict (low cardinality)`, name, null);
2292
- }
2354
+ return { widest, candidate, set, heuristic };
2293
2355
  }
2294
2356
  function typeText(snapshot, plan, e) {
2295
2357
  if (!snapshot.directed || !plan.folding.sourceDirected(e)) {
@@ -2303,6 +2365,14 @@ function neighbourCell(id, weight) {
2303
2365
  }
2304
2366
  return id.includes(":") ? `${id}:` : id;
2305
2367
  }
2368
+ function sourceRunEnd(src, edgeRows, r) {
2369
+ const source = src[edgeRows[r]];
2370
+ let end = r;
2371
+ while (end < edgeRows.length && src[edgeRows[end]] === source) {
2372
+ end++;
2373
+ }
2374
+ return end;
2375
+ }
2306
2376
  function* adjacencyLines(snapshot, plan) {
2307
2377
  const { delimiter, newline } = plan.csv;
2308
2378
  const forced = (text) => `"${text.replaceAll('"', '""')}"`;
@@ -2329,10 +2399,7 @@ function* adjacencyLines(snapshot, plan) {
2329
2399
  let r = 0;
2330
2400
  while (r < edgeRows.length) {
2331
2401
  const source = list.src[edgeRows[r]];
2332
- let end = r;
2333
- while (end < edgeRows.length && list.src[edgeRows[end]] === source) {
2334
- end++;
2335
- }
2402
+ const end = sourceRunEnd(list.src, edgeRows, r);
2336
2403
  const fresh = [];
2337
2404
  let highest = -1;
2338
2405
  let inOrder = true;
@@ -2366,59 +2433,54 @@ function* adjacencyLines(snapshot, plan) {
2366
2433
  yield* singles(snapshot.nodeCount - 1);
2367
2434
  }
2368
2435
  function* lines(snapshot, plan) {
2369
- const { csv } = plan;
2370
- if (csv.table === "adjacency") {
2436
+ if (plan.csv.table === "adjacency") {
2371
2437
  yield* adjacencyLines(snapshot, plan);
2372
- return;
2438
+ } else if (plan.csv.table === "nodes") {
2439
+ yield* nodeLines(snapshot, plan);
2440
+ } else {
2441
+ yield* edgeLines(snapshot, plan);
2373
2442
  }
2374
- const { delimiter, newline } = csv;
2443
+ }
2444
+ function cellWriters(delimiter) {
2375
2445
  const quote = (text) => quoteCsvCell(text, delimiter);
2376
2446
  const cell = (column, row) => {
2377
2447
  const text = cellText(column, row);
2378
2448
  return text === null ? "" : quote(text);
2379
2449
  };
2380
- if (csv.table === "nodes") {
2381
- const headers2 = [csv.dialect.id];
2382
- if (plan.nodeLabel !== null) {
2383
- headers2.push(plan.nodeLabel.meta.name);
2384
- }
2385
- for (const { header } of plan.nodeColumns) {
2386
- headers2.push(header);
2387
- }
2388
- if (csv.header) {
2389
- yield headers2.map(quote).join(delimiter) + newline;
2390
- }
2391
- for (let i = 0; i < snapshot.nodeCount; i++) {
2392
- const cells = [quote(plan.idText(i))];
2393
- if (plan.nodeLabel !== null) {
2394
- cells.push(cell(plan.nodeLabel, i));
2395
- }
2396
- for (const { column } of plan.nodeColumns) {
2397
- cells.push(cell(column, i));
2398
- }
2399
- yield cells.join(delimiter) + newline;
2400
- }
2401
- return;
2402
- }
2403
- const headers = [csv.dialect.source, csv.dialect.target];
2404
- if (csv.dialect.type !== null) {
2405
- headers.push(csv.dialect.type);
2406
- }
2407
- if (plan.edgeId !== null) {
2408
- headers.push(plan.edgeId.meta.name);
2409
- }
2410
- if (plan.edgeLabel !== null) {
2411
- headers.push(plan.edgeLabel.meta.name);
2412
- }
2413
- if (plan.weights.weighted) {
2414
- headers.push(csv.dialect.weight);
2450
+ return { quote, cell };
2451
+ }
2452
+ function* nodeLines(snapshot, plan) {
2453
+ const { csv } = plan;
2454
+ const { delimiter, newline } = csv;
2455
+ const { quote, cell } = cellWriters(delimiter);
2456
+ const headers = [csv.dialect.id];
2457
+ if (plan.nodeLabel !== null) {
2458
+ headers.push(plan.nodeLabel.meta.name);
2415
2459
  }
2416
- for (const { header } of plan.edgeColumns) {
2460
+ for (const { header } of plan.nodeColumns) {
2417
2461
  headers.push(header);
2418
2462
  }
2419
2463
  if (csv.header) {
2420
2464
  yield headers.map(quote).join(delimiter) + newline;
2421
2465
  }
2466
+ for (let i = 0; i < snapshot.nodeCount; i++) {
2467
+ const cells = [quote(plan.idText(i))];
2468
+ if (plan.nodeLabel !== null) {
2469
+ cells.push(cell(plan.nodeLabel, i));
2470
+ }
2471
+ for (const { column } of plan.nodeColumns) {
2472
+ cells.push(cell(column, i));
2473
+ }
2474
+ yield cells.join(delimiter) + newline;
2475
+ }
2476
+ }
2477
+ function* edgeLines(snapshot, plan) {
2478
+ const { csv } = plan;
2479
+ const { delimiter, newline } = csv;
2480
+ const { quote, cell } = cellWriters(delimiter);
2481
+ if (csv.header) {
2482
+ yield edgeHeaders(plan).map(quote).join(delimiter) + newline;
2483
+ }
2422
2484
  const list = snapshot.edgeList();
2423
2485
  for (const e of plan.edgeRows) {
2424
2486
  const cells = [quote(plan.idText(list.src[e])), quote(plan.idText(list.dst[e]))];
@@ -2440,6 +2502,26 @@ function* lines(snapshot, plan) {
2440
2502
  yield cells.join(delimiter) + newline;
2441
2503
  }
2442
2504
  }
2505
+ function edgeHeaders(plan) {
2506
+ const { dialect } = plan.csv;
2507
+ const headers = [dialect.source, dialect.target];
2508
+ if (dialect.type !== null) {
2509
+ headers.push(dialect.type);
2510
+ }
2511
+ if (plan.edgeId !== null) {
2512
+ headers.push(plan.edgeId.meta.name);
2513
+ }
2514
+ if (plan.edgeLabel !== null) {
2515
+ headers.push(plan.edgeLabel.meta.name);
2516
+ }
2517
+ if (plan.weights.weighted) {
2518
+ headers.push(dialect.weight);
2519
+ }
2520
+ for (const { header } of plan.edgeColumns) {
2521
+ headers.push(header);
2522
+ }
2523
+ return headers;
2524
+ }
2443
2525
  function prepare(snapshot, options) {
2444
2526
  const plan = planExport(snapshot, options, false);
2445
2527
  const collision = plan.notes.find((n) => n.code === CSV_LOSS.ID_TEXT_COLLISION);