@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/gml.js CHANGED
@@ -1,8 +1,8 @@
1
- import { ak as SYNTAX_CODE, b as plural, a as checkCapabilities, L as LOSS, p as pairFolding, $ as agree, c as capabilities, r as resolveExportOptions, ac as DictHeuristic, aG as formatInteger, bv as formatGmlReal, aE as sanitizeIds, bD as countMixedEdges, e as explicitWeights, bU as DICT_SAMPLE_ROWS, R as ROLE_TAKEN_CODE$1, aj as WIDENED_CODE$1, P as PRECISION_CODE$1, n as DUPLICATE_NODE_CODE$1, s as MISSING_ENDPOINT_CODE$1, t as MISSING_ID_CODE$1, l as MULTIPLE_GRAPHS_CODE, N as NO_GRAPH_CODE$1, al as resolveImportOptions, a8 as ImportReportBuilder, an as reportUnusedOptions, bB as readText, am as throwIfAborted, ao as reportSinkOptions, ap as graphChosen, ar as IdCoercer, aq as DirectionResolver, a0 as uniqueColumnName, C as COLUMN_RENAMED_CODE, as as weightFromValue, bV as reportWeightPrecision, aK as parseWeightText, a1 as declareResolved, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, I as ID_MERGED_CODE, ae as AMBIGUOUS_GRAPH_NAME_CODE, af as GRAPH_NOT_FOUND_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE } from "./chunks/export-B-aVCwyG.js";
1
+ import { ak as SYNTAX_CODE, b as plural, a as checkCapabilities, L as LOSS, p as pairFolding, $ as agree, c as capabilities, r as resolveExportOptions, ac as DictHeuristic, aG as formatInteger, bv as formatGmlReal, aE as sanitizeIds, bD as countMixedEdges, e as explicitWeights, bU as DICT_SAMPLE_ROWS, R as ROLE_TAKEN_CODE$1, aj as WIDENED_CODE$1, P as PRECISION_CODE$1, n as DUPLICATE_NODE_CODE$1, s as MISSING_ENDPOINT_CODE$1, t as MISSING_ID_CODE$1, l as MULTIPLE_GRAPHS_CODE, N as NO_GRAPH_CODE$1, al as resolveImportOptions, a8 as ImportReportBuilder, an as reportUnusedOptions, bB as readText, am as throwIfAborted, ao as reportSinkOptions, ap as graphChosen, ar as IdCoercer, aq as DirectionResolver, a0 as uniqueColumnName, C as COLUMN_RENAMED_CODE, as as weightFromValue, bV as reportWeightPrecision, aK as parseWeightText, a1 as declareResolved, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, I as ID_MERGED_CODE, ae as AMBIGUOUS_GRAPH_NAME_CODE, af as GRAPH_NOT_FOUND_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE } from "./chunks/export-rKLeGQMo.js";
2
2
  import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
3
- import { k as decodeGmlString, m as quoteGmlString } from "./chunks/escape-BJRLd3vJ.js";
3
+ import { k as decodeGmlString, m as quoteGmlString } from "./chunks/escape-B62ZGM_a.js";
4
4
  import { j as joinText, e as encodeChunks } from "./chunks/writer-DxjgHwSe.js";
5
- import { i as importChosenGraph } from "./chunks/choose-CS3fvGT1.js";
5
+ import { i as importChosenGraph } from "./chunks/choose-DvpbdRuz.js";
6
6
  const TOKEN_WORD = 0;
7
7
  const TOKEN_INT = 1;
8
8
  const TOKEN_REAL = 2;
@@ -328,14 +328,7 @@ function parseRecord(tokens, open) {
328
328
  const key = tokens.textOf(p);
329
329
  const v = p + 1;
330
330
  if (tokens.kind[v] === TOKEN_STRING) {
331
- const text = tokens.stringOf(v);
332
- if (text === LIST_START_MARKER) {
333
- frame.record[key] = [];
334
- frame.listKeys ??= /* @__PURE__ */ new Set();
335
- frame.listKeys.add(key);
336
- continue;
337
- }
338
- storeValue(frame, key, text === EMPTY_LIST_TEXT || text === EMPTY_TUPLE_TEXT ? [] : text);
331
+ storeString(frame, key, tokens.stringOf(v));
339
332
  } else if (tokens.kind[v] === TOKEN_OPEN) {
340
333
  stack.push({
341
334
  record: /* @__PURE__ */ Object.create(null),
@@ -350,6 +343,15 @@ function parseRecord(tokens, open) {
350
343
  }
351
344
  return root;
352
345
  }
346
+ function storeString(frame, key, text) {
347
+ if (text === LIST_START_MARKER) {
348
+ frame.record[key] = [];
349
+ frame.listKeys ??= /* @__PURE__ */ new Set();
350
+ frame.listKeys.add(key);
351
+ return;
352
+ }
353
+ storeValue(frame, key, text === EMPTY_LIST_TEXT || text === EMPTY_TUPLE_TEXT ? [] : text);
354
+ }
353
355
  function storeValue(frame, key, value) {
354
356
  const { record } = frame;
355
357
  if (frame.listKeys?.has(key)) {
@@ -544,34 +546,30 @@ function uniqueKey(base, used) {
544
546
  function note(code, message, column = null, count = null) {
545
547
  return Object.freeze({ code, message, column, count });
546
548
  }
549
+ function countOnce(seen, flag, stats, counter) {
550
+ if (!seen[flag]) {
551
+ seen[flag] = true;
552
+ stats[counter]++;
553
+ }
554
+ }
547
555
  function inspectJson(value, stats, inArray, seen) {
548
556
  if (value === null) {
549
- if (!seen.nulls) {
550
- seen.nulls = true;
551
- stats.nulls++;
552
- }
557
+ countOnce(seen, "nulls", stats, "nulls");
553
558
  return;
554
559
  }
555
560
  switch (typeof value) {
556
561
  case "number":
557
- if (!seen.numbers) {
558
- seen.numbers = true;
559
- stats.numbers++;
560
- }
562
+ countOnce(seen, "numbers", stats, "numbers");
561
563
  return;
562
564
  case "boolean":
563
- if (!seen.booleans) {
564
- seen.booleans = true;
565
- stats.booleans++;
566
- }
565
+ countOnce(seen, "booleans", stats, "booleans");
567
566
  return;
568
567
  case "string":
569
568
  return;
570
569
  }
571
570
  if (Array.isArray(value)) {
572
- if (inArray && !seen.nested) {
573
- seen.nested = true;
574
- stats.nestedArrays++;
571
+ if (inArray) {
572
+ countOnce(seen, "nested", stats, "nestedArrays");
575
573
  }
576
574
  for (const item of value) {
577
575
  inspectJson(item, stats, true, seen);
@@ -586,6 +584,14 @@ function inspectJson(value, stats, inArray, seen) {
586
584
  inspectJson(record[key], stats, false, seen);
587
585
  }
588
586
  }
587
+ function rowCount(count) {
588
+ return `${count} row${plural(count)}`;
589
+ }
590
+ function pushCounted(notes, count, code, text, column) {
591
+ if (count > 0) {
592
+ notes.push(note(code, text, column, count));
593
+ }
594
+ }
589
595
  function jsonNotes(columns, label, plan, notes) {
590
596
  for (const { column } of columns) {
591
597
  const stats = jsonStatsOf(column);
@@ -594,71 +600,40 @@ function jsonNotes(columns, label, plan, notes) {
594
600
  }
595
601
  const { name } = column.meta;
596
602
  const where = `${label} column "${name}"`;
597
- if (stats.numbers > 0) {
598
- notes.push(
599
- note(
600
- RECORD_NUMBER_TYPE_CODE,
601
- `${where} holds numbers in ${stats.numbers} row${plural(stats.numbers)}; GML records cannot keep the int / real distinction`,
602
- name,
603
- stats.numbers
604
- )
605
- );
606
- }
607
- if (stats.booleans > 0) {
608
- notes.push(
609
- note(
610
- RECORD_BOOLEAN_CODE,
611
- `${where} holds booleans in ${stats.booleans} row${plural(stats.booleans)}; written as 1 / 0`,
612
- name,
613
- stats.booleans
614
- )
615
- );
616
- }
617
- if (stats.nulls > 0) {
618
- notes.push(
619
- note(
620
- RECORD_NULL_CODE,
621
- `${where} holds nulls in ${stats.nulls} row${plural(stats.nulls)}; GML has no null, the key is omitted`,
622
- name,
623
- stats.nulls
624
- )
625
- );
626
- }
627
- if (stats.arrays > 0) {
628
- notes.push(
629
- note(
630
- JSON_ARRAY_CODE,
631
- `${where} holds arrays as values in ${stats.arrays} row${plural(stats.arrays)}; written as repeated keys, they re-import as a list`,
632
- name,
633
- stats.arrays
634
- )
635
- );
636
- }
637
- if (stats.nestedArrays > 0) {
638
- notes.push(
639
- note(
640
- NESTED_ARRAY_CODE,
641
- `${where} holds arrays nested in arrays in ${stats.nestedArrays} row${plural(stats.nestedArrays)}; GML cannot write them and the save fails`,
642
- name,
643
- stats.nestedArrays
644
- )
645
- );
646
- }
647
- if (stats.invalidKeys > 0) {
648
- notes.push(
649
- plan.mangle ? note(
650
- KEY_MANGLED_CODE,
651
- `${where}: ${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys and are rewritten`,
652
- name,
653
- stats.invalidKeys
654
- ) : note(
655
- INVALID_KEY_CODE,
656
- `${where}: ${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys; the save fails unless sanitizeKeys is "mangle"`,
657
- name,
658
- stats.invalidKeys
659
- )
660
- );
661
- }
603
+ const counted = (count, code, text) => {
604
+ pushCounted(notes, count, code, text, name);
605
+ };
606
+ counted(
607
+ stats.numbers,
608
+ RECORD_NUMBER_TYPE_CODE,
609
+ `${where} holds numbers in ${rowCount(stats.numbers)}; GML records cannot keep the int / real distinction`
610
+ );
611
+ counted(
612
+ stats.booleans,
613
+ RECORD_BOOLEAN_CODE,
614
+ `${where} holds booleans in ${rowCount(stats.booleans)}; written as 1 / 0`
615
+ );
616
+ counted(
617
+ stats.nulls,
618
+ RECORD_NULL_CODE,
619
+ `${where} holds nulls in ${rowCount(stats.nulls)}; GML has no null, the key is omitted`
620
+ );
621
+ counted(
622
+ stats.arrays,
623
+ JSON_ARRAY_CODE,
624
+ `${where} holds arrays as values in ${rowCount(stats.arrays)}; written as repeated keys, they re-import as a list`
625
+ );
626
+ counted(
627
+ stats.nestedArrays,
628
+ NESTED_ARRAY_CODE,
629
+ `${where} holds arrays nested in arrays in ${rowCount(stats.nestedArrays)}; GML cannot write them and the save fails`
630
+ );
631
+ const keys = `${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys`;
632
+ counted(
633
+ stats.invalidKeys,
634
+ plan.mangle ? KEY_MANGLED_CODE : INVALID_KEY_CODE,
635
+ plan.mangle ? `${where}: ${keys} and are rewritten` : `${where}: ${keys}; the save fails unless sanitizeKeys is "mangle"`
636
+ );
662
637
  }
663
638
  }
664
639
  function jsonStatsOf(column) {
@@ -1530,29 +1505,46 @@ class GmlImport {
1530
1505
  if (key === "graph" && !record && scalarGraph < 0) {
1531
1506
  scalarGraph = v;
1532
1507
  }
1533
- if (key === "graph" && record) {
1534
- if (this.graphCount++ === this.which) {
1535
- this.graphOpen = v;
1536
- this.scanGraph(v);
1537
- }
1538
- } else if (key === "Creator" && !record && this.creatorToken < 0) {
1539
- this.creatorToken = v;
1540
- } else if (key === "Version" && !record && this.versionToken < 0) {
1541
- this.versionToken = v;
1542
- } else {
1543
- this.observe(this.topSchema, "graph", key, v, TOP_SEQ);
1544
- }
1508
+ this.scanTopPair(key, record, v);
1545
1509
  }
1546
1510
  if (this.graphOpen < 0) {
1547
- if (scalarGraph >= 0) {
1548
- this.report.fail(
1549
- NO_GRAPH_CODE,
1550
- `the graph key at line ${t.line[scalarGraph]} holds ${describeValue(t, scalarGraph)}, not a [ ... ] block; the input contains no graph block`,
1551
- { line: t.line[scalarGraph] }
1552
- );
1511
+ this.failNoGraph(t, scalarGraph);
1512
+ }
1513
+ }
1514
+ /**
1515
+ * Handle one top-level pair other than a scalar `graph`.
1516
+ * @param key - the key
1517
+ * @param record - whether the value is a `[ ... ]` block
1518
+ * @param v - the value token
1519
+ */
1520
+ scanTopPair(key, record, v) {
1521
+ if (key === "graph" && record) {
1522
+ if (this.graphCount++ === this.which) {
1523
+ this.graphOpen = v;
1524
+ this.scanGraph(v);
1553
1525
  }
1554
- this.report.fail(NO_GRAPH_CODE, "the input contains no graph [ ... ] block", { line: null });
1526
+ } else if (key === "Creator" && !record && this.creatorToken < 0) {
1527
+ this.creatorToken = v;
1528
+ } else if (key === "Version" && !record && this.versionToken < 0) {
1529
+ this.versionToken = v;
1530
+ } else {
1531
+ this.observe(this.topSchema, "graph", key, v, TOP_SEQ);
1532
+ }
1533
+ }
1534
+ /**
1535
+ * Fail because the input holds no graph block.
1536
+ * @param t - the tokens
1537
+ * @param scalarGraph - the token of a scalar `graph` key, or -1
1538
+ */
1539
+ failNoGraph(t, scalarGraph) {
1540
+ if (scalarGraph >= 0) {
1541
+ this.report.fail(
1542
+ NO_GRAPH_CODE,
1543
+ `the graph key at line ${t.line[scalarGraph]} holds ${describeValue(t, scalarGraph)}, not a [ ... ] block; the input contains no graph block`,
1544
+ { line: t.line[scalarGraph] }
1545
+ );
1555
1546
  }
1547
+ this.report.fail(NO_GRAPH_CODE, "the input contains no graph [ ... ] block", { line: null });
1556
1548
  }
1557
1549
  /**
1558
1550
  * Walk the graph block for the schema.
@@ -1602,30 +1594,41 @@ class GmlImport {
1602
1594
  const close = t.match[open];
1603
1595
  const seq = this.seq++;
1604
1596
  const schema = domain === "node" ? this.nodeSchema : this.edgeSchema;
1605
- const { weightFrom, restoreMangledIds } = this.options;
1606
1597
  for (let p = open + 1; p < close; p = t.nextPair(p)) {
1607
1598
  const key = t.textOf(p);
1608
1599
  const v = p + 1;
1609
- if (domain === "node") {
1610
- if (key === "id") {
1611
- continue;
1612
- }
1613
- if (key === ORIGINAL_ID_KEY && restoreMangledIds) {
1614
- this.hasOriginalId = true;
1615
- continue;
1616
- }
1617
- } else {
1618
- if (key === "source" || key === "target" || key === "directed") {
1619
- continue;
1620
- }
1621
- if (key === weightFrom) {
1622
- this.weightKinds |= kindBit(t, v);
1623
- continue;
1624
- }
1600
+ if (this.skipsKey(domain, key, v)) {
1601
+ continue;
1625
1602
  }
1626
1603
  this.observe(schema, domain, key, v, seq);
1627
1604
  }
1628
1605
  }
1606
+ /**
1607
+ * Whether a key of a node or edge block is structural (id, endpoints, weight, original id), not
1608
+ * a column; notes the original id and the weight's kinds on the way.
1609
+ * @param domain - node or edge
1610
+ * @param key - the key
1611
+ * @param v - the value token
1612
+ * @returns true when the key is not a column
1613
+ */
1614
+ skipsKey(domain, key, v) {
1615
+ const { weightFrom, restoreMangledIds } = this.options;
1616
+ if (domain === "node") {
1617
+ if (key === ORIGINAL_ID_KEY && restoreMangledIds) {
1618
+ this.hasOriginalId = true;
1619
+ return true;
1620
+ }
1621
+ return key === "id";
1622
+ }
1623
+ if (key === "source" || key === "target" || key === "directed") {
1624
+ return true;
1625
+ }
1626
+ if (key === weightFrom) {
1627
+ this.weightKinds |= kindBit(this.requireTokens(), v);
1628
+ return true;
1629
+ }
1630
+ return false;
1631
+ }
1629
1632
  /**
1630
1633
  * Record one value of one key in a table's schema.
1631
1634
  * @param schema - the table's schema
@@ -1807,19 +1810,12 @@ class GmlImport {
1807
1810
  }
1808
1811
  }
1809
1812
  /**
1810
- * The plan of one schema entry.
1813
+ * Warn about a key that mixes numbers and strings, or holds integers beyond 2^53.
1811
1814
  * @param domain - the table
1812
1815
  * @param entry - the schema entry
1813
- * @param top - whether the key is a top-level one (graph table)
1814
- * @returns the plan
1816
+ * @param asText - whether the integers are kept as text
1815
1817
  */
1816
- planOf(domain, entry, top = false) {
1817
- const asText = entry.unsafe && this.options.long === "string" && kindOf(entry) === "int";
1818
- const kind = asText ? "string" : kindOf(entry);
1819
- let scalar = dtypeOfKind(kind, entry);
1820
- if (scalar === "string" && !entry.list && entry.dict !== null && entry.kinds === KIND_STRING && entry.dict.decide() === "dict") {
1821
- scalar = "dict";
1822
- }
1818
+ warnKinds(domain, entry, asText) {
1823
1819
  if ((entry.kinds & KIND_STRING) !== 0 && (entry.kinds & (KIND_INT | KIND_REAL)) !== 0 && (entry.kinds & KIND_RECORD) === 0) {
1824
1820
  this.report.warnOnce(
1825
1821
  "coercion",
@@ -1838,6 +1834,42 @@ class GmlImport {
1838
1834
  `${PRECISION_CODE}:${domain}:${entry.key}`
1839
1835
  );
1840
1836
  }
1837
+ }
1838
+ /**
1839
+ * The plan of a node `graphics` record: the plan plus its position and rest columns.
1840
+ * @param plan - the record's plan
1841
+ * @param entry - the schema entry
1842
+ * @returns the plan with its position (and rest) plans
1843
+ */
1844
+ graphicsPlan(plan, entry) {
1845
+ const taken = (candidate) => this.nodeSchema.has(candidate) || this.sink.nodeColumn(candidate) !== INVALID_INDEX;
1846
+ const position = {
1847
+ ...plan,
1848
+ name: uniqueColumnName("position", "graphics", taken),
1849
+ dtype: "f64",
1850
+ itemDtype: null,
1851
+ kind: "real",
1852
+ list: false,
1853
+ role: "position"
1854
+ };
1855
+ const rest = entry.rest ? { ...plan, dtype: "json", itemDtype: null, kind: "record", list: false } : null;
1856
+ return { ...plan, position, rest };
1857
+ }
1858
+ /**
1859
+ * The plan of one schema entry.
1860
+ * @param domain - the table
1861
+ * @param entry - the schema entry
1862
+ * @param top - whether the key is a top-level one (graph table)
1863
+ * @returns the plan
1864
+ */
1865
+ planOf(domain, entry, top = false) {
1866
+ const asText = entry.unsafe && this.options.long === "string" && kindOf(entry) === "int";
1867
+ const kind = asText ? "string" : kindOf(entry);
1868
+ let scalar = dtypeOfKind(kind, entry);
1869
+ if (scalar === "string" && !entry.list && entry.dict !== null && entry.kinds === KIND_STRING && entry.dict.decide() === "dict") {
1870
+ scalar = "dict";
1871
+ }
1872
+ this.warnKinds(domain, entry, asText);
1841
1873
  const name = domain === "graph" ? this.uniqueGraphName(entry.key) : entry.key;
1842
1874
  let itemDtype = null;
1843
1875
  if (entry.list) {
@@ -1860,18 +1892,7 @@ class GmlImport {
1860
1892
  seq: Number.NEGATIVE_INFINITY
1861
1893
  };
1862
1894
  if (domain === "node" && entry.key === "graphics" && this.positions && (entry.kinds & KIND_RECORD) !== 0) {
1863
- const taken = (candidate) => this.nodeSchema.has(candidate) || this.sink.nodeColumn(candidate) !== INVALID_INDEX;
1864
- const position = {
1865
- ...plan,
1866
- name: uniqueColumnName("position", "graphics", taken),
1867
- dtype: "f64",
1868
- itemDtype: null,
1869
- kind: "real",
1870
- list: false,
1871
- role: "position"
1872
- };
1873
- const rest = entry.rest ? { ...plan, dtype: "json", itemDtype: null, kind: "record", list: false } : null;
1874
- return { ...plan, position, rest };
1895
+ return this.graphicsPlan(plan, entry);
1875
1896
  }
1876
1897
  return plan;
1877
1898
  }