@graphty/graph-io 0.3.29 → 0.3.31

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 (141) hide show
  1. package/dist/chunks/{choose-DT5da6OC.js → choose-BJq_vp1b.js} +2 -2
  2. package/dist/chunks/{choose-DT5da6OC.js.map → choose-BJq_vp1b.js.map} +1 -1
  3. package/dist/chunks/{constants-DowRD9ai.js → constants-Bw-6JQuC.js} +2 -2
  4. package/dist/chunks/{constants-DowRD9ai.js.map → constants-Bw-6JQuC.js.map} +1 -1
  5. package/dist/chunks/{escape-DQC5HWqF.js → escape-CnTfa_y8.js} +2 -2
  6. package/dist/chunks/{escape-DQC5HWqF.js.map → escape-CnTfa_y8.js.map} +1 -1
  7. package/dist/chunks/{export-CVi12U7J.js → export-ZucVKiU4.js} +97 -91
  8. package/dist/chunks/{export-CVi12U7J.js.map → export-ZucVKiU4.js.map} +1 -1
  9. package/dist/chunks/{exporter-JubAlNWi.js → exporter-BOLJIzvA.js} +5 -5
  10. package/dist/chunks/exporter-BOLJIzvA.js.map +1 -0
  11. package/dist/chunks/{exporter-C3Pa2PSd.js → exporter-DRNkA2k0.js} +9 -10
  12. package/dist/chunks/exporter-DRNkA2k0.js.map +1 -0
  13. package/dist/chunks/{exporter-Cy_1da4c.js → exporter-DgDjwLOm.js} +7 -8
  14. package/dist/chunks/exporter-DgDjwLOm.js.map +1 -0
  15. package/dist/chunks/{exporter-CgsO2nSl.js → exporter-hc2ZuqVe.js} +8 -7
  16. package/dist/chunks/{exporter-CgsO2nSl.js.map → exporter-hc2ZuqVe.js.map} +1 -1
  17. package/dist/chunks/{importer-Dej72hIF.js → importer-BAlFKpHP.js} +10 -9
  18. package/dist/chunks/importer-BAlFKpHP.js.map +1 -0
  19. package/dist/chunks/{importer-CDXlxXlS.js → importer-ByCd2CAY.js} +11 -20
  20. package/dist/chunks/importer-ByCd2CAY.js.map +1 -0
  21. package/dist/chunks/{importer-lLH9gQF_.js → importer-CaLIrkjL.js} +10 -19
  22. package/dist/chunks/importer-CaLIrkjL.js.map +1 -0
  23. package/dist/chunks/{importer-q-R-Q98w.js → importer-D7xB8oVV.js} +4 -4
  24. package/dist/chunks/{importer-q-R-Q98w.js.map → importer-D7xB8oVV.js.map} +1 -1
  25. package/dist/chunks/{importer-DLz2Vb4q.js → importer-DsNj_zoG.js} +5 -5
  26. package/dist/chunks/importer-DsNj_zoG.js.map +1 -0
  27. package/dist/chunks/{importer-BA_hZKo7.js → importer-WPz-AUz8.js} +6 -6
  28. package/dist/chunks/importer-WPz-AUz8.js.map +1 -0
  29. package/dist/chunks/{importer-DVxzzibh.js → importer-rjFCmKfx.js} +5 -5
  30. package/dist/chunks/{importer-DVxzzibh.js.map → importer-rjFCmKfx.js.map} +1 -1
  31. package/dist/chunks/{json-elements-1IUzkRNy.js → json-elements-CkC87El2.js} +12 -16
  32. package/dist/chunks/{json-elements-1IUzkRNy.js.map → json-elements-CkC87El2.js.map} +1 -1
  33. package/dist/chunks/{ontology-export-b0GLNIj-.js → ontology-export-BRErBO6J.js} +11 -9
  34. package/dist/chunks/ontology-export-BRErBO6J.js.map +1 -0
  35. package/dist/chunks/{records-CdyqwXeY.js → records-DwfSjH8c.js} +2 -2
  36. package/dist/chunks/{records-CdyqwXeY.js.map → records-DwfSjH8c.js.map} +1 -1
  37. package/dist/chunks/{text-CDgjbkLC.js → text-BEEWD-G1.js} +2 -2
  38. package/dist/chunks/{text-CDgjbkLC.js.map → text-BEEWD-G1.js.map} +1 -1
  39. package/dist/csv.js +4 -4
  40. package/dist/cx.js +1 -1
  41. package/dist/cx2.js +2 -2
  42. package/dist/cys.js +1 -1
  43. package/dist/dot.js +1 -1
  44. package/dist/gexf.js +9 -8
  45. package/dist/gexf.js.map +1 -1
  46. package/dist/gml.js +3 -3
  47. package/dist/graph-io.js +69 -69
  48. package/dist/graphml.js +2 -2
  49. package/dist/json.js +1 -1
  50. package/dist/neo4j.js +3 -3
  51. package/dist/obo.js +1 -1
  52. package/dist/pajek.js +1 -1
  53. package/dist/src/common/export.d.ts +6 -1
  54. package/dist/src/common/export.d.ts.map +1 -1
  55. package/dist/src/common/export.js +18 -2
  56. package/dist/src/common/export.js.map +1 -1
  57. package/dist/src/common/format.d.ts +0 -8
  58. package/dist/src/common/format.d.ts.map +1 -1
  59. package/dist/src/common/format.js +0 -10
  60. package/dist/src/common/format.js.map +1 -1
  61. package/dist/src/common/json-elements.d.ts +1 -9
  62. package/dist/src/common/json-elements.d.ts.map +1 -1
  63. package/dist/src/common/json-elements.js +2 -12
  64. package/dist/src/common/json-elements.js.map +1 -1
  65. package/dist/src/common/ontology-export.d.ts.map +1 -1
  66. package/dist/src/common/ontology-export.js +11 -15
  67. package/dist/src/common/ontology-export.js.map +1 -1
  68. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  69. package/dist/src/formats/cx/importer.js +3 -3
  70. package/dist/src/formats/cx/importer.js.map +1 -1
  71. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  72. package/dist/src/formats/cx2/exporter.js +2 -4
  73. package/dist/src/formats/cx2/exporter.js.map +1 -1
  74. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  75. package/dist/src/formats/cx2/importer.js +3 -3
  76. package/dist/src/formats/cx2/importer.js.map +1 -1
  77. package/dist/src/formats/cys/exporter.d.ts.map +1 -1
  78. package/dist/src/formats/cys/exporter.js +4 -3
  79. package/dist/src/formats/cys/exporter.js.map +1 -1
  80. package/dist/src/formats/dot/exporter.js +1 -1
  81. package/dist/src/formats/dot/importer.d.ts +2 -2
  82. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  83. package/dist/src/formats/dot/importer.js +7 -11
  84. package/dist/src/formats/dot/importer.js.map +1 -1
  85. package/dist/src/formats/gexf/exporter.d.ts +2 -2
  86. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  87. package/dist/src/formats/gexf/exporter.js +7 -5
  88. package/dist/src/formats/gexf/exporter.js.map +1 -1
  89. package/dist/src/formats/gexf/schema.js +1 -1
  90. package/dist/src/formats/json/exporter.js +1 -1
  91. package/dist/src/formats/json/importer.d.ts.map +1 -1
  92. package/dist/src/formats/json/importer.js +4 -6
  93. package/dist/src/formats/json/importer.js.map +1 -1
  94. package/dist/src/formats/obo/exporter.d.ts +2 -2
  95. package/dist/src/formats/obo/exporter.js +2 -2
  96. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  97. package/dist/src/formats/pajek/exporter.js +5 -4
  98. package/dist/src/formats/pajek/exporter.js.map +1 -1
  99. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  100. package/dist/src/formats/pajek/importer.js +3 -7
  101. package/dist/src/formats/pajek/importer.js.map +1 -1
  102. package/dist/src/formats/pajek/syntax.d.ts +1 -1
  103. package/dist/src/formats/pajek/syntax.js +1 -1
  104. package/dist/src/formats/xgmml/constants.d.ts +1 -1
  105. package/dist/src/formats/xgmml/constants.js +1 -1
  106. package/dist/src/formats/xgmml/emit.js +1 -1
  107. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  108. package/dist/src/formats/xgmml/exporter.js +4 -3
  109. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  110. package/dist/xgmml.js +2 -2
  111. package/package.json +2 -2
  112. package/src/common/export.ts +25 -3
  113. package/src/common/format.ts +0 -11
  114. package/src/common/json-elements.ts +2 -13
  115. package/src/common/ontology-export.ts +12 -17
  116. package/src/formats/cx/importer.ts +2 -3
  117. package/src/formats/cx2/exporter.ts +2 -4
  118. package/src/formats/cx2/importer.ts +2 -3
  119. package/src/formats/cys/exporter.ts +4 -3
  120. package/src/formats/dot/exporter.ts +1 -1
  121. package/src/formats/dot/importer.ts +7 -17
  122. package/src/formats/gexf/exporter.ts +7 -5
  123. package/src/formats/gexf/schema.ts +1 -1
  124. package/src/formats/json/exporter.ts +1 -1
  125. package/src/formats/json/importer.ts +4 -7
  126. package/src/formats/obo/exporter.ts +2 -2
  127. package/src/formats/pajek/exporter.ts +5 -4
  128. package/src/formats/pajek/importer.ts +3 -13
  129. package/src/formats/pajek/syntax.ts +1 -1
  130. package/src/formats/xgmml/constants.ts +1 -1
  131. package/src/formats/xgmml/emit.ts +1 -1
  132. package/src/formats/xgmml/exporter.ts +4 -3
  133. package/dist/chunks/exporter-C3Pa2PSd.js.map +0 -1
  134. package/dist/chunks/exporter-Cy_1da4c.js.map +0 -1
  135. package/dist/chunks/exporter-JubAlNWi.js.map +0 -1
  136. package/dist/chunks/importer-BA_hZKo7.js.map +0 -1
  137. package/dist/chunks/importer-CDXlxXlS.js.map +0 -1
  138. package/dist/chunks/importer-DLz2Vb4q.js.map +0 -1
  139. package/dist/chunks/importer-Dej72hIF.js.map +0 -1
  140. package/dist/chunks/importer-lLH9gQF_.js.map +0 -1
  141. package/dist/chunks/ontology-export-b0GLNIj-.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
2
- import { V as BAD_ASPECT_BLOCK_CODE, W as MULTI_ASPECT_FRAGMENT_CODE, Y as ASPECT_ORDER_CODE, j as COUNT_MISMATCH_CODE, Z as STATUS_FAILED_CODE, _ as STATUS_WARNING_CODE, b as plural, $ as agree, a0 as uniqueColumnName, C as COLUMN_RENAMED_CODE, a1 as declareResolved, a2 as JSON_NONSTANDARD_NUMBER_CODE, o as DUPLICATE_ATTRIBUTE_CODE, P as PRECISION_CODE, a3 as BAD_VALUE_CODE } from "./export-CVi12U7J.js";
3
- import { t as trimTrailingZeros } from "./text-CDgjbkLC.js";
2
+ import { V as BAD_ASPECT_BLOCK_CODE, W as MULTI_ASPECT_FRAGMENT_CODE, Y as ASPECT_ORDER_CODE, j as COUNT_MISMATCH_CODE, Z as STATUS_FAILED_CODE, _ as STATUS_WARNING_CODE, b as plural, $ as agree, a0 as uniqueColumnName, C as COLUMN_RENAMED_CODE, a1 as declareResolved, a2 as JSON_NONSTANDARD_NUMBER_CODE, o as DUPLICATE_ATTRIBUTE_CODE, P as PRECISION_CODE, a3 as BAD_VALUE_CODE } from "./export-ZucVKiU4.js";
3
+ import { t as trimTrailingZeros } from "./text-BEEWD-G1.js";
4
4
  const MAYBE_UNSAFE_INTEGER = new RegExp("(?<![0-9.])[0-9]{16}");
5
5
  const MAYBE_INEXACT_EXPONENT = new RegExp(
6
6
  [
@@ -972,7 +972,7 @@ const Z_COLUMN = "z";
972
972
  function positionDecl(format, sourceDims = 2) {
973
973
  return {
974
974
  name: POSITION_COLUMN,
975
- dtype: "f32",
975
+ dtype: "f64",
976
976
  components: 3,
977
977
  role: "position",
978
978
  mutable: true,
@@ -990,9 +990,6 @@ function zDecl(format) {
990
990
  extra: { cytoscape: "z" }
991
991
  };
992
992
  }
993
- function fitsF32(value) {
994
- return Number.isFinite(Math.fround(value));
995
- }
996
993
  function headText(head) {
997
994
  let encoding = "utf-8";
998
995
  if (head.byteLength >= 2 && head[0] === 255 && head[1] === 254) {
@@ -1204,22 +1201,21 @@ export {
1204
1201
  MAYBE_UNSAFE_INTEGER as M,
1205
1202
  POSITION_COLUMN as P,
1206
1203
  reportSharedBlock as a,
1207
- flipY as b,
1204
+ positionDecl as b,
1208
1205
  cxId as c,
1209
- positionDecl as d,
1210
- declareFresh as e,
1211
- fitsF32 as f,
1212
- inexactLiteral as g,
1206
+ declareFresh as d,
1207
+ inexactLiteral as e,
1208
+ flipY as f,
1209
+ setOwn as g,
1213
1210
  headText as h,
1214
1211
  isRecord as i,
1215
- setOwn as j,
1212
+ MAYBE_INEXACT_EXPONENT as j,
1216
1213
  keptPrecision as k,
1217
- MAYBE_INEXACT_EXPONENT as l,
1218
- rewriteNumbers as m,
1219
- findDuplicateKeys as n,
1214
+ rewriteNumbers as l,
1215
+ findDuplicateKeys as m,
1220
1216
  plainJson as p,
1221
1217
  reportTooDeep as r,
1222
1218
  scanAspects as s,
1223
1219
  zDecl as z
1224
1220
  };
1225
- //# sourceMappingURL=json-elements-1IUzkRNy.js.map
1221
+ //# sourceMappingURL=json-elements-CkC87El2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-elements-1IUzkRNy.js","sources":["../../src/common/json-elements.ts"],"sourcesContent":["/**\n * The JSON reading the two CX formats share (design section 1.0 of\n * design/graph-io/cytoscape-and-obo/design.md): the exact-integer rewrite (also used by the JSON\n * importer), a streaming scanner that walks a CX document's top-level array and hands over each\n * aspect element as soon as its closing brace arrives (section 1.2.5), and the CX id rule of\n * section 1.0.2.\n *\n * The scanner never holds the document as one string, so a file larger than the longest string a\n * JavaScript engine can hold is still read: it keeps the text of the element being read and\n * nothing else. Each element is parsed with `JSON.parse`; an integer literal beyond 2^53 is kept as\n * an ExactInteger (its digits) so an id never loses a digit.\n */\n\nimport {\n type ColumnDecl,\n type ColumnHandle,\n GraphFormatError,\n type GraphSink,\n INVALID_INDEX,\n type NodeId,\n} from \"@graphty/graph-format\";\n\nimport { declareResolved, uniqueColumnName } from \"./attributes.js\";\nimport {\n ASPECT_ORDER_CODE,\n BAD_ASPECT_BLOCK_CODE,\n BAD_VALUE_CODE,\n COLUMN_RENAMED_CODE,\n COUNT_MISMATCH_CODE,\n DUPLICATE_ATTRIBUTE_CODE,\n JSON_NONSTANDARD_NUMBER_CODE,\n MULTI_ASPECT_FRAGMENT_CODE,\n PRECISION_CODE,\n STATUS_FAILED_CODE,\n STATUS_WARNING_CODE,\n} from \"./codes.js\";\nimport { agree, plural } from \"./plural.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\nimport { trimTrailingZeros } from \"./text.js\";\n\n// ============================================================ exact integers\n\n/**\n * A run of 16 digits not inside a fraction: the shortest integer literal that can exceed 2^53 (9007199254740992).\n * @category Plugin helpers\n */\nexport const MAYBE_UNSAFE_INTEGER = /(?<![0-9.])[0-9]{16}/;\n\n/**\n * A literal the double read for may not equal: an exponent of 15 or more (`9.007199254740993e15`,\n * `1e400`: an integer beyond 2^53 or an overflow), an exponent of -300 or less (`1e-400`: an\n * underflow to 0), or 16 or more digits split by a decimal point before an exponent\n * (`90071992547409.93e2`). MAYBE_UNSAFE_INTEGER sees none of them.\n * ponytail: a literal that underflows through 300+ leading fraction zeros and no exponent is not\n * gated; add a digit-count check if such files turn up.\n * @category Plugin helpers\n */\nexport const MAYBE_INEXACT_EXPONENT = new RegExp(\n [\n /\\d[eE]\\+?0*(?:1[5-9]|[2-9]\\d|[1-9]\\d{2,})/, // an exponent of 15 or more\n /\\d[eE]-0*(?:[3-9]\\d{2}|[1-9]\\d{3,})/, // an exponent of -300 or less\n /(?<![\\d.])(?=(?:\\d\\.?){16})\\d+\\.\\d+[eE]/, // 16 or more digits split by a decimal point\n ]\n .map((r) => r.source)\n .join(\"|\"),\n);\n\n/**\n * The prefix of the string a non-standard token or an exact integer is rewritten to (a NUL\n * character first, which no sensible attribute value starts with). A document that already holds\n * the text gets a numbered variant, so a genuine string is never revived as a number.\n */\nconst SENTINEL = `${String.fromCodePoint(0)}graph-io`;\n\n/** The non-standard tokens Python's json module writes, longest first so -Infinity wins over a bare minus. */\nconst NONSTANDARD_TOKENS: readonly (readonly [string, number])[] = [\n [\"-Infinity\", -Infinity],\n [\"Infinity\", Infinity],\n [\"NaN\", Number.NaN],\n];\n\n/** A JSON integer literal (no fraction, no exponent, no leading zero), as CANONICAL_INTEGER in common/ids.ts. */\nconst INTEGER_LITERAL = /^-?(0|[1-9][0-9]*)$/;\n\n/** A JSON number literal, split into sign, integer digits, fraction digits and exponent. */\nconst NUMBER_PARTS = /^(-?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$/;\n\n/** What rewriteNumbers() found. */\ninterface RewrittenNumbers {\n /** The rewritten text. */\n readonly text: string;\n /** The non-standard tokens seen (only when they were allowed). */\n readonly tokens: Set<string>;\n /** The integer literals beyond 2^53 that were quoted. */\n readonly bigIntegers: string[];\n /**\n * The literals with a fraction or an exponent that denote an integer beyond 2^53 a double cannot\n * hold (`9007199254740993.0`), and the finite literals that overflow to an infinity (`1e400`) or\n * underflow to 0 (`1e-400`):\n * read as the nearest double, which changes the value.\n */\n readonly inexact: string[];\n /** The JSON.parse reviver that turns the sentinel strings of this rewrite back into numbers. */\n readonly revive: (key: string, value: unknown) => unknown;\n /** The JSON.parse reviver that turns this rewrite's exact sentinels into ExactInteger. */\n readonly reviveExact: (key: string, value: unknown) => unknown;\n}\n\n/**\n * The sentinel prefix of one rewrite: SENTINEL with a colon, or a numbered variant when the document\n * already holds that one. A string can only decode to a NUL by escaping it (`\\u0000`), so when the\n * text holds that escape the escaped strings are decoded and searched too: any other escape in them\n * (`graph\\u002dio:`) would hide the sentinel from a search of the raw text.\n * @param text - the document text\n * @returns the prefix\n */\nfunction sentinelFor(text: string): string {\n let haystack = text;\n if (/\\\\u0000/i.test(text)) {\n const decoded: string[] = [];\n for (const [literal] of text.matchAll(/\"(?:[^\"\\\\]|\\\\.)*\"/g)) {\n if (literal.includes(\"\\\\\")) {\n decoded.push(decodeString(literal));\n }\n }\n haystack = `${decoded.join(\"\\n\")}\\n${text}`;\n }\n let k = 0;\n while (haystack.includes(`graph-io${k === 0 ? \"\" : String(k)}:`)) {\n k++;\n }\n return `${SENTINEL}${k === 0 ? \"\" : String(k)}:`;\n}\n\n/**\n * Decode one JSON string literal, or give it back as written when it is not valid (the parse then\n * reports the syntax error).\n * @param literal - the literal with its quotes\n * @returns the decoded value\n */\nfunction decodeString(literal: string): string {\n try {\n return String(JSON.parse(literal));\n } catch {\n return literal;\n }\n}\n\n/**\n * Whether a number literal with a fraction or an exponent reads back as another value: an integer\n * beyond 2^53 written with more significant digits than a double keeps, which the nearest double\n * misses, or a finite literal beyond the double range or a non-zero one below it.\n * @param literal - the literal as written\n * @returns true when the double read for it is not the value it denotes\n */\nfunction isInexactLiteral(literal: string): boolean {\n const value = Number(literal);\n if (!Number.isFinite(value)) {\n return true;\n }\n if (value === 0) {\n // a non-zero literal below the smallest double underflows to 0\n return /[1-9]/.test(literal.split(/[eE]/)[0]);\n }\n if (Number.isSafeInteger(value) || !Number.isInteger(value)) {\n return false;\n }\n const m = NUMBER_PARTS.exec(literal);\n if (m === null) {\n return false;\n }\n const digits = trimTrailingZeros(m[2] + (m[3] ?? \"\"));\n const exponent = Number(m[4] ?? \"0\") - (m[3] ?? \"\").length + ((m[2] + (m[3] ?? \"\")).length - digits.length);\n if (digits.replace(/^0+/, \"\").length <= 15 || exponent < 0) {\n // up to 15 significant digits the nearest double reads back as the same text (1e39 is the\n // float it says); a fraction that rounds to an integer is ordinary floating-point rounding\n return false;\n }\n const exact = BigInt(digits) * 10n ** BigInt(exponent);\n return exact !== BigInt(Math.abs(value));\n}\n\n/**\n * Rewrite the numbers JSON.parse cannot read exactly, outside strings and in value positions only:\n * NaN / Infinity / -Infinity become sentinel strings (when `nonstandard` is true), an integer\n * literal that is not a safe integer becomes a string of its digits (or, with `exactSentinel`, a\n * sentinel string the result's reviveExact turns into an ExactInteger). A container stack tells a\n * value position (after `:`, `[`, or `,` inside an array) from a key position, so `{NaN: 1}` stays\n * invalid. Literals with a fraction or an exponent that no double holds are listed, not rewritten.\n * @param text - the document text\n * @param options - which rewrites apply\n * @param options.nonstandard - rewrite NaN / Infinity / -Infinity (default true)\n * @param options.exactSentinel - quote big integers as sentinels instead of bare digits (default false)\n * @returns the rewritten text, what was found and the revivers of this rewrite\n * @category Plugin helpers\n */\nexport function rewriteNumbers(\n text: string,\n options: { readonly nonstandard?: boolean; readonly exactSentinel?: boolean } = {},\n): RewrittenNumbers {\n const nonstandard = options.nonstandard ?? true;\n const exactSentinel = options.exactSentinel ?? false;\n const sentinel = sentinelFor(text);\n const exactPrefix = `${sentinel}exact:`;\n const parts: string[] = [];\n const tokens = new Set<string>();\n const bigIntegers: string[] = [];\n const inexact: string[] = [];\n const arrays: boolean[] = [];\n let expectValue = true;\n let copied = 0;\n let i = 0;\n const n = text.length;\n while (i < n) {\n const ch = text[i];\n if (ch === '\"') {\n i++;\n while (i < n && text[i] !== '\"') {\n i += text[i] === \"\\\\\" ? 2 : 1;\n }\n i++;\n expectValue = false;\n continue;\n }\n if (ch === \"{\" || ch === \"[\") {\n arrays.push(ch === \"[\");\n expectValue = ch === \"[\";\n } else if (ch === \"}\" || ch === \"]\") {\n arrays.pop();\n expectValue = false;\n } else if (ch === \":\") {\n expectValue = true;\n } else if (ch === \",\") {\n expectValue = arrays.length > 0 && arrays[arrays.length - 1];\n } else if (expectValue && ch !== \" \" && ch !== \"\\t\" && ch !== \"\\n\" && ch !== \"\\r\") {\n expectValue = false;\n const token = nonstandard ? NONSTANDARD_TOKENS.find(([word]) => text.startsWith(word, i)) : undefined;\n let end = i;\n let replacement: string | null = null;\n if (token !== undefined) {\n end = i + token[0].length;\n tokens.add(token[0]);\n replacement = JSON.stringify(`${sentinel}${token[0]}`);\n } else if (ch === \"-\" || (ch >= \"0\" && ch <= \"9\")) {\n end = i + 1;\n while (end < n && \"0123456789+-.eE\".includes(text[end])) {\n end++;\n }\n const literal = text.slice(i, end);\n if (INTEGER_LITERAL.test(literal)) {\n if (!Number.isSafeInteger(Number(literal))) {\n bigIntegers.push(literal);\n replacement = exactSentinel ? JSON.stringify(`${exactPrefix}${literal}`) : `\"${literal}\"`;\n }\n } else if (isInexactLiteral(literal)) {\n inexact.push(literal);\n }\n }\n if (replacement !== null) {\n parts.push(text.slice(copied, i), replacement);\n copied = end;\n }\n i = Math.max(end, i + 1);\n continue;\n }\n i++;\n }\n parts.push(text.slice(copied));\n const revive = (_key: string, value: unknown): unknown => {\n if (typeof value === \"string\" && value.startsWith(sentinel)) {\n const found = NONSTANDARD_TOKENS.find(([word]) => word === value.slice(sentinel.length));\n return found === undefined ? value : found[1];\n }\n return value;\n };\n const reviveExact = (_key: string, value: unknown): unknown =>\n typeof value === \"string\" && value.startsWith(exactPrefix)\n ? new ExactInteger(value.slice(exactPrefix.length))\n : value;\n return { text: parts.join(\"\"), tokens, bigIntegers, inexact, revive, reviveExact };\n}\n\n/** One key repeated in a JSON object. */\ninterface DuplicateKey {\n /** The key. */\n readonly key: string;\n /** The UTF-16 offset of the repeated occurrence. */\n readonly offset: number;\n}\n\n/** Keys of one object compared pairwise up to this count; a wider object switches to a Set. */\nconst LINEAR_KEYS = 32;\n\n/**\n * The keys that occur twice in one object of a valid JSON text, which JSON.parse silently reduces\n * to the last value (RFC 8259 section 4: names SHOULD be unique). One pass over the characters\n * that jumps over strings; the keys of a small object are compared in place, without\n * slicing (the parse itself costs about as much as this scan, so it allocates nothing per key).\n * Keys are compared as written: `\"\\u0069d\"` and `\"id\"` are not recognized as one key.\n * @param text - a text JSON.parse accepted (or its rewrite)\n * @returns every repetition in document order\n * @category Plugin helpers\n */\nexport function findDuplicateKeys(text: string): DuplicateKey[] {\n const found: DuplicateKey[] = [];\n // per depth: the [start, end) of each key of the open object (reused), or -1 for an array\n const spans: number[][] = [];\n const wide: (Set<string> | null)[] = [];\n let depth = 0;\n const n = text.length;\n let at = 0;\n while (at < n) {\n const c = text.codePointAt(at);\n if (c === 123 || c === 91) {\n // { or [\n openContainer(spans, wide, depth, c === 91);\n depth++;\n } else if (c === 125 || c === 93) {\n depth--;\n } else if (c === 34) {\n const end = closingQuote(text, at + 1);\n const keys = depth > 0 ? spans[depth - 1] : null;\n // an array's span list starts with -1: its strings are values, never keys\n const isObjectKey = keys !== null && keys[0] !== -1 && isKey(text, end + 1);\n if (isObjectKey && isRepeated(text, at + 1, end, keys, wide, depth - 1)) {\n found.push({ key: text.slice(at + 1, end), offset: at });\n }\n at = end;\n }\n at++;\n }\n return found;\n}\n\n/**\n * Start the key list of a container just opened at a depth (reusing the list of an earlier one).\n * @param spans - the key spans per depth\n * @param wide - the key sets per depth, for wide objects\n * @param depth - the container's depth\n * @param isArray - whether it is an array, whose strings are never keys\n */\nfunction openContainer(spans: number[][], wide: (Set<string> | null)[], depth: number, isArray: boolean): void {\n if (spans.length === depth) {\n spans.push([]);\n wide.push(null);\n }\n spans[depth].length = 0;\n if (isArray) {\n spans[depth].push(-1);\n }\n wide[depth] = null;\n}\n\n/**\n * Whether a key repeats one already seen in its object, remembering it otherwise.\n * @param text - the text\n * @param start - the key's first character\n * @param end - the key's closing quote\n * @param keys - the spans of the object's keys so far\n * @param wide - the Set of each depth's keys once the object is wide\n * @param level - the object's depth\n * @returns true for a repetition\n */\nfunction isRepeated(\n text: string,\n start: number,\n end: number,\n keys: number[],\n wide: (Set<string> | null)[],\n level: number,\n): boolean {\n const set = wide[level];\n if (set !== null) {\n const key = text.slice(start, end);\n return set.has(key) || (set.add(key), false);\n }\n const length = end - start;\n for (let k = 0; k < keys.length; k += 2) {\n if (keys[k + 1] - keys[k] === length && sameText(text, keys[k], start, length)) {\n return true;\n }\n }\n keys.push(start, end);\n if (keys.length > 2 * LINEAR_KEYS) {\n const all = new Set<string>();\n for (let k = 0; k < keys.length; k += 2) {\n all.add(text.slice(keys[k], keys[k + 1]));\n }\n wide[level] = all;\n }\n return false;\n}\n\n/**\n * Whether two ranges of one text hold the same characters.\n * @param text - the text\n * @param a - the first range's start\n * @param b - the second range's start\n * @param length - the ranges' length\n * @returns true when equal\n */\nfunction sameText(text: string, a: number, b: number, length: number): boolean {\n for (let i = 0; i < length; i++) {\n if (text.codePointAt(a + i) !== text.codePointAt(b + i)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * The offset of the quote that closes a JSON string.\n * @param text - the text\n * @param from - the offset after the opening quote\n * @returns the closing quote's offset (the text length when there is none)\n */\nfunction closingQuote(text: string, from: number): number {\n for (let q = text.indexOf('\"', from); q >= 0; q = text.indexOf('\"', q + 1)) {\n let backslashes = 0;\n while (text.codePointAt(q - 1 - backslashes) === 92) {\n backslashes++;\n }\n if (backslashes % 2 === 0) {\n return q;\n }\n }\n return text.length;\n}\n\n/**\n * Whether the string that ends before an offset is an object key: a colon follows after whitespace.\n * @param text - the text\n * @param from - the offset after the closing quote\n * @returns true for a key\n */\nfunction isKey(text: string, from: number): boolean {\n let i = from;\n while (i < text.length && isSpace(text.codePointAt(i))) {\n i++;\n }\n return text.codePointAt(i) === 58;\n}\n\n/**\n * An integer literal beyond 2^53, kept as its exact digits (a CX id must never lose a digit).\n * @category Plugin helpers\n */\nexport class ExactInteger {\n /** The digits, with a leading minus for a negative value. */\n readonly digits: string;\n\n /**\n * Wrap the digits of an integer literal.\n * @param digits - the literal\n */\n constructor(digits: string) {\n this.digits = digits;\n }\n\n /**\n * The nearest number (JSON.stringify writes it as such).\n * @returns the number\n */\n toJSON(): number {\n return Number(this.digits);\n }\n}\n\n/**\n * Parse one JSON value, keeping integer literals beyond 2^53 as ExactInteger.\n * @param text - the JSON text\n * @returns the value and whether it may hold an ExactInteger; SyntaxError when it is not JSON\n * @category Plugin helpers\n */\nexport function parseExact(text: string): { readonly value: unknown; readonly exact: boolean } {\n if (!MAYBE_UNSAFE_INTEGER.test(text)) {\n return { value: JSON.parse(text) as unknown, exact: false };\n }\n const scan = rewriteNumbers(text, { nonstandard: false, exactSentinel: true });\n if (scan.bigIntegers.length === 0) {\n return { value: JSON.parse(text) as unknown, exact: false };\n }\n return { value: JSON.parse(scan.text, scan.reviveExact) as unknown, exact: true };\n}\n\n/**\n * A parsed value with every ExactInteger replaced by its nearest number, for metadata and for\n * values that are not ids.\n * @param value - the value\n * @param onPrecision - called with the digits of each integer that lost precision\n * @returns the plain JSON value\n * @category Plugin helpers\n */\nexport function plainJson(value: unknown, onPrecision?: (digits: string) => void): unknown {\n if (value instanceof ExactInteger) {\n onPrecision?.(value.digits);\n return Number(value.digits);\n }\n if (Array.isArray(value)) {\n return value.map((item) => plainJson(item, onPrecision));\n }\n if (typeof value === \"object\" && value !== null) {\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n setOwn(out, key, plainJson(item, onPrecision));\n }\n return out;\n }\n return value;\n}\n\n/**\n * Set an own property, also for the key `__proto__` (which an assignment would turn into the\n * object's prototype, losing the entry).\n * @param target - the object\n * @param key - the key\n * @param value - the value\n * @category Plugin helpers\n */\nexport function setOwn(target: Record<string, unknown>, key: string, value: unknown): void {\n if (key === \"__proto__\") {\n Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });\n } else {\n target[key] = value;\n }\n}\n\n/**\n * Parse a text with the bare tokens NaN / Infinity / -Infinity that Python's json writes.\n * @param text - the JSON text that JSON.parse refused\n * @returns the value, whether it may hold an ExactInteger and the tokens seen; null when the\n * tokens are not what made the text invalid\n */\nfunction parseNonstandard(\n text: string,\n): { readonly value: unknown; readonly exact: boolean; readonly tokens: ReadonlySet<string> } | null {\n if (!/NaN|Infinity/.test(text)) {\n return null;\n }\n const scan = rewriteNumbers(text, { nonstandard: true, exactSentinel: true });\n if (scan.tokens.size === 0) {\n return null;\n }\n try {\n const value = JSON.parse(scan.text, (key, raw) => scan.reviveExact(key, scan.revive(key, raw))) as unknown;\n return { value, exact: scan.bigIntegers.length > 0, tokens: scan.tokens };\n } catch {\n return null;\n }\n}\n\n/** A lone surrogate code unit: a high one not followed by a low one, or a low one not after a high one. */\n/** The escapes of the surrogate range, as a message writes them. */\nconst SURROGATE_ESCAPES = String.raw`\\uD800-\\uDFFF`;\n\nconst LONE_SURROGATE = /[\\ud800-\\udbff](?![\\udc00-\\udfff])|(?<![\\ud800-\\udbff])[\\udc00-\\udfff]/g;\n\n/** Text that may hold a surrogate: an escaped one, or a raw one (string input). */\nconst MAYBE_SURROGATE = /\\\\u[dD][89a-fA-F]|[\\ud800-\\udfff]/;\n\n/**\n * Replace every lone surrogate of a parsed value's strings and keys with U+FFFD, in place.\n * @param value - the parsed value\n * @param fixed - counts the strings repaired and names the keys two repaired keys collapse into\n * @param fixed.count - the count\n * @param fixed.collisions - a key one object holds twice once repaired (the later value is kept)\n * @returns the value (a string, or an object with a repaired key, is returned as a new value)\n */\nfunction repairSurrogates(value: unknown, fixed: { count: number; collisions: string[] }): unknown {\n if (typeof value === \"string\") {\n const repaired = value.replaceAll(LONE_SURROGATE, \"\\ufffd\");\n if (repaired !== value) {\n fixed.count++;\n }\n return repaired;\n }\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n value[i] = repairSurrogates(value[i], fixed);\n }\n return value;\n }\n if (isRecord(value)) {\n const keys = Object.keys(value);\n const repairedKeys = keys.map((key) => key.replaceAll(LONE_SURROGATE, \"\\ufffd\"));\n const renamed = repairedKeys.some((key, i) => key !== keys[i]);\n const out: Record<string, unknown> = renamed ? {} : value;\n keys.forEach((key, i) => {\n if (repairedKeys[i] !== key) {\n fixed.count++;\n }\n if (renamed && Object.hasOwn(out, repairedKeys[i])) {\n fixed.collisions.push(repairedKeys[i]);\n }\n setOwn(out, repairedKeys[i], repairSurrogates(value[key], fixed));\n });\n return out;\n }\n return value;\n}\n\n/**\n * The number of keys of a parsed value, at every depth.\n * @param value - the value\n * @returns the count\n */\nfunction keyCount(value: unknown): number {\n if (Array.isArray(value)) {\n return value.reduce<number>((n, item) => n + keyCount(item), 0);\n }\n if (isRecord(value)) {\n return Object.keys(value).reduce((n, key) => n + 1 + keyCount(value[key]), 0);\n }\n return 0;\n}\n\n/**\n * The first key an object of a JSON text holds twice (JSON.parse keeps the later value silently).\n * @param text - valid JSON text\n * @returns the key, or null\n */\nfunction duplicateKey(text: string): string | null {\n const stack: (Set<string> | null)[] = [];\n let i = 0;\n while (i < text.length) {\n const c = text[i];\n if (c === \"{\") {\n stack.push(new Set());\n } else if (c === \"[\") {\n stack.push(null);\n } else if (c === \"}\" || c === \"]\") {\n stack.pop();\n } else if (c === '\"') {\n const start = i;\n i = stringEnd(text, i + 1);\n const keys = stack.at(-1) ?? null;\n if (keys !== null && text[skipSpace(text, i + 1)] === \":\") {\n const key = JSON.parse(text.slice(start, i + 1)) as string;\n if (keys.has(key)) {\n return key;\n }\n keys.add(key);\n }\n }\n i++;\n }\n return null;\n}\n\n/**\n * The offset of the quote that ends a JSON string, skipping escaped characters.\n * @param text - the text\n * @param from - the offset after the opening quote\n * @returns the closing quote's offset, or the text length (or one past it) when there is none\n */\nfunction stringEnd(text: string, from: number): number {\n let i = from;\n while (i < text.length && text[i] !== '\"') {\n i += text[i] === \"\\\\\" ? 2 : 1;\n }\n return i;\n}\n\n/**\n * The index of the first character at or after `from` that is not JSON whitespace.\n * @param text - the text\n * @param from - the first index\n * @returns the index (text.length at the end)\n */\nfunction skipSpace(text: string, from: number): number {\n let i = from;\n while (i < text.length && isSpace(text.codePointAt(i))) {\n i++;\n }\n return i;\n}\n\n/** A key followed by its colon, for the cheap duplicate-key check. */\nconst KEY_COLON = /\"\\s*:/g;\n\n/**\n * The checks of a parsed element that JSON.parse cannot make, recorded in the report: lone\n * surrogates (replaced with U+FFFD, E_BAD_VALUE) and a key held twice by one object\n * (W_DUPLICATE_ATTRIBUTE; the later value is the one read, also for two keys that a repair makes one).\n * @param text - the element text\n * @param parsed - the parsed element\n * @param line - its line\n * @param report - the report\n * @returns the value, repaired\n */\nfunction checkParsed(text: string, parsed: unknown, line: number, report: ImportReportBuilder): unknown {\n let value = parsed;\n if (MAYBE_SURROGATE.test(text)) {\n const fixed = { count: 0, collisions: [] as string[] };\n value = repairSurrogates(value, fixed);\n for (const key of fixed.collisions) {\n report.warnOnce(\n \"validation-error\",\n DUPLICATE_ATTRIBUTE_CODE,\n `an object holds two keys that are the same key ${JSON.stringify(key)} once their lone surrogates are replaced with U+FFFD; the later value is read`,\n { line, element: key },\n `${DUPLICATE_ATTRIBUTE_CODE}:json:${key}`,\n );\n }\n if (fixed.count > 0) {\n report.error(\n \"validation-error\",\n BAD_VALUE_CODE,\n `${fixed.count} string${plural(fixed.count)} ${agree(fixed.count, \"holds\", \"hold\")} a lone surrogate (an unpaired ${SURROGATE_ESCAPES} escape); each is read with U+FFFD in its place`,\n { line },\n );\n }\n }\n if (typeof value === \"object\" && value !== null && (text.match(KEY_COLON)?.length ?? 0) > keyCount(value)) {\n const key = duplicateKey(text);\n if (key !== null) {\n report.warnOnce(\n \"validation-error\",\n DUPLICATE_ATTRIBUTE_CODE,\n `an object holds the key ${JSON.stringify(key)} twice; the later value is read`,\n { line, element: key },\n `${DUPLICATE_ATTRIBUTE_CODE}:json:${key}`,\n );\n }\n }\n return value;\n}\n\n// ============================================================ the streaming aspect scanner\n\n/**\n * A problem with the document's JSON; the importer turns it into a fatal E_SYNTAX or E_EMPTY_INPUT.\n * @category Plugin helpers\n */\nexport class JsonScanError extends Error {\n /** The 1-based line of the problem. */\n readonly line: number;\n\n /** Whether the document holds nothing but whitespace. */\n readonly empty: boolean;\n\n /**\n * Create the error.\n * @param message - what is wrong\n * @param line - the 1-based line\n * @param empty - whether the input is empty\n */\n constructor(message: string, line: number, empty = false) {\n super(message);\n this.name = \"JsonScanError\";\n this.line = line;\n this.empty = empty;\n }\n}\n\n/**\n * One thing the scanner found in a CX document.\n * @category Plugin helpers\n */\nexport type AspectEvent =\n | {\n /**\n * A top-level value that is not an array: a scalar document, parsed; an object is not\n * read at all (object true, value undefined), however large it is.\n */\n readonly kind: \"root\";\n readonly value: unknown;\n readonly object: boolean;\n readonly line: number;\n }\n | {\n /** A member `{\"<aspect>\": [` whose elements follow one by one. */\n readonly kind: \"block\";\n readonly aspect: string;\n readonly block: number;\n /** Whether the aspect is not the member's first key (`{\"nodes\": [...], \"edges\": [...]}`). */\n readonly shared: boolean;\n readonly line: number;\n }\n | {\n /** One element of the current block, parsed. */\n readonly kind: \"element\";\n readonly aspect: string;\n readonly block: number;\n readonly value: unknown;\n /** Whether the value may hold an ExactInteger. */\n readonly exact: boolean;\n /** The element's JSON text (for lexical checks of its ids). */\n readonly text: string;\n readonly line: number;\n }\n | {\n /** The keys of a block member whose values are not arrays (`{\"nodes\": [...], \"x\": 1}`, in any order); skipped. */\n readonly kind: \"extraKeys\";\n readonly aspect: string;\n readonly block: number;\n readonly keys: readonly string[];\n readonly line: number;\n }\n | {\n /**\n * An element, or a member, nested deeper than MAX_ELEMENT_DEPTH: not parsed, so nothing\n * downstream recurses into it (aspect: the block's aspect, null for a member).\n */\n readonly kind: \"deep\";\n readonly aspect: string | null;\n readonly block: number;\n readonly depth: number;\n readonly line: number;\n }\n | {\n /** Any other member of the top-level array (a CX2 descriptor, `{\"ndexStatus\": {...}}`, `1`), parsed. */\n readonly kind: \"member\";\n readonly block: number;\n readonly value: unknown;\n readonly exact: boolean;\n /** The member's JSON text (for lexical checks of its ids). */\n readonly text: string;\n readonly line: number;\n };\n\n/**\n * Whether a character code is whitespace to JSON.\n * @param c - the character code\n * @returns true for space, line feed, carriage return and tab\n */\nconst isSpace = (c: number | undefined): boolean => c === 32 || c === 10 || c === 13 || c === 9;\n\n/**\n * Whether a character code ends a bare scalar (number, true, false, null).\n * @param c - the character code\n * @returns true for whitespace, a comma, a closing bracket or brace, or a colon\n */\nconst endsScalar = (c: number): boolean => isSpace(c) || c === 44 || c === 93 || c === 125 || c === 58;\n\nconst QUOTE_OR_BACKSLASH = /[\"\\\\]/g;\n\n/**\n * The deepest nesting of an element the scanner parses. graph-format refuses JSON values (a json\n * cell, meta.extra) nested deeper than 256 levels, and a kept element sits a few levels down\n * meta.extra; a deeper element is reported instead, before anything recurses into it.\n */\nconst MAX_ELEMENT_DEPTH = 200;\n\n/**\n * A cursor over text chunks with capture: the text of the value being read is kept across chunk\n * boundaries as a list of parts, so a long element costs linear time.\n */\nclass ChunkCursor {\n private readonly iterator: AsyncIterator<string>;\n\n private buf = \"\";\n\n /** The read position in buf. */\n pos = 0;\n\n /** The line number of buf's first character. */\n private linesBefore = 1;\n\n private parts: string[] | null = null;\n\n private captureStart = 0;\n\n /** The deepest nesting the last skipValue() reached. */\n deepest = 0;\n\n /**\n * Wrap text chunks.\n * @param chunks - the decoded text\n */\n constructor(chunks: AsyncIterable<string>) {\n this.iterator = chunks[Symbol.asyncIterator]();\n }\n\n /**\n * Read the next chunk once the current one is consumed.\n * @returns false at the end of the input\n */\n async fill(): Promise<boolean> {\n if (this.parts !== null) {\n this.parts.push(this.buf.slice(this.captureStart));\n this.captureStart = 0;\n }\n this.linesBefore += countNewlines(this.buf, 0, this.buf.length);\n const endsWithCr = this.buf.endsWith(\"\\r\");\n for (;;) {\n const next = await this.iterator.next();\n if (next.done === true) {\n this.buf = \"\";\n this.pos = 0;\n return false;\n }\n if (next.value.length > 0) {\n if (endsWithCr && next.value.startsWith(\"\\n\")) {\n // a CR LF split across two chunks is one line break, counted with the CR\n this.linesBefore--;\n }\n this.buf = next.value;\n this.pos = 0;\n return true;\n }\n }\n }\n\n /**\n * The 1-based line of the read position.\n * @returns the line\n */\n line(): number {\n return this.linesBefore + countNewlines(this.buf, 0, Math.min(this.pos, this.buf.length));\n }\n\n /**\n * The text from the read position to the end of the current chunk, at most `length` characters.\n * @param length - the most characters\n * @returns the text\n */\n ahead(length: number): string {\n return this.buf.slice(this.pos, this.pos + length);\n }\n\n /**\n * Skip whitespace and return the next character without consuming it.\n * @returns the character, or \"\" at the end of the input\n */\n async peek(): Promise<string> {\n for (;;) {\n while (this.pos < this.buf.length && isSpace(this.buf.charCodeAt(this.pos))) {\n this.pos++;\n }\n if (this.pos < this.buf.length) {\n return this.buf[this.pos];\n }\n if (!(await this.fill())) {\n return \"\";\n }\n }\n }\n\n /** Start keeping the text from the read position. */\n beginCapture(): void {\n this.parts = [];\n this.captureStart = this.pos;\n }\n\n /**\n * Stop keeping text.\n * @returns the text kept since beginCapture()\n */\n endCapture(): string {\n const parts = this.parts ?? [];\n parts.push(this.buf.slice(this.captureStart, this.pos));\n this.parts = null;\n return parts.length === 1 ? parts[0] : parts.join(\"\");\n }\n\n /** Drop the capture without building its text. */\n dropCapture(): void {\n this.parts = null;\n }\n\n /**\n * Advance past one JSON value starting at the read position (which holds its first\n * character), tracking strings and nesting only; the value is validated when it is parsed.\n * @param startDepth - the nesting depth already entered (1 when the opening brace was consumed)\n */\n async skipValue(startDepth = 0): Promise<void> {\n let depth = startDepth;\n this.deepest = depth;\n if (depth === 0) {\n const c = this.buf.charCodeAt(this.pos);\n if (c === 34) {\n this.pos++;\n await this.skipString();\n return;\n }\n if (c !== 123 && c !== 91) {\n await this.skipScalar();\n return;\n }\n }\n for (;;) {\n if (this.pos >= this.buf.length && !(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a value\", this.line());\n }\n const c = this.buf.charCodeAt(this.pos);\n this.pos++;\n if (c === 34) {\n await this.skipString();\n } else if (c === 123 || c === 91) {\n depth++;\n if (depth > this.deepest) {\n this.deepest = depth;\n }\n } else if (c === 125 || c === 93) {\n depth--;\n if (depth === 0) {\n return;\n }\n }\n }\n }\n\n /** Advance past the rest of a string whose opening quote was consumed. */\n private async skipString(): Promise<void> {\n for (;;) {\n QUOTE_OR_BACKSLASH.lastIndex = this.pos;\n const found = QUOTE_OR_BACKSLASH.exec(this.buf);\n if (found === null) {\n this.pos = this.buf.length;\n if (!(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a string\", this.line());\n }\n continue;\n }\n this.pos = found.index + 1;\n if (found[0] === '\"') {\n return;\n }\n // a backslash: skip the escaped character, which may be in the next chunk\n if (this.pos >= this.buf.length && !(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a string\", this.line());\n }\n this.pos++;\n }\n }\n\n /** Advance past a bare scalar (a number, true, false, null or a stray word). */\n private async skipScalar(): Promise<void> {\n for (;;) {\n while (this.pos < this.buf.length && !endsScalar(this.buf.charCodeAt(this.pos))) {\n this.pos++;\n }\n if (this.pos < this.buf.length || !(await this.fill())) {\n return;\n }\n }\n }\n\n /**\n * Read one JSON string literal at the read position (its opening quote).\n * @returns the decoded string\n */\n async readString(): Promise<string> {\n this.beginCapture();\n this.pos++;\n await this.skipString();\n const literal = this.endCapture();\n try {\n return JSON.parse(literal) as string;\n } catch (err) {\n throw new JsonScanError(`invalid string: ${messageOf(err)}`, this.line());\n }\n }\n}\n\n/**\n * The number of line breaks in a slice of a string: LF, CR LF and a CR alone (old Mac files).\n * @param text - the string\n * @param from - the first index\n * @param to - one past the last index\n * @returns the count\n */\nfunction countNewlines(text: string, from: number, to: number): number {\n let count = 0;\n let i = text.indexOf(\"\\n\", from);\n while (i >= 0 && i < to) {\n count++;\n i = text.indexOf(\"\\n\", i + 1);\n }\n i = text.indexOf(\"\\r\", from);\n while (i >= 0 && i < to) {\n if (text[i + 1] !== \"\\n\") {\n count++;\n }\n i = text.indexOf(\"\\r\", i + 1);\n }\n return count;\n}\n\n/**\n * The message of a caught value.\n * @param err - the thrown value\n * @returns the message\n */\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Parse a captured value, turning a parse failure into a JsonScanError at its line. With a report,\n * the bare tokens NaN / Infinity / -Infinity are read as numbers (W_JSON_NONSTANDARD_NUMBER), and\n * lone surrogates and repeated keys are recorded (checkParsed()).\n * @param text - the value text\n * @param line - the line the value starts on\n * @param report - the report, or null to only parse\n * @returns the value and whether it may hold an ExactInteger\n */\nfunction parseAt(\n text: string,\n line: number,\n report: ImportReportBuilder | null,\n): { readonly value: unknown; readonly exact: boolean } {\n let parsed: { readonly value: unknown; readonly exact: boolean };\n try {\n parsed = parseExact(text);\n } catch (err) {\n const recovered = report === null ? null : parseNonstandard(text);\n if (recovered === null) {\n throw new JsonScanError(`invalid JSON: ${messageOf(err)}`, line);\n }\n report?.warnOnce(\n \"coercion\",\n JSON_NONSTANDARD_NUMBER_CODE,\n `the document uses the non-standard token${plural(recovered.tokens.size)} ${[...recovered.tokens].join(\", \")}, which strict JSON does not allow; read as numbers`,\n { line },\n );\n parsed = recovered;\n }\n if (report !== null) {\n parsed = { ...parsed, value: checkParsed(text, parsed.value, line, report) };\n }\n return parsed;\n}\n\n/**\n * What a document that does not start with \"[\" looks like, for the syntax error: HTML or XML (an\n * error page saved under the file's name), a ZIP archive, or nothing in particular.\n * @param head - the first characters\n * @returns a sentence to append, or \"\"\n */\nfunction notJsonHint(head: string): string {\n if (head.startsWith(\"<\")) {\n return \"; the input looks like HTML or XML (an error page saved as the file?), not JSON\";\n }\n if (head.startsWith(\"PK\\u0003\\u0004\")) {\n return \"; the input is a ZIP archive (a Cytoscape session .cys is read by the cys importer), not JSON\";\n }\n return \"\";\n}\n\n/**\n * Walk a CX document (CX1 or CX2): a top-level array of members, each normally a one-key object\n * `{\"<aspect>\": [elements]}`. The elements of such a block are parsed and yielded one at a time;\n * any other member is parsed whole and yielded as a \"member\". A document that is not an array is\n * yielded as one \"root\" event (an object without reading it). Syntax errors throw JsonScanError.\n * @param chunks - the decoded text\n * @param report - where NaN tokens, lone surrogates and repeated keys are recorded (parseAt()); null to only parse\n * @yields the events in document order\n * @returns nothing\n */\nexport async function* scanAspects(\n chunks: AsyncIterable<string>,\n report: ImportReportBuilder | null = null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n const cur = new ChunkCursor(chunks);\n const first = await cur.peek();\n if (first === \"\") {\n throw new JsonScanError(\"the input is empty\", 1, true);\n }\n const hint = notJsonHint(cur.ahead(16));\n if (hint === \"\" && cur.ahead(8).includes(String.fromCodePoint(0))) {\n throw new JsonScanError(\n 'the input holds NUL characters: UTF-16 without a byte order mark (pass the encoding option, such as \"utf-16le\"), or binary data; not JSON',\n cur.line(),\n );\n }\n if (first === \"[\") {\n cur.pos++;\n yield* members(cur, report);\n return;\n }\n const line = cur.line();\n if (first === \"{\") {\n yield { kind: \"root\", value: undefined, object: true, line };\n return;\n }\n yield { kind: \"root\", value: await scalarRoot(cur, line, hint), object: false, line };\n}\n\n/**\n * Read a document that is neither an array nor an object: one value, and nothing after it.\n * @param cur - the cursor at the value\n * @param line - the value's line\n * @param hint - what the input looks like when it is not JSON, for the error\n * @returns the value\n */\nasync function scalarRoot(cur: ChunkCursor, line: number, hint: string): Promise<unknown> {\n cur.beginCapture();\n await cur.skipValue();\n let value: unknown;\n try {\n ({ value } = parseAt(cur.endCapture(), line, null));\n } catch (err) {\n if (err instanceof JsonScanError && hint !== \"\") {\n throw new JsonScanError(`${err.message}${hint}`, err.line);\n }\n throw err;\n }\n if ((await cur.peek()) !== \"\") {\n throw new JsonScanError(\"unexpected text after the document\", cur.line());\n }\n return value;\n}\n\n/**\n * Read the members of the top-level array, and check that nothing follows it.\n * @param cur - the cursor after the opening bracket\n * @param report - the report of parseAt(), or null\n * @yields the members' events\n * @returns nothing\n */\nasync function* members(\n cur: ChunkCursor,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n let block = 0;\n let next = await cur.peek();\n if (next === \"]\") {\n cur.pos++;\n }\n while (next !== \"]\") {\n yield* member(cur, block, report);\n block++;\n next = await cur.peek();\n if (next !== \",\" && next !== \"]\") {\n throw new JsonScanError(\n next === \"\" ? \"the document ends before its closing bracket\" : `expected \",\" or \"]\", found \"${next}\"`,\n cur.line(),\n );\n }\n cur.pos++;\n }\n if ((await cur.peek()) !== \"\") {\n throw new JsonScanError(\"unexpected text after the closing bracket\", cur.line());\n }\n}\n\n/**\n * Read one member of the top-level array.\n * @param cur - the cursor at the member's first character\n * @param block - the member's position among the members\n * @param report - the report of parseAt(), or null\n * @yields the member's events\n * @returns nothing\n */\nasync function* member(\n cur: ChunkCursor,\n block: number,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n const c = await cur.peek();\n const line = cur.line();\n if (c === \"\" || c === \"]\" || c === \",\") {\n throw new JsonScanError(c === \"\" ? \"the document ends inside its array\" : `unexpected \"${c}\"`, line);\n }\n cur.beginCapture();\n if (c !== \"{\") {\n await cur.skipValue();\n yield wholeMember(cur, block, line, report);\n return;\n }\n cur.pos++;\n if ((await cur.peek()) !== '\"') {\n // `{}` or not JSON: parse the whole member so a syntax error is reported as such\n await cur.skipValue(1);\n yield wholeMember(cur, block, line, report);\n return;\n }\n // every array-valued key is a block whose elements stream, whatever the key order; any other\n // value is kept as text, so a member without an array is parsed whole (a CX2 descriptor, a\n // one-object aspect) and one with an array names its other keys (extraKeys), skipped\n const outer = cur.endCapture();\n const firstLine = cur.line();\n const keys: MemberKeys = { others: [], firstBlock: null, deep: 0 };\n let next = \",\";\n let index = 0;\n while (next === \",\") {\n const key = await readKey(cur);\n yield* keyValue(cur, key, index, block, keys, report);\n index++;\n next = await cur.peek();\n if (next !== \",\" && next !== \"}\") {\n throw new JsonScanError(\n next === \"\" ? \"the document ends inside a member\" : `expected \",\" or \"}\" after the key \"${key.name}\"`,\n cur.line(),\n );\n }\n cur.pos++;\n }\n yield* memberEnd(keys, block, line, { outer, firstLine }, report);\n}\n\n/** What one-object member's keys held: the non-array values as text, the first block, the deepest value. */\ninterface MemberKeys {\n readonly others: { key: string; text: string | null; line: number }[];\n firstBlock: string | null;\n deep: number;\n}\n\n/**\n * The event of a member read whole (its capture already taken): the member, or \"deep\" when it\n * nests too deep to parse.\n * @param cur - the cursor after the member\n * @param block - the member's position among the members\n * @param line - the member's line\n * @param report - the report of parseAt(), or null\n * @returns the event\n */\nfunction wholeMember(cur: ChunkCursor, block: number, line: number, report: ImportReportBuilder | null): AspectEvent {\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n return { kind: \"deep\", aspect: null, block, depth: cur.deepest, line };\n }\n const text = cur.endCapture();\n const parsed = parseAt(text, line, report);\n return { kind: \"member\", block, value: parsed.value, exact: parsed.exact, text, line };\n}\n\n/**\n * Read a member's key and the colon after it.\n * @param cur - the cursor at the key\n * @returns the key and its line\n */\nasync function readKey(cur: ChunkCursor): Promise<{ name: string; line: number }> {\n if ((await cur.peek()) !== '\"') {\n throw new JsonScanError(\"expected a key\", cur.line());\n }\n const line = cur.line();\n const name = await cur.readString();\n if ((await cur.peek()) !== \":\") {\n throw new JsonScanError(`expected \":\" after the key \"${name}\"`, cur.line());\n }\n cur.pos++;\n return { name, line };\n}\n\n/**\n * Read one key's value: an array is a block whose elements stream; any other value is kept as text\n * (or, when it nests too deep to parse, only its depth).\n * @param cur - the cursor at the value\n * @param key - the key\n * @param key.name - its name\n * @param key.line - its line\n * @param index - the key's position in the member\n * @param block - the member's position among the members\n * @param keys - what the member's keys held so far\n * @param report - the report of parseAt(), or null\n * @yields the block's events\n * @returns nothing\n */\nasync function* keyValue(\n cur: ChunkCursor,\n key: { name: string; line: number },\n index: number,\n block: number,\n keys: MemberKeys,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n if ((await cur.peek()) === \"[\") {\n cur.pos++;\n yield { kind: \"block\", aspect: key.name, block, shared: index > 0, line: key.line };\n keys.firstBlock ??= key.name;\n yield* blockElements(cur, key.name, block, report);\n return;\n }\n cur.beginCapture();\n await cur.skipValue();\n // the member's own brace is one level more\n if (cur.deepest + 1 > MAX_ELEMENT_DEPTH) {\n // never parse (recursively) what no one reads\n cur.dropCapture();\n keys.deep = Math.max(keys.deep, cur.deepest + 1);\n keys.others.push({ key: key.name, text: null, line: key.line });\n } else {\n keys.others.push({ key: key.name, text: cur.endCapture(), line: key.line });\n }\n}\n\n/**\n * The events after a one-object member's keys: with a block, its other keys (checked, then named\n * as extraKeys); without one, the member parsed whole from its kept text.\n * @param keys - what the member's keys held\n * @param block - the member's position among the members\n * @param line - the member's line\n * @param start - the member's opening text and the line after it\n * @param start.outer - the text up to the first key\n * @param start.firstLine - the line of the first key\n * @param report - the report of parseAt(), or null\n * @yields the events\n * @returns nothing\n */\nfunction* memberEnd(\n keys: MemberKeys,\n block: number,\n line: number,\n start: { outer: string; firstLine: number },\n report: ImportReportBuilder | null,\n): Generator<AspectEvent, void, undefined> {\n const { others, firstBlock, deep } = keys;\n if (firstBlock !== null) {\n for (const other of others) {\n if (other.text !== null) {\n parseAt(other.text, other.line, null);\n }\n }\n if (others.length > 0) {\n yield {\n kind: \"extraKeys\",\n aspect: firstBlock,\n block,\n keys: others.map((o) => o.key),\n line: others[0].line,\n };\n }\n return;\n }\n if (deep > 0) {\n yield { kind: \"deep\", aspect: others[0].key, block, depth: deep, line };\n return;\n }\n const members = others.map((o) => [JSON.stringify(o.key), o.text ?? \"\"].join(\":\"));\n const text = `${start.outer}${members.join(\",\")}}`;\n const parsed = parseAt(text, start.firstLine, report);\n yield { kind: \"member\", block, value: parsed.value, exact: parsed.exact, text, line };\n}\n\n/**\n * Read the elements of a block whose opening bracket was consumed, through its closing bracket.\n * @param cur - the cursor after the opening bracket\n * @param aspect - the block's aspect\n * @param block - the member's position among the members\n * @param report - the report of parseAt(), or null\n * @yields an element (or deep) event per element\n * @returns nothing\n */\nasync function* blockElements(\n cur: ChunkCursor,\n aspect: string,\n block: number,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n if ((await cur.peek()) === \"]\") {\n cur.pos++;\n return;\n }\n for (;;) {\n const e = await cur.peek();\n if (e === \"\") {\n throw new JsonScanError(`the document ends inside the \"${aspect}\" block`, cur.line());\n }\n const elementLine = cur.line();\n cur.beginCapture();\n await cur.skipValue();\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n yield { kind: \"deep\", aspect, block, depth: cur.deepest, line: elementLine };\n } else {\n const text = cur.endCapture();\n const parsed = parseAt(text, elementLine, report);\n yield {\n kind: \"element\",\n aspect,\n block,\n value: parsed.value,\n exact: parsed.exact,\n text,\n line: elementLine,\n };\n }\n const next = await cur.peek();\n if (next === \",\") {\n cur.pos++;\n continue;\n }\n if (next === \"]\") {\n cur.pos++;\n return;\n }\n throw new JsonScanError(\n next === \"\"\n ? `the document ends inside the \"${aspect}\" block`\n : `expected \",\" or \"]\" in the \"${aspect}\" block, found \"${next}\"`,\n cur.line(),\n );\n }\n}\n\n/**\n * Report an element or member the scanner refused for its depth (E_BAD_ASPECT_BLOCK; skipped).\n * @param report - the report\n * @param event - the \"deep\" event\n * @category Plugin helpers\n */\nexport function reportTooDeep(report: ImportReportBuilder, event: Extract<AspectEvent, { kind: \"deep\" }>): void {\n report.error(\n \"parse-error\",\n BAD_ASPECT_BLOCK_CODE,\n `${event.aspect === null ? \"a member of the document\" : `an element of \"${event.aspect}\"`} is nested ${event.depth} levels deep, more than the ${MAX_ELEMENT_DEPTH} a graph keeps; skipped`,\n { line: event.line, element: event.aspect },\n );\n}\n\n/**\n * The precision callback of plainJson() for an aspect kept verbatim in the metadata: W_PRECISION\n * once, since the digits stored there are not the file's.\n * @param report - the report\n * @param where - where the aspect is kept (meta.extra.cx, ...)\n * @returns the callback\n * @category Plugin helpers\n */\nexport function keptPrecision(report: ImportReportBuilder, where: string): (digits: string) => void {\n return (digits) => {\n report.warnOnce(\n \"precision\",\n PRECISION_CODE,\n `an aspect kept in ${where} holds ${digits}, beyond 2^53; it is kept as the nearest double`,\n { element: where },\n `${PRECISION_CODE}:kept`,\n );\n };\n}\n\n/**\n * Warn once (W_MULTI_ASPECT_FRAGMENT) about a block that shares its array member with another\n * aspect (`{\"nodes\": [...], \"edges\": [...]}`); a block of its own needs nothing.\n * @param report - the report\n * @param event - the \"block\" event\n * @category Plugin helpers\n */\nexport function reportSharedBlock(report: ImportReportBuilder, event: Extract<AspectEvent, { kind: \"block\" }>): void {\n if (event.shared) {\n report.warnOnce(\n \"coercion\",\n MULTI_ASPECT_FRAGMENT_CODE,\n `the \"${event.aspect}\" aspect shares an array member with another aspect; each is read as its own fragment`,\n { line: event.line, element: event.aspect },\n );\n }\n}\n\n// ============================================================ the CX id rule (design section 1.0.2)\n\n/** How an id was spelled, when it was not a plain safe integer. */\ntype CxIdNote = \"text\" | \"precision\" | null;\n\n/** A CX id, decided per id: a safe integer is a number, a larger one its exact digits. */\ninterface CxId {\n /** The node or edge id. */\n readonly id: NodeId;\n /** \"text\": spelled as a string or a non-integer literal (W_ID_TEXT_TYPE); \"precision\": beyond 2^53 (W_PRECISION). */\n readonly note: CxIdNote;\n}\n\nconst DECIMAL_INTEGER_TEXT = /^-?(0|[1-9][0-9]*)$/;\n\n/**\n * Apply the CX id rule (design section 1.0.2) to a parsed value: a safe integer is the id; an\n * integer beyond 2^53 is its exact digit string; a string spelling a decimal integer is that\n * integer (as Jackson coerces it); anything else is E_INVALID_ID.\n * @param raw - the parsed value\n * @param inexactLiteral - whether the number was written as a non-integer literal (`5.0`, `1e3`)\n * @returns the id and how it was spelled\n * @category Plugin helpers\n */\nexport function cxId(raw: unknown, inexactLiteral = false): CxId {\n if (raw instanceof ExactInteger) {\n return { id: raw.digits, note: \"precision\" };\n }\n if (typeof raw === \"number\") {\n if (Number.isSafeInteger(raw)) {\n return { id: raw === 0 ? 0 : raw, note: inexactLiteral ? \"text\" : null };\n }\n throw invalidCxId(raw, Number.isInteger(raw) ? \"an integer beyond 2^53 written inexactly\" : \"not an integer\");\n }\n if (typeof raw === \"string\" && raw !== \"-0\" && DECIMAL_INTEGER_TEXT.test(raw)) {\n const n = Number(raw);\n return Number.isSafeInteger(n) ? { id: n, note: \"text\" } : { id: raw, note: \"precision\" };\n }\n throw invalidCxId(raw, \"a CX id is an integer\");\n}\n\n/**\n * The E_INVALID_ID error of a rejected CX id.\n * @param raw - the value\n * @param reason - why\n * @returns the error\n */\nfunction invalidCxId(raw: unknown, reason: string): GraphFormatError {\n const shown = typeof raw === \"string\" || typeof raw === \"number\" ? JSON.stringify(raw) : typeof raw;\n return new GraphFormatError(\"E_INVALID_ID\", `invalid id ${raw === null ? \"null\" : shown}: ${reason}`, {\n reason,\n });\n}\n\n/** A number literal at lastIndex (sticky). */\nconst NUMBER_LITERAL = /-?\\d[\\d.eE+-]*/y;\n\n/**\n * Whether the element text writes a top-level key's number as a non-integer literal (`\"id\": 5.0`,\n * `\"@id\": 1e3`). A lexical check, so the parsed value (5) cannot tell. Only a key of the element\n * itself counts, not one nested in its `v` (where `s` and `id` are ordinary attribute names).\n * @param text - the element's JSON text\n * @param key - the key\n * @param keyDepth - the nesting depth of the element's own keys: 1, or 2 for an element written as\n * a member's single object (`{\"nodes\": {\"@id\": 1.0}}`)\n * @returns true when the literal holds a fraction or an exponent\n * @category Plugin helpers\n */\nexport function inexactLiteral(text: string, key: string, keyDepth = 1): boolean {\n const quoted = JSON.stringify(key);\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const c = text[i];\n if (c === \"{\" || c === \"[\") {\n depth++;\n } else if (c === \"}\" || c === \"]\") {\n depth--;\n } else if (c === '\"') {\n const start = i;\n i = stringEnd(text, i + 1);\n // the key itself (a string at depth 1 followed by a colon), not a value spelled like it\n const colon = skipSpace(text, i + 1);\n if (depth === keyDepth && text.startsWith(quoted, start) && text[colon] === \":\") {\n return inexactNumberAt(text, skipSpace(text, colon + 1));\n }\n }\n i++;\n }\n return false;\n}\n\n/**\n * Whether a number literal at an offset holds a fraction or an exponent.\n * @param text - the text\n * @param at - the offset\n * @returns true when it does; false for an integer or no number\n */\nfunction inexactNumberAt(text: string, at: number): boolean {\n NUMBER_LITERAL.lastIndex = at;\n const match = NUMBER_LITERAL.exec(text);\n return match !== null && /[.eE]/.test(match[0]);\n}\n\n// ============================================================ Cytoscape positions (design section 1.0.2)\n\n/**\n * The name of the position column of every Cytoscape-family importer.\n * @category Plugin helpers\n */\nexport const POSITION_COLUMN = \"position\";\n\n/** The name of the stacking-order column (Cytoscape's NODE_Z_LOCATION). */\nconst Z_COLUMN = \"z\";\n\n/**\n * The position column of a Cytoscape-family importer: f32 x3, y-up, 2 source dimensions.\n * @param format - the importer's format name\n * @param sourceDims - 3 when z goes into the position (zAs \"position\")\n * @returns the declaration\n * @category Plugin helpers\n */\nexport function positionDecl(format: string, sourceDims = 2): ColumnDecl {\n return {\n name: POSITION_COLUMN,\n dtype: \"f32\",\n components: 3,\n role: \"position\",\n mutable: true,\n nullable: true,\n extra: { sourceDims, units: \"file\" },\n origin: { format, namespace: \"cytoscape\" },\n };\n}\n\n/**\n * The stacking-order column `z` (f64, origin namespace \"cytoscape\").\n * @param format - the importer's format name\n * @returns the declaration\n * @category Plugin helpers\n */\nexport function zDecl(format: string): ColumnDecl {\n return {\n name: Z_COLUMN,\n dtype: \"f64\",\n nullable: true,\n origin: { format, id: null, namespace: \"cytoscape\" },\n extra: { cytoscape: \"z\" },\n };\n}\n\n/**\n * Whether a coordinate fits the f32 position column: finite once rounded to f32 (1e39 and the\n * Infinity of a literal like 1e400 do not, nor does NaN).\n * @param value - the coordinate\n * @returns true when the column can hold it\n * @category Plugin helpers\n */\nexport function fitsF32(value: number): boolean {\n return Number.isFinite(Math.fround(value));\n}\n\n/**\n * The head of an input as text for a sniff: UTF-16 by its byte order mark, else UTF-8, never\n * failing, without the BOM.\n * @param head - the first bytes\n * @returns the text\n * @category Plugin helpers\n */\nexport function headText(head: Uint8Array): string {\n let encoding = \"utf-8\";\n if (head.byteLength >= 2 && head[0] === 0xff && head[1] === 0xfe) {\n encoding = \"utf-16le\";\n } else if (head.byteLength >= 2 && head[0] === 0xfe && head[1] === 0xff) {\n encoding = \"utf-16be\";\n }\n return new TextDecoder(encoding).decode(head).replace(/^\\uFEFF/, \"\");\n}\n\n/**\n * Cytoscape's screen y (growing downward) as graph-io's y (growing upward), and back: a negation\n * that never yields -0.\n * @param y - the coordinate\n * @returns the flipped coordinate\n * @category Plugin helpers\n */\nexport function flipY(y: number): number {\n return y === 0 ? 0 : -y;\n}\n\n// ============================================================ stream structure shared by CX1 and CX2\n\n/** The aspect names of the stream structure, not of the network. */\nconst STRUCTURE_ASPECTS: ReadonlySet<string> = new Set([\"metaData\", \"status\", \"numberVerification\"]);\n\n/**\n * The order, metadata and status rules CX1 and CX2 share (design section 1.0.1): a third\n * `metaData` or an aspect after the post-metadata or after `status` is W_ASPECT_ORDER; declared\n * element counts are compared with what was read (W_COUNT_MISMATCH); `status.success: false` is\n * fatal (E_STATUS_FAILED) and `success: true` with an error text a warning (W_STATUS_WARNING).\n * @category Plugin helpers\n */\nexport class CxStructure {\n private readonly report: ImportReportBuilder;\n\n /** Elements read per aspect name. */\n readonly counts = new Map<string, number>();\n\n /** Blocks read per aspect name. */\n readonly blocks = new Map<string, number>();\n\n /** The element counts the metadata declares, by aspect name (a later declaration wins). */\n private readonly declared = new Map<string, number>();\n\n private metaDataBlocks = 0;\n\n private networkBlocks = 0;\n\n private postMetadata = false;\n\n private statusSeen = false;\n\n /** The status element, when one was read. */\n status: unknown = undefined;\n\n /**\n * Whether a status block was read.\n * @returns true once a status block began\n */\n get hasStatus(): boolean {\n return this.statusSeen;\n }\n\n /** The name an aspect is counted under (CX1 reads old Cytoscape names under their cy names). */\n private readonly canonical: (name: string) => string;\n\n /**\n * Create the checker.\n * @param report - the report\n * @param canonical - the name an aspect named in metaData is counted under (default: itself)\n */\n constructor(report: ImportReportBuilder, canonical: (name: string) => string = (name) => name) {\n this.report = report;\n this.canonical = canonical;\n }\n\n /**\n * Record the start of a block (or a member holding one aspect).\n * @param aspect - the aspect name\n * @param line - its line\n */\n block(aspect: string, line: number): void {\n this.blocks.set(aspect, (this.blocks.get(aspect) ?? 0) + 1);\n if (this.statusSeen) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `the \"${aspect}\" block comes after the status block, which must be last; it is read`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:status`,\n );\n }\n if (aspect === \"metaData\") {\n this.metaDataBlocks++;\n if (this.networkBlocks > 0) {\n this.postMetadata = true;\n }\n if (this.metaDataBlocks > 2) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `${this.metaDataBlocks} metaData blocks; a document has at most two (pre and post)`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:metaData`,\n );\n }\n return;\n }\n if (aspect === \"status\") {\n this.statusSeen = true;\n return;\n }\n if (STRUCTURE_ASPECTS.has(aspect)) {\n return;\n }\n this.networkBlocks++;\n if (this.postMetadata) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `the \"${aspect}\" block comes after the post-metadata, where only status is allowed; it is read`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:post`,\n );\n }\n }\n\n /**\n * Record one element of a block; metaData and status elements are checked here.\n * @param aspect - the aspect name\n * @param value - the parsed element\n * @param line - its line\n */\n element(aspect: string, value: unknown, line: number): void {\n this.counts.set(aspect, (this.counts.get(aspect) ?? 0) + 1);\n if (aspect === \"metaData\") {\n this.readCount(value, line);\n return;\n }\n if (aspect === \"status\") {\n this.readStatus(value, line);\n }\n }\n\n /**\n * Record the element count a metaData element declares; a count that is not an integer (text,\n * a fraction) or that names no aspect is W_COUNT_MISMATCH and not checked.\n * @param value - the metaData element\n * @param line - its line\n */\n private readCount(value: unknown, line: number): void {\n if (!isRecord(value) || value.elementCount === undefined || value.elementCount === null) {\n return;\n }\n const raw = value.elementCount;\n let count: number | null = null;\n if (raw instanceof ExactInteger) {\n count = Number(raw.digits);\n } else if (typeof raw === \"number\" && Number.isInteger(raw)) {\n count = raw;\n }\n if (typeof value.name !== \"string\" || count === null) {\n const shownCount = raw instanceof ExactInteger ? raw.digits : JSON.stringify(raw);\n this.report.warning(\n \"validation-error\",\n COUNT_MISMATCH_CODE,\n `a metaData element declares the elementCount ${shownCount} for the name ${JSON.stringify(value.name) ?? \"undefined\"}; not a count of a named aspect, not checked`,\n { line, element: \"metaData\" },\n );\n return;\n }\n this.declared.set(this.canonical(value.name), count);\n }\n\n /**\n * Check a status element: success false is fatal, an error text with success true a warning.\n * @param value - the element\n * @param line - its line\n */\n private readStatus(value: unknown, line: number): void {\n if (this.status !== undefined) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n \"more than one status element; the last is used\",\n { line, element: \"status\" },\n `${ASPECT_ORDER_CODE}:status2`,\n );\n }\n this.status = value;\n if (!isRecord(value)) {\n return;\n }\n const error = typeof value.error === \"string\" ? value.error : \"\";\n if (value.success === false) {\n const message = `the producer marked the document as failed${error.length > 0 ? `: ${error}` : \"\"}`;\n this.report.error(\"validation-error\", STATUS_FAILED_CODE, message, { line, element: \"status\" });\n throw this.report.abort(message, { code: STATUS_FAILED_CODE });\n }\n if (value.success === true && error.length > 0) {\n this.report.warning(\"validation-error\", STATUS_WARNING_CODE, `the producer reports a warning: ${error}`, {\n line,\n element: \"status\",\n });\n }\n }\n\n /**\n * Whether the status element is one object with a boolean success.\n * @returns true for a well-formed status\n */\n statusWellFormed(): boolean {\n return isRecord(this.status) && typeof this.status.success === \"boolean\";\n }\n\n /** Compare the declared element counts with what was read (W_COUNT_MISMATCH per aspect). */\n checkCounts(): void {\n for (const [name, declared] of this.declared) {\n const read = this.counts.get(name) ?? 0;\n if (read !== declared) {\n this.report.warning(\n \"validation-error\",\n COUNT_MISMATCH_CODE,\n `metaData declares ${declared} \"${name}\" element${plural(declared)}; ${read} ${agree(read, \"was\", \"were\")} read`,\n { element: name },\n );\n }\n }\n }\n}\n\n/**\n * Whether a value is a plain JSON object.\n * @param value - any value\n * @returns true for a non-null, non-array object\n * @category Plugin helpers\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof ExactInteger);\n}\n\n/**\n * Declare a column under a name no column of the table holds yet: a per-element visual property\n * whose name an attribute already has gets `<name>#2` (W_COLUMN_RENAMED), even when the two share a\n * shape, so the visual value never overwrites the attribute.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the declaration\n * @param report - the report the rename is recorded in\n * @returns the handle\n * @category Plugin helpers\n */\nexport function declareFresh(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n decl: ColumnDecl,\n report: ImportReportBuilder,\n): ColumnHandle {\n const taken = (name: string): boolean =>\n (domain === \"node\" ? sink.nodeColumn(name) : sink.edgeColumn(name)) !== INVALID_INDEX;\n let fresh = decl;\n if (taken(decl.name)) {\n fresh = { ...decl, name: uniqueColumnName(decl.name, null, taken) };\n report.warning(\n \"coercion\",\n COLUMN_RENAMED_CODE,\n `${domain} column \"${decl.name}\" renamed to \"${fresh.name}\": an attribute holds that name`,\n { element: decl.name },\n );\n }\n return declareResolved(sink, domain, fresh, report).handle;\n}\n"],"names":["members","inexactLiteral"],"mappings":"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"}
1
+ {"version":3,"file":"json-elements-CkC87El2.js","sources":["../../src/common/json-elements.ts"],"sourcesContent":["/**\n * The JSON reading the two CX formats share (design section 1.0 of\n * design/graph-io/cytoscape-and-obo/design.md): the exact-integer rewrite (also used by the JSON\n * importer), a streaming scanner that walks a CX document's top-level array and hands over each\n * aspect element as soon as its closing brace arrives (section 1.2.5), and the CX id rule of\n * section 1.0.2.\n *\n * The scanner never holds the document as one string, so a file larger than the longest string a\n * JavaScript engine can hold is still read: it keeps the text of the element being read and\n * nothing else. Each element is parsed with `JSON.parse`; an integer literal beyond 2^53 is kept as\n * an ExactInteger (its digits) so an id never loses a digit.\n */\n\nimport {\n type ColumnDecl,\n type ColumnHandle,\n GraphFormatError,\n type GraphSink,\n INVALID_INDEX,\n type NodeId,\n} from \"@graphty/graph-format\";\n\nimport { declareResolved, uniqueColumnName } from \"./attributes.js\";\nimport {\n ASPECT_ORDER_CODE,\n BAD_ASPECT_BLOCK_CODE,\n BAD_VALUE_CODE,\n COLUMN_RENAMED_CODE,\n COUNT_MISMATCH_CODE,\n DUPLICATE_ATTRIBUTE_CODE,\n JSON_NONSTANDARD_NUMBER_CODE,\n MULTI_ASPECT_FRAGMENT_CODE,\n PRECISION_CODE,\n STATUS_FAILED_CODE,\n STATUS_WARNING_CODE,\n} from \"./codes.js\";\nimport { agree, plural } from \"./plural.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\nimport { trimTrailingZeros } from \"./text.js\";\n\n// ============================================================ exact integers\n\n/**\n * A run of 16 digits not inside a fraction: the shortest integer literal that can exceed 2^53 (9007199254740992).\n * @category Plugin helpers\n */\nexport const MAYBE_UNSAFE_INTEGER = /(?<![0-9.])[0-9]{16}/;\n\n/**\n * A literal the double read for may not equal: an exponent of 15 or more (`9.007199254740993e15`,\n * `1e400`: an integer beyond 2^53 or an overflow), an exponent of -300 or less (`1e-400`: an\n * underflow to 0), or 16 or more digits split by a decimal point before an exponent\n * (`90071992547409.93e2`). MAYBE_UNSAFE_INTEGER sees none of them.\n * ponytail: a literal that underflows through 300+ leading fraction zeros and no exponent is not\n * gated; add a digit-count check if such files turn up.\n * @category Plugin helpers\n */\nexport const MAYBE_INEXACT_EXPONENT = new RegExp(\n [\n /\\d[eE]\\+?0*(?:1[5-9]|[2-9]\\d|[1-9]\\d{2,})/, // an exponent of 15 or more\n /\\d[eE]-0*(?:[3-9]\\d{2}|[1-9]\\d{3,})/, // an exponent of -300 or less\n /(?<![\\d.])(?=(?:\\d\\.?){16})\\d+\\.\\d+[eE]/, // 16 or more digits split by a decimal point\n ]\n .map((r) => r.source)\n .join(\"|\"),\n);\n\n/**\n * The prefix of the string a non-standard token or an exact integer is rewritten to (a NUL\n * character first, which no sensible attribute value starts with). A document that already holds\n * the text gets a numbered variant, so a genuine string is never revived as a number.\n */\nconst SENTINEL = `${String.fromCodePoint(0)}graph-io`;\n\n/** The non-standard tokens Python's json module writes, longest first so -Infinity wins over a bare minus. */\nconst NONSTANDARD_TOKENS: readonly (readonly [string, number])[] = [\n [\"-Infinity\", -Infinity],\n [\"Infinity\", Infinity],\n [\"NaN\", Number.NaN],\n];\n\n/** A JSON integer literal (no fraction, no exponent, no leading zero), as CANONICAL_INTEGER in common/ids.ts. */\nconst INTEGER_LITERAL = /^-?(0|[1-9][0-9]*)$/;\n\n/** A JSON number literal, split into sign, integer digits, fraction digits and exponent. */\nconst NUMBER_PARTS = /^(-?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$/;\n\n/** What rewriteNumbers() found. */\ninterface RewrittenNumbers {\n /** The rewritten text. */\n readonly text: string;\n /** The non-standard tokens seen (only when they were allowed). */\n readonly tokens: Set<string>;\n /** The integer literals beyond 2^53 that were quoted. */\n readonly bigIntegers: string[];\n /**\n * The literals with a fraction or an exponent that denote an integer beyond 2^53 a double cannot\n * hold (`9007199254740993.0`), and the finite literals that overflow to an infinity (`1e400`) or\n * underflow to 0 (`1e-400`):\n * read as the nearest double, which changes the value.\n */\n readonly inexact: string[];\n /** The JSON.parse reviver that turns the sentinel strings of this rewrite back into numbers. */\n readonly revive: (key: string, value: unknown) => unknown;\n /** The JSON.parse reviver that turns this rewrite's exact sentinels into ExactInteger. */\n readonly reviveExact: (key: string, value: unknown) => unknown;\n}\n\n/**\n * The sentinel prefix of one rewrite: SENTINEL with a colon, or a numbered variant when the document\n * already holds that one. A string can only decode to a NUL by escaping it (`\\u0000`), so when the\n * text holds that escape the escaped strings are decoded and searched too: any other escape in them\n * (`graph\\u002dio:`) would hide the sentinel from a search of the raw text.\n * @param text - the document text\n * @returns the prefix\n */\nfunction sentinelFor(text: string): string {\n let haystack = text;\n if (/\\\\u0000/i.test(text)) {\n const decoded: string[] = [];\n for (const [literal] of text.matchAll(/\"(?:[^\"\\\\]|\\\\.)*\"/g)) {\n if (literal.includes(\"\\\\\")) {\n decoded.push(decodeString(literal));\n }\n }\n haystack = `${decoded.join(\"\\n\")}\\n${text}`;\n }\n let k = 0;\n while (haystack.includes(`graph-io${k === 0 ? \"\" : String(k)}:`)) {\n k++;\n }\n return `${SENTINEL}${k === 0 ? \"\" : String(k)}:`;\n}\n\n/**\n * Decode one JSON string literal, or give it back as written when it is not valid (the parse then\n * reports the syntax error).\n * @param literal - the literal with its quotes\n * @returns the decoded value\n */\nfunction decodeString(literal: string): string {\n try {\n return String(JSON.parse(literal));\n } catch {\n return literal;\n }\n}\n\n/**\n * Whether a number literal with a fraction or an exponent reads back as another value: an integer\n * beyond 2^53 written with more significant digits than a double keeps, which the nearest double\n * misses, or a finite literal beyond the double range or a non-zero one below it.\n * @param literal - the literal as written\n * @returns true when the double read for it is not the value it denotes\n */\nfunction isInexactLiteral(literal: string): boolean {\n const value = Number(literal);\n if (!Number.isFinite(value)) {\n return true;\n }\n if (value === 0) {\n // a non-zero literal below the smallest double underflows to 0\n return /[1-9]/.test(literal.split(/[eE]/)[0]);\n }\n if (Number.isSafeInteger(value) || !Number.isInteger(value)) {\n return false;\n }\n const m = NUMBER_PARTS.exec(literal);\n if (m === null) {\n return false;\n }\n const digits = trimTrailingZeros(m[2] + (m[3] ?? \"\"));\n const exponent = Number(m[4] ?? \"0\") - (m[3] ?? \"\").length + ((m[2] + (m[3] ?? \"\")).length - digits.length);\n if (digits.replace(/^0+/, \"\").length <= 15 || exponent < 0) {\n // up to 15 significant digits the nearest double reads back as the same text (1e39 is the\n // float it says); a fraction that rounds to an integer is ordinary floating-point rounding\n return false;\n }\n const exact = BigInt(digits) * 10n ** BigInt(exponent);\n return exact !== BigInt(Math.abs(value));\n}\n\n/**\n * Rewrite the numbers JSON.parse cannot read exactly, outside strings and in value positions only:\n * NaN / Infinity / -Infinity become sentinel strings (when `nonstandard` is true), an integer\n * literal that is not a safe integer becomes a string of its digits (or, with `exactSentinel`, a\n * sentinel string the result's reviveExact turns into an ExactInteger). A container stack tells a\n * value position (after `:`, `[`, or `,` inside an array) from a key position, so `{NaN: 1}` stays\n * invalid. Literals with a fraction or an exponent that no double holds are listed, not rewritten.\n * @param text - the document text\n * @param options - which rewrites apply\n * @param options.nonstandard - rewrite NaN / Infinity / -Infinity (default true)\n * @param options.exactSentinel - quote big integers as sentinels instead of bare digits (default false)\n * @returns the rewritten text, what was found and the revivers of this rewrite\n * @category Plugin helpers\n */\nexport function rewriteNumbers(\n text: string,\n options: { readonly nonstandard?: boolean; readonly exactSentinel?: boolean } = {},\n): RewrittenNumbers {\n const nonstandard = options.nonstandard ?? true;\n const exactSentinel = options.exactSentinel ?? false;\n const sentinel = sentinelFor(text);\n const exactPrefix = `${sentinel}exact:`;\n const parts: string[] = [];\n const tokens = new Set<string>();\n const bigIntegers: string[] = [];\n const inexact: string[] = [];\n const arrays: boolean[] = [];\n let expectValue = true;\n let copied = 0;\n let i = 0;\n const n = text.length;\n while (i < n) {\n const ch = text[i];\n if (ch === '\"') {\n i++;\n while (i < n && text[i] !== '\"') {\n i += text[i] === \"\\\\\" ? 2 : 1;\n }\n i++;\n expectValue = false;\n continue;\n }\n if (ch === \"{\" || ch === \"[\") {\n arrays.push(ch === \"[\");\n expectValue = ch === \"[\";\n } else if (ch === \"}\" || ch === \"]\") {\n arrays.pop();\n expectValue = false;\n } else if (ch === \":\") {\n expectValue = true;\n } else if (ch === \",\") {\n expectValue = arrays.length > 0 && arrays[arrays.length - 1];\n } else if (expectValue && ch !== \" \" && ch !== \"\\t\" && ch !== \"\\n\" && ch !== \"\\r\") {\n expectValue = false;\n const token = nonstandard ? NONSTANDARD_TOKENS.find(([word]) => text.startsWith(word, i)) : undefined;\n let end = i;\n let replacement: string | null = null;\n if (token !== undefined) {\n end = i + token[0].length;\n tokens.add(token[0]);\n replacement = JSON.stringify(`${sentinel}${token[0]}`);\n } else if (ch === \"-\" || (ch >= \"0\" && ch <= \"9\")) {\n end = i + 1;\n while (end < n && \"0123456789+-.eE\".includes(text[end])) {\n end++;\n }\n const literal = text.slice(i, end);\n if (INTEGER_LITERAL.test(literal)) {\n if (!Number.isSafeInteger(Number(literal))) {\n bigIntegers.push(literal);\n replacement = exactSentinel ? JSON.stringify(`${exactPrefix}${literal}`) : `\"${literal}\"`;\n }\n } else if (isInexactLiteral(literal)) {\n inexact.push(literal);\n }\n }\n if (replacement !== null) {\n parts.push(text.slice(copied, i), replacement);\n copied = end;\n }\n i = Math.max(end, i + 1);\n continue;\n }\n i++;\n }\n parts.push(text.slice(copied));\n const revive = (_key: string, value: unknown): unknown => {\n if (typeof value === \"string\" && value.startsWith(sentinel)) {\n const found = NONSTANDARD_TOKENS.find(([word]) => word === value.slice(sentinel.length));\n return found === undefined ? value : found[1];\n }\n return value;\n };\n const reviveExact = (_key: string, value: unknown): unknown =>\n typeof value === \"string\" && value.startsWith(exactPrefix)\n ? new ExactInteger(value.slice(exactPrefix.length))\n : value;\n return { text: parts.join(\"\"), tokens, bigIntegers, inexact, revive, reviveExact };\n}\n\n/** One key repeated in a JSON object. */\ninterface DuplicateKey {\n /** The key. */\n readonly key: string;\n /** The UTF-16 offset of the repeated occurrence. */\n readonly offset: number;\n}\n\n/** Keys of one object compared pairwise up to this count; a wider object switches to a Set. */\nconst LINEAR_KEYS = 32;\n\n/**\n * The keys that occur twice in one object of a valid JSON text, which JSON.parse silently reduces\n * to the last value (RFC 8259 section 4: names SHOULD be unique). One pass over the characters\n * that jumps over strings; the keys of a small object are compared in place, without\n * slicing (the parse itself costs about as much as this scan, so it allocates nothing per key).\n * Keys are compared as written: `\"\\u0069d\"` and `\"id\"` are not recognized as one key.\n * @param text - a text JSON.parse accepted (or its rewrite)\n * @returns every repetition in document order\n * @category Plugin helpers\n */\nexport function findDuplicateKeys(text: string): DuplicateKey[] {\n const found: DuplicateKey[] = [];\n // per depth: the [start, end) of each key of the open object (reused), or -1 for an array\n const spans: number[][] = [];\n const wide: (Set<string> | null)[] = [];\n let depth = 0;\n const n = text.length;\n let at = 0;\n while (at < n) {\n const c = text.codePointAt(at);\n if (c === 123 || c === 91) {\n // { or [\n openContainer(spans, wide, depth, c === 91);\n depth++;\n } else if (c === 125 || c === 93) {\n depth--;\n } else if (c === 34) {\n const end = closingQuote(text, at + 1);\n const keys = depth > 0 ? spans[depth - 1] : null;\n // an array's span list starts with -1: its strings are values, never keys\n const isObjectKey = keys !== null && keys[0] !== -1 && isKey(text, end + 1);\n if (isObjectKey && isRepeated(text, at + 1, end, keys, wide, depth - 1)) {\n found.push({ key: text.slice(at + 1, end), offset: at });\n }\n at = end;\n }\n at++;\n }\n return found;\n}\n\n/**\n * Start the key list of a container just opened at a depth (reusing the list of an earlier one).\n * @param spans - the key spans per depth\n * @param wide - the key sets per depth, for wide objects\n * @param depth - the container's depth\n * @param isArray - whether it is an array, whose strings are never keys\n */\nfunction openContainer(spans: number[][], wide: (Set<string> | null)[], depth: number, isArray: boolean): void {\n if (spans.length === depth) {\n spans.push([]);\n wide.push(null);\n }\n spans[depth].length = 0;\n if (isArray) {\n spans[depth].push(-1);\n }\n wide[depth] = null;\n}\n\n/**\n * Whether a key repeats one already seen in its object, remembering it otherwise.\n * @param text - the text\n * @param start - the key's first character\n * @param end - the key's closing quote\n * @param keys - the spans of the object's keys so far\n * @param wide - the Set of each depth's keys once the object is wide\n * @param level - the object's depth\n * @returns true for a repetition\n */\nfunction isRepeated(\n text: string,\n start: number,\n end: number,\n keys: number[],\n wide: (Set<string> | null)[],\n level: number,\n): boolean {\n const set = wide[level];\n if (set !== null) {\n const key = text.slice(start, end);\n return set.has(key) || (set.add(key), false);\n }\n const length = end - start;\n for (let k = 0; k < keys.length; k += 2) {\n if (keys[k + 1] - keys[k] === length && sameText(text, keys[k], start, length)) {\n return true;\n }\n }\n keys.push(start, end);\n if (keys.length > 2 * LINEAR_KEYS) {\n const all = new Set<string>();\n for (let k = 0; k < keys.length; k += 2) {\n all.add(text.slice(keys[k], keys[k + 1]));\n }\n wide[level] = all;\n }\n return false;\n}\n\n/**\n * Whether two ranges of one text hold the same characters.\n * @param text - the text\n * @param a - the first range's start\n * @param b - the second range's start\n * @param length - the ranges' length\n * @returns true when equal\n */\nfunction sameText(text: string, a: number, b: number, length: number): boolean {\n for (let i = 0; i < length; i++) {\n if (text.codePointAt(a + i) !== text.codePointAt(b + i)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * The offset of the quote that closes a JSON string.\n * @param text - the text\n * @param from - the offset after the opening quote\n * @returns the closing quote's offset (the text length when there is none)\n */\nfunction closingQuote(text: string, from: number): number {\n for (let q = text.indexOf('\"', from); q >= 0; q = text.indexOf('\"', q + 1)) {\n let backslashes = 0;\n while (text.codePointAt(q - 1 - backslashes) === 92) {\n backslashes++;\n }\n if (backslashes % 2 === 0) {\n return q;\n }\n }\n return text.length;\n}\n\n/**\n * Whether the string that ends before an offset is an object key: a colon follows after whitespace.\n * @param text - the text\n * @param from - the offset after the closing quote\n * @returns true for a key\n */\nfunction isKey(text: string, from: number): boolean {\n let i = from;\n while (i < text.length && isSpace(text.codePointAt(i))) {\n i++;\n }\n return text.codePointAt(i) === 58;\n}\n\n/**\n * An integer literal beyond 2^53, kept as its exact digits (a CX id must never lose a digit).\n * @category Plugin helpers\n */\nexport class ExactInteger {\n /** The digits, with a leading minus for a negative value. */\n readonly digits: string;\n\n /**\n * Wrap the digits of an integer literal.\n * @param digits - the literal\n */\n constructor(digits: string) {\n this.digits = digits;\n }\n\n /**\n * The nearest number (JSON.stringify writes it as such).\n * @returns the number\n */\n toJSON(): number {\n return Number(this.digits);\n }\n}\n\n/**\n * Parse one JSON value, keeping integer literals beyond 2^53 as ExactInteger.\n * @param text - the JSON text\n * @returns the value and whether it may hold an ExactInteger; SyntaxError when it is not JSON\n * @category Plugin helpers\n */\nexport function parseExact(text: string): { readonly value: unknown; readonly exact: boolean } {\n if (!MAYBE_UNSAFE_INTEGER.test(text)) {\n return { value: JSON.parse(text) as unknown, exact: false };\n }\n const scan = rewriteNumbers(text, { nonstandard: false, exactSentinel: true });\n if (scan.bigIntegers.length === 0) {\n return { value: JSON.parse(text) as unknown, exact: false };\n }\n return { value: JSON.parse(scan.text, scan.reviveExact) as unknown, exact: true };\n}\n\n/**\n * A parsed value with every ExactInteger replaced by its nearest number, for metadata and for\n * values that are not ids.\n * @param value - the value\n * @param onPrecision - called with the digits of each integer that lost precision\n * @returns the plain JSON value\n * @category Plugin helpers\n */\nexport function plainJson(value: unknown, onPrecision?: (digits: string) => void): unknown {\n if (value instanceof ExactInteger) {\n onPrecision?.(value.digits);\n return Number(value.digits);\n }\n if (Array.isArray(value)) {\n return value.map((item) => plainJson(item, onPrecision));\n }\n if (typeof value === \"object\" && value !== null) {\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n setOwn(out, key, plainJson(item, onPrecision));\n }\n return out;\n }\n return value;\n}\n\n/**\n * Set an own property, also for the key `__proto__` (which an assignment would turn into the\n * object's prototype, losing the entry).\n * @param target - the object\n * @param key - the key\n * @param value - the value\n * @category Plugin helpers\n */\nexport function setOwn(target: Record<string, unknown>, key: string, value: unknown): void {\n if (key === \"__proto__\") {\n Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });\n } else {\n target[key] = value;\n }\n}\n\n/**\n * Parse a text with the bare tokens NaN / Infinity / -Infinity that Python's json writes.\n * @param text - the JSON text that JSON.parse refused\n * @returns the value, whether it may hold an ExactInteger and the tokens seen; null when the\n * tokens are not what made the text invalid\n */\nfunction parseNonstandard(\n text: string,\n): { readonly value: unknown; readonly exact: boolean; readonly tokens: ReadonlySet<string> } | null {\n if (!/NaN|Infinity/.test(text)) {\n return null;\n }\n const scan = rewriteNumbers(text, { nonstandard: true, exactSentinel: true });\n if (scan.tokens.size === 0) {\n return null;\n }\n try {\n const value = JSON.parse(scan.text, (key, raw) => scan.reviveExact(key, scan.revive(key, raw))) as unknown;\n return { value, exact: scan.bigIntegers.length > 0, tokens: scan.tokens };\n } catch {\n return null;\n }\n}\n\n/** A lone surrogate code unit: a high one not followed by a low one, or a low one not after a high one. */\n/** The escapes of the surrogate range, as a message writes them. */\nconst SURROGATE_ESCAPES = String.raw`\\uD800-\\uDFFF`;\n\nconst LONE_SURROGATE = /[\\ud800-\\udbff](?![\\udc00-\\udfff])|(?<![\\ud800-\\udbff])[\\udc00-\\udfff]/g;\n\n/** Text that may hold a surrogate: an escaped one, or a raw one (string input). */\nconst MAYBE_SURROGATE = /\\\\u[dD][89a-fA-F]|[\\ud800-\\udfff]/;\n\n/**\n * Replace every lone surrogate of a parsed value's strings and keys with U+FFFD, in place.\n * @param value - the parsed value\n * @param fixed - counts the strings repaired and names the keys two repaired keys collapse into\n * @param fixed.count - the count\n * @param fixed.collisions - a key one object holds twice once repaired (the later value is kept)\n * @returns the value (a string, or an object with a repaired key, is returned as a new value)\n */\nfunction repairSurrogates(value: unknown, fixed: { count: number; collisions: string[] }): unknown {\n if (typeof value === \"string\") {\n const repaired = value.replaceAll(LONE_SURROGATE, \"\\ufffd\");\n if (repaired !== value) {\n fixed.count++;\n }\n return repaired;\n }\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n value[i] = repairSurrogates(value[i], fixed);\n }\n return value;\n }\n if (isRecord(value)) {\n const keys = Object.keys(value);\n const repairedKeys = keys.map((key) => key.replaceAll(LONE_SURROGATE, \"\\ufffd\"));\n const renamed = repairedKeys.some((key, i) => key !== keys[i]);\n const out: Record<string, unknown> = renamed ? {} : value;\n keys.forEach((key, i) => {\n if (repairedKeys[i] !== key) {\n fixed.count++;\n }\n if (renamed && Object.hasOwn(out, repairedKeys[i])) {\n fixed.collisions.push(repairedKeys[i]);\n }\n setOwn(out, repairedKeys[i], repairSurrogates(value[key], fixed));\n });\n return out;\n }\n return value;\n}\n\n/**\n * The number of keys of a parsed value, at every depth.\n * @param value - the value\n * @returns the count\n */\nfunction keyCount(value: unknown): number {\n if (Array.isArray(value)) {\n return value.reduce<number>((n, item) => n + keyCount(item), 0);\n }\n if (isRecord(value)) {\n return Object.keys(value).reduce((n, key) => n + 1 + keyCount(value[key]), 0);\n }\n return 0;\n}\n\n/**\n * The first key an object of a JSON text holds twice (JSON.parse keeps the later value silently).\n * @param text - valid JSON text\n * @returns the key, or null\n */\nfunction duplicateKey(text: string): string | null {\n const stack: (Set<string> | null)[] = [];\n let i = 0;\n while (i < text.length) {\n const c = text[i];\n if (c === \"{\") {\n stack.push(new Set());\n } else if (c === \"[\") {\n stack.push(null);\n } else if (c === \"}\" || c === \"]\") {\n stack.pop();\n } else if (c === '\"') {\n const start = i;\n i = stringEnd(text, i + 1);\n const keys = stack.at(-1) ?? null;\n if (keys !== null && text[skipSpace(text, i + 1)] === \":\") {\n const key = JSON.parse(text.slice(start, i + 1)) as string;\n if (keys.has(key)) {\n return key;\n }\n keys.add(key);\n }\n }\n i++;\n }\n return null;\n}\n\n/**\n * The offset of the quote that ends a JSON string, skipping escaped characters.\n * @param text - the text\n * @param from - the offset after the opening quote\n * @returns the closing quote's offset, or the text length (or one past it) when there is none\n */\nfunction stringEnd(text: string, from: number): number {\n let i = from;\n while (i < text.length && text[i] !== '\"') {\n i += text[i] === \"\\\\\" ? 2 : 1;\n }\n return i;\n}\n\n/**\n * The index of the first character at or after `from` that is not JSON whitespace.\n * @param text - the text\n * @param from - the first index\n * @returns the index (text.length at the end)\n */\nfunction skipSpace(text: string, from: number): number {\n let i = from;\n while (i < text.length && isSpace(text.codePointAt(i))) {\n i++;\n }\n return i;\n}\n\n/** A key followed by its colon, for the cheap duplicate-key check. */\nconst KEY_COLON = /\"\\s*:/g;\n\n/**\n * The checks of a parsed element that JSON.parse cannot make, recorded in the report: lone\n * surrogates (replaced with U+FFFD, E_BAD_VALUE) and a key held twice by one object\n * (W_DUPLICATE_ATTRIBUTE; the later value is the one read, also for two keys that a repair makes one).\n * @param text - the element text\n * @param parsed - the parsed element\n * @param line - its line\n * @param report - the report\n * @returns the value, repaired\n */\nfunction checkParsed(text: string, parsed: unknown, line: number, report: ImportReportBuilder): unknown {\n let value = parsed;\n if (MAYBE_SURROGATE.test(text)) {\n const fixed = { count: 0, collisions: [] as string[] };\n value = repairSurrogates(value, fixed);\n for (const key of fixed.collisions) {\n report.warnOnce(\n \"validation-error\",\n DUPLICATE_ATTRIBUTE_CODE,\n `an object holds two keys that are the same key ${JSON.stringify(key)} once their lone surrogates are replaced with U+FFFD; the later value is read`,\n { line, element: key },\n `${DUPLICATE_ATTRIBUTE_CODE}:json:${key}`,\n );\n }\n if (fixed.count > 0) {\n report.error(\n \"validation-error\",\n BAD_VALUE_CODE,\n `${fixed.count} string${plural(fixed.count)} ${agree(fixed.count, \"holds\", \"hold\")} a lone surrogate (an unpaired ${SURROGATE_ESCAPES} escape); each is read with U+FFFD in its place`,\n { line },\n );\n }\n }\n if (typeof value === \"object\" && value !== null && (text.match(KEY_COLON)?.length ?? 0) > keyCount(value)) {\n const key = duplicateKey(text);\n if (key !== null) {\n report.warnOnce(\n \"validation-error\",\n DUPLICATE_ATTRIBUTE_CODE,\n `an object holds the key ${JSON.stringify(key)} twice; the later value is read`,\n { line, element: key },\n `${DUPLICATE_ATTRIBUTE_CODE}:json:${key}`,\n );\n }\n }\n return value;\n}\n\n// ============================================================ the streaming aspect scanner\n\n/**\n * A problem with the document's JSON; the importer turns it into a fatal E_SYNTAX or E_EMPTY_INPUT.\n * @category Plugin helpers\n */\nexport class JsonScanError extends Error {\n /** The 1-based line of the problem. */\n readonly line: number;\n\n /** Whether the document holds nothing but whitespace. */\n readonly empty: boolean;\n\n /**\n * Create the error.\n * @param message - what is wrong\n * @param line - the 1-based line\n * @param empty - whether the input is empty\n */\n constructor(message: string, line: number, empty = false) {\n super(message);\n this.name = \"JsonScanError\";\n this.line = line;\n this.empty = empty;\n }\n}\n\n/**\n * One thing the scanner found in a CX document.\n * @category Plugin helpers\n */\nexport type AspectEvent =\n | {\n /**\n * A top-level value that is not an array: a scalar document, parsed; an object is not\n * read at all (object true, value undefined), however large it is.\n */\n readonly kind: \"root\";\n readonly value: unknown;\n readonly object: boolean;\n readonly line: number;\n }\n | {\n /** A member `{\"<aspect>\": [` whose elements follow one by one. */\n readonly kind: \"block\";\n readonly aspect: string;\n readonly block: number;\n /** Whether the aspect is not the member's first key (`{\"nodes\": [...], \"edges\": [...]}`). */\n readonly shared: boolean;\n readonly line: number;\n }\n | {\n /** One element of the current block, parsed. */\n readonly kind: \"element\";\n readonly aspect: string;\n readonly block: number;\n readonly value: unknown;\n /** Whether the value may hold an ExactInteger. */\n readonly exact: boolean;\n /** The element's JSON text (for lexical checks of its ids). */\n readonly text: string;\n readonly line: number;\n }\n | {\n /** The keys of a block member whose values are not arrays (`{\"nodes\": [...], \"x\": 1}`, in any order); skipped. */\n readonly kind: \"extraKeys\";\n readonly aspect: string;\n readonly block: number;\n readonly keys: readonly string[];\n readonly line: number;\n }\n | {\n /**\n * An element, or a member, nested deeper than MAX_ELEMENT_DEPTH: not parsed, so nothing\n * downstream recurses into it (aspect: the block's aspect, null for a member).\n */\n readonly kind: \"deep\";\n readonly aspect: string | null;\n readonly block: number;\n readonly depth: number;\n readonly line: number;\n }\n | {\n /** Any other member of the top-level array (a CX2 descriptor, `{\"ndexStatus\": {...}}`, `1`), parsed. */\n readonly kind: \"member\";\n readonly block: number;\n readonly value: unknown;\n readonly exact: boolean;\n /** The member's JSON text (for lexical checks of its ids). */\n readonly text: string;\n readonly line: number;\n };\n\n/**\n * Whether a character code is whitespace to JSON.\n * @param c - the character code\n * @returns true for space, line feed, carriage return and tab\n */\nconst isSpace = (c: number | undefined): boolean => c === 32 || c === 10 || c === 13 || c === 9;\n\n/**\n * Whether a character code ends a bare scalar (number, true, false, null).\n * @param c - the character code\n * @returns true for whitespace, a comma, a closing bracket or brace, or a colon\n */\nconst endsScalar = (c: number): boolean => isSpace(c) || c === 44 || c === 93 || c === 125 || c === 58;\n\nconst QUOTE_OR_BACKSLASH = /[\"\\\\]/g;\n\n/**\n * The deepest nesting of an element the scanner parses. graph-format refuses JSON values (a json\n * cell, meta.extra) nested deeper than 256 levels, and a kept element sits a few levels down\n * meta.extra; a deeper element is reported instead, before anything recurses into it.\n */\nconst MAX_ELEMENT_DEPTH = 200;\n\n/**\n * A cursor over text chunks with capture: the text of the value being read is kept across chunk\n * boundaries as a list of parts, so a long element costs linear time.\n */\nclass ChunkCursor {\n private readonly iterator: AsyncIterator<string>;\n\n private buf = \"\";\n\n /** The read position in buf. */\n pos = 0;\n\n /** The line number of buf's first character. */\n private linesBefore = 1;\n\n private parts: string[] | null = null;\n\n private captureStart = 0;\n\n /** The deepest nesting the last skipValue() reached. */\n deepest = 0;\n\n /**\n * Wrap text chunks.\n * @param chunks - the decoded text\n */\n constructor(chunks: AsyncIterable<string>) {\n this.iterator = chunks[Symbol.asyncIterator]();\n }\n\n /**\n * Read the next chunk once the current one is consumed.\n * @returns false at the end of the input\n */\n async fill(): Promise<boolean> {\n if (this.parts !== null) {\n this.parts.push(this.buf.slice(this.captureStart));\n this.captureStart = 0;\n }\n this.linesBefore += countNewlines(this.buf, 0, this.buf.length);\n const endsWithCr = this.buf.endsWith(\"\\r\");\n for (;;) {\n const next = await this.iterator.next();\n if (next.done === true) {\n this.buf = \"\";\n this.pos = 0;\n return false;\n }\n if (next.value.length > 0) {\n if (endsWithCr && next.value.startsWith(\"\\n\")) {\n // a CR LF split across two chunks is one line break, counted with the CR\n this.linesBefore--;\n }\n this.buf = next.value;\n this.pos = 0;\n return true;\n }\n }\n }\n\n /**\n * The 1-based line of the read position.\n * @returns the line\n */\n line(): number {\n return this.linesBefore + countNewlines(this.buf, 0, Math.min(this.pos, this.buf.length));\n }\n\n /**\n * The text from the read position to the end of the current chunk, at most `length` characters.\n * @param length - the most characters\n * @returns the text\n */\n ahead(length: number): string {\n return this.buf.slice(this.pos, this.pos + length);\n }\n\n /**\n * Skip whitespace and return the next character without consuming it.\n * @returns the character, or \"\" at the end of the input\n */\n async peek(): Promise<string> {\n for (;;) {\n while (this.pos < this.buf.length && isSpace(this.buf.charCodeAt(this.pos))) {\n this.pos++;\n }\n if (this.pos < this.buf.length) {\n return this.buf[this.pos];\n }\n if (!(await this.fill())) {\n return \"\";\n }\n }\n }\n\n /** Start keeping the text from the read position. */\n beginCapture(): void {\n this.parts = [];\n this.captureStart = this.pos;\n }\n\n /**\n * Stop keeping text.\n * @returns the text kept since beginCapture()\n */\n endCapture(): string {\n const parts = this.parts ?? [];\n parts.push(this.buf.slice(this.captureStart, this.pos));\n this.parts = null;\n return parts.length === 1 ? parts[0] : parts.join(\"\");\n }\n\n /** Drop the capture without building its text. */\n dropCapture(): void {\n this.parts = null;\n }\n\n /**\n * Advance past one JSON value starting at the read position (which holds its first\n * character), tracking strings and nesting only; the value is validated when it is parsed.\n * @param startDepth - the nesting depth already entered (1 when the opening brace was consumed)\n */\n async skipValue(startDepth = 0): Promise<void> {\n let depth = startDepth;\n this.deepest = depth;\n if (depth === 0) {\n const c = this.buf.charCodeAt(this.pos);\n if (c === 34) {\n this.pos++;\n await this.skipString();\n return;\n }\n if (c !== 123 && c !== 91) {\n await this.skipScalar();\n return;\n }\n }\n for (;;) {\n if (this.pos >= this.buf.length && !(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a value\", this.line());\n }\n const c = this.buf.charCodeAt(this.pos);\n this.pos++;\n if (c === 34) {\n await this.skipString();\n } else if (c === 123 || c === 91) {\n depth++;\n if (depth > this.deepest) {\n this.deepest = depth;\n }\n } else if (c === 125 || c === 93) {\n depth--;\n if (depth === 0) {\n return;\n }\n }\n }\n }\n\n /** Advance past the rest of a string whose opening quote was consumed. */\n private async skipString(): Promise<void> {\n for (;;) {\n QUOTE_OR_BACKSLASH.lastIndex = this.pos;\n const found = QUOTE_OR_BACKSLASH.exec(this.buf);\n if (found === null) {\n this.pos = this.buf.length;\n if (!(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a string\", this.line());\n }\n continue;\n }\n this.pos = found.index + 1;\n if (found[0] === '\"') {\n return;\n }\n // a backslash: skip the escaped character, which may be in the next chunk\n if (this.pos >= this.buf.length && !(await this.fill())) {\n throw new JsonScanError(\"the document ends inside a string\", this.line());\n }\n this.pos++;\n }\n }\n\n /** Advance past a bare scalar (a number, true, false, null or a stray word). */\n private async skipScalar(): Promise<void> {\n for (;;) {\n while (this.pos < this.buf.length && !endsScalar(this.buf.charCodeAt(this.pos))) {\n this.pos++;\n }\n if (this.pos < this.buf.length || !(await this.fill())) {\n return;\n }\n }\n }\n\n /**\n * Read one JSON string literal at the read position (its opening quote).\n * @returns the decoded string\n */\n async readString(): Promise<string> {\n this.beginCapture();\n this.pos++;\n await this.skipString();\n const literal = this.endCapture();\n try {\n return JSON.parse(literal) as string;\n } catch (err) {\n throw new JsonScanError(`invalid string: ${messageOf(err)}`, this.line());\n }\n }\n}\n\n/**\n * The number of line breaks in a slice of a string: LF, CR LF and a CR alone (old Mac files).\n * @param text - the string\n * @param from - the first index\n * @param to - one past the last index\n * @returns the count\n */\nfunction countNewlines(text: string, from: number, to: number): number {\n let count = 0;\n let i = text.indexOf(\"\\n\", from);\n while (i >= 0 && i < to) {\n count++;\n i = text.indexOf(\"\\n\", i + 1);\n }\n i = text.indexOf(\"\\r\", from);\n while (i >= 0 && i < to) {\n if (text[i + 1] !== \"\\n\") {\n count++;\n }\n i = text.indexOf(\"\\r\", i + 1);\n }\n return count;\n}\n\n/**\n * The message of a caught value.\n * @param err - the thrown value\n * @returns the message\n */\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Parse a captured value, turning a parse failure into a JsonScanError at its line. With a report,\n * the bare tokens NaN / Infinity / -Infinity are read as numbers (W_JSON_NONSTANDARD_NUMBER), and\n * lone surrogates and repeated keys are recorded (checkParsed()).\n * @param text - the value text\n * @param line - the line the value starts on\n * @param report - the report, or null to only parse\n * @returns the value and whether it may hold an ExactInteger\n */\nfunction parseAt(\n text: string,\n line: number,\n report: ImportReportBuilder | null,\n): { readonly value: unknown; readonly exact: boolean } {\n let parsed: { readonly value: unknown; readonly exact: boolean };\n try {\n parsed = parseExact(text);\n } catch (err) {\n const recovered = report === null ? null : parseNonstandard(text);\n if (recovered === null) {\n throw new JsonScanError(`invalid JSON: ${messageOf(err)}`, line);\n }\n report?.warnOnce(\n \"coercion\",\n JSON_NONSTANDARD_NUMBER_CODE,\n `the document uses the non-standard token${plural(recovered.tokens.size)} ${[...recovered.tokens].join(\", \")}, which strict JSON does not allow; read as numbers`,\n { line },\n );\n parsed = recovered;\n }\n if (report !== null) {\n parsed = { ...parsed, value: checkParsed(text, parsed.value, line, report) };\n }\n return parsed;\n}\n\n/**\n * What a document that does not start with \"[\" looks like, for the syntax error: HTML or XML (an\n * error page saved under the file's name), a ZIP archive, or nothing in particular.\n * @param head - the first characters\n * @returns a sentence to append, or \"\"\n */\nfunction notJsonHint(head: string): string {\n if (head.startsWith(\"<\")) {\n return \"; the input looks like HTML or XML (an error page saved as the file?), not JSON\";\n }\n if (head.startsWith(\"PK\\u0003\\u0004\")) {\n return \"; the input is a ZIP archive (a Cytoscape session .cys is read by the cys importer), not JSON\";\n }\n return \"\";\n}\n\n/**\n * Walk a CX document (CX1 or CX2): a top-level array of members, each normally a one-key object\n * `{\"<aspect>\": [elements]}`. The elements of such a block are parsed and yielded one at a time;\n * any other member is parsed whole and yielded as a \"member\". A document that is not an array is\n * yielded as one \"root\" event (an object without reading it). Syntax errors throw JsonScanError.\n * @param chunks - the decoded text\n * @param report - where NaN tokens, lone surrogates and repeated keys are recorded (parseAt()); null to only parse\n * @yields the events in document order\n * @returns nothing\n */\nexport async function* scanAspects(\n chunks: AsyncIterable<string>,\n report: ImportReportBuilder | null = null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n const cur = new ChunkCursor(chunks);\n const first = await cur.peek();\n if (first === \"\") {\n throw new JsonScanError(\"the input is empty\", 1, true);\n }\n const hint = notJsonHint(cur.ahead(16));\n if (hint === \"\" && cur.ahead(8).includes(String.fromCodePoint(0))) {\n throw new JsonScanError(\n 'the input holds NUL characters: UTF-16 without a byte order mark (pass the encoding option, such as \"utf-16le\"), or binary data; not JSON',\n cur.line(),\n );\n }\n if (first === \"[\") {\n cur.pos++;\n yield* members(cur, report);\n return;\n }\n const line = cur.line();\n if (first === \"{\") {\n yield { kind: \"root\", value: undefined, object: true, line };\n return;\n }\n yield { kind: \"root\", value: await scalarRoot(cur, line, hint), object: false, line };\n}\n\n/**\n * Read a document that is neither an array nor an object: one value, and nothing after it.\n * @param cur - the cursor at the value\n * @param line - the value's line\n * @param hint - what the input looks like when it is not JSON, for the error\n * @returns the value\n */\nasync function scalarRoot(cur: ChunkCursor, line: number, hint: string): Promise<unknown> {\n cur.beginCapture();\n await cur.skipValue();\n let value: unknown;\n try {\n ({ value } = parseAt(cur.endCapture(), line, null));\n } catch (err) {\n if (err instanceof JsonScanError && hint !== \"\") {\n throw new JsonScanError(`${err.message}${hint}`, err.line);\n }\n throw err;\n }\n if ((await cur.peek()) !== \"\") {\n throw new JsonScanError(\"unexpected text after the document\", cur.line());\n }\n return value;\n}\n\n/**\n * Read the members of the top-level array, and check that nothing follows it.\n * @param cur - the cursor after the opening bracket\n * @param report - the report of parseAt(), or null\n * @yields the members' events\n * @returns nothing\n */\nasync function* members(\n cur: ChunkCursor,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n let block = 0;\n let next = await cur.peek();\n if (next === \"]\") {\n cur.pos++;\n }\n while (next !== \"]\") {\n yield* member(cur, block, report);\n block++;\n next = await cur.peek();\n if (next !== \",\" && next !== \"]\") {\n throw new JsonScanError(\n next === \"\" ? \"the document ends before its closing bracket\" : `expected \",\" or \"]\", found \"${next}\"`,\n cur.line(),\n );\n }\n cur.pos++;\n }\n if ((await cur.peek()) !== \"\") {\n throw new JsonScanError(\"unexpected text after the closing bracket\", cur.line());\n }\n}\n\n/**\n * Read one member of the top-level array.\n * @param cur - the cursor at the member's first character\n * @param block - the member's position among the members\n * @param report - the report of parseAt(), or null\n * @yields the member's events\n * @returns nothing\n */\nasync function* member(\n cur: ChunkCursor,\n block: number,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n const c = await cur.peek();\n const line = cur.line();\n if (c === \"\" || c === \"]\" || c === \",\") {\n throw new JsonScanError(c === \"\" ? \"the document ends inside its array\" : `unexpected \"${c}\"`, line);\n }\n cur.beginCapture();\n if (c !== \"{\") {\n await cur.skipValue();\n yield wholeMember(cur, block, line, report);\n return;\n }\n cur.pos++;\n if ((await cur.peek()) !== '\"') {\n // `{}` or not JSON: parse the whole member so a syntax error is reported as such\n await cur.skipValue(1);\n yield wholeMember(cur, block, line, report);\n return;\n }\n // every array-valued key is a block whose elements stream, whatever the key order; any other\n // value is kept as text, so a member without an array is parsed whole (a CX2 descriptor, a\n // one-object aspect) and one with an array names its other keys (extraKeys), skipped\n const outer = cur.endCapture();\n const firstLine = cur.line();\n const keys: MemberKeys = { others: [], firstBlock: null, deep: 0 };\n let next = \",\";\n let index = 0;\n while (next === \",\") {\n const key = await readKey(cur);\n yield* keyValue(cur, key, index, block, keys, report);\n index++;\n next = await cur.peek();\n if (next !== \",\" && next !== \"}\") {\n throw new JsonScanError(\n next === \"\" ? \"the document ends inside a member\" : `expected \",\" or \"}\" after the key \"${key.name}\"`,\n cur.line(),\n );\n }\n cur.pos++;\n }\n yield* memberEnd(keys, block, line, { outer, firstLine }, report);\n}\n\n/** What one-object member's keys held: the non-array values as text, the first block, the deepest value. */\ninterface MemberKeys {\n readonly others: { key: string; text: string | null; line: number }[];\n firstBlock: string | null;\n deep: number;\n}\n\n/**\n * The event of a member read whole (its capture already taken): the member, or \"deep\" when it\n * nests too deep to parse.\n * @param cur - the cursor after the member\n * @param block - the member's position among the members\n * @param line - the member's line\n * @param report - the report of parseAt(), or null\n * @returns the event\n */\nfunction wholeMember(cur: ChunkCursor, block: number, line: number, report: ImportReportBuilder | null): AspectEvent {\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n return { kind: \"deep\", aspect: null, block, depth: cur.deepest, line };\n }\n const text = cur.endCapture();\n const parsed = parseAt(text, line, report);\n return { kind: \"member\", block, value: parsed.value, exact: parsed.exact, text, line };\n}\n\n/**\n * Read a member's key and the colon after it.\n * @param cur - the cursor at the key\n * @returns the key and its line\n */\nasync function readKey(cur: ChunkCursor): Promise<{ name: string; line: number }> {\n if ((await cur.peek()) !== '\"') {\n throw new JsonScanError(\"expected a key\", cur.line());\n }\n const line = cur.line();\n const name = await cur.readString();\n if ((await cur.peek()) !== \":\") {\n throw new JsonScanError(`expected \":\" after the key \"${name}\"`, cur.line());\n }\n cur.pos++;\n return { name, line };\n}\n\n/**\n * Read one key's value: an array is a block whose elements stream; any other value is kept as text\n * (or, when it nests too deep to parse, only its depth).\n * @param cur - the cursor at the value\n * @param key - the key\n * @param key.name - its name\n * @param key.line - its line\n * @param index - the key's position in the member\n * @param block - the member's position among the members\n * @param keys - what the member's keys held so far\n * @param report - the report of parseAt(), or null\n * @yields the block's events\n * @returns nothing\n */\nasync function* keyValue(\n cur: ChunkCursor,\n key: { name: string; line: number },\n index: number,\n block: number,\n keys: MemberKeys,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n if ((await cur.peek()) === \"[\") {\n cur.pos++;\n yield { kind: \"block\", aspect: key.name, block, shared: index > 0, line: key.line };\n keys.firstBlock ??= key.name;\n yield* blockElements(cur, key.name, block, report);\n return;\n }\n cur.beginCapture();\n await cur.skipValue();\n // the member's own brace is one level more\n if (cur.deepest + 1 > MAX_ELEMENT_DEPTH) {\n // never parse (recursively) what no one reads\n cur.dropCapture();\n keys.deep = Math.max(keys.deep, cur.deepest + 1);\n keys.others.push({ key: key.name, text: null, line: key.line });\n } else {\n keys.others.push({ key: key.name, text: cur.endCapture(), line: key.line });\n }\n}\n\n/**\n * The events after a one-object member's keys: with a block, its other keys (checked, then named\n * as extraKeys); without one, the member parsed whole from its kept text.\n * @param keys - what the member's keys held\n * @param block - the member's position among the members\n * @param line - the member's line\n * @param start - the member's opening text and the line after it\n * @param start.outer - the text up to the first key\n * @param start.firstLine - the line of the first key\n * @param report - the report of parseAt(), or null\n * @yields the events\n * @returns nothing\n */\nfunction* memberEnd(\n keys: MemberKeys,\n block: number,\n line: number,\n start: { outer: string; firstLine: number },\n report: ImportReportBuilder | null,\n): Generator<AspectEvent, void, undefined> {\n const { others, firstBlock, deep } = keys;\n if (firstBlock !== null) {\n for (const other of others) {\n if (other.text !== null) {\n parseAt(other.text, other.line, null);\n }\n }\n if (others.length > 0) {\n yield {\n kind: \"extraKeys\",\n aspect: firstBlock,\n block,\n keys: others.map((o) => o.key),\n line: others[0].line,\n };\n }\n return;\n }\n if (deep > 0) {\n yield { kind: \"deep\", aspect: others[0].key, block, depth: deep, line };\n return;\n }\n const members = others.map((o) => [JSON.stringify(o.key), o.text ?? \"\"].join(\":\"));\n const text = `${start.outer}${members.join(\",\")}}`;\n const parsed = parseAt(text, start.firstLine, report);\n yield { kind: \"member\", block, value: parsed.value, exact: parsed.exact, text, line };\n}\n\n/**\n * Read the elements of a block whose opening bracket was consumed, through its closing bracket.\n * @param cur - the cursor after the opening bracket\n * @param aspect - the block's aspect\n * @param block - the member's position among the members\n * @param report - the report of parseAt(), or null\n * @yields an element (or deep) event per element\n * @returns nothing\n */\nasync function* blockElements(\n cur: ChunkCursor,\n aspect: string,\n block: number,\n report: ImportReportBuilder | null,\n): AsyncGenerator<AspectEvent, void, undefined> {\n if ((await cur.peek()) === \"]\") {\n cur.pos++;\n return;\n }\n for (;;) {\n const e = await cur.peek();\n if (e === \"\") {\n throw new JsonScanError(`the document ends inside the \"${aspect}\" block`, cur.line());\n }\n const elementLine = cur.line();\n cur.beginCapture();\n await cur.skipValue();\n if (cur.deepest > MAX_ELEMENT_DEPTH) {\n cur.dropCapture();\n yield { kind: \"deep\", aspect, block, depth: cur.deepest, line: elementLine };\n } else {\n const text = cur.endCapture();\n const parsed = parseAt(text, elementLine, report);\n yield {\n kind: \"element\",\n aspect,\n block,\n value: parsed.value,\n exact: parsed.exact,\n text,\n line: elementLine,\n };\n }\n const next = await cur.peek();\n if (next === \",\") {\n cur.pos++;\n continue;\n }\n if (next === \"]\") {\n cur.pos++;\n return;\n }\n throw new JsonScanError(\n next === \"\"\n ? `the document ends inside the \"${aspect}\" block`\n : `expected \",\" or \"]\" in the \"${aspect}\" block, found \"${next}\"`,\n cur.line(),\n );\n }\n}\n\n/**\n * Report an element or member the scanner refused for its depth (E_BAD_ASPECT_BLOCK; skipped).\n * @param report - the report\n * @param event - the \"deep\" event\n * @category Plugin helpers\n */\nexport function reportTooDeep(report: ImportReportBuilder, event: Extract<AspectEvent, { kind: \"deep\" }>): void {\n report.error(\n \"parse-error\",\n BAD_ASPECT_BLOCK_CODE,\n `${event.aspect === null ? \"a member of the document\" : `an element of \"${event.aspect}\"`} is nested ${event.depth} levels deep, more than the ${MAX_ELEMENT_DEPTH} a graph keeps; skipped`,\n { line: event.line, element: event.aspect },\n );\n}\n\n/**\n * The precision callback of plainJson() for an aspect kept verbatim in the metadata: W_PRECISION\n * once, since the digits stored there are not the file's.\n * @param report - the report\n * @param where - where the aspect is kept (meta.extra.cx, ...)\n * @returns the callback\n * @category Plugin helpers\n */\nexport function keptPrecision(report: ImportReportBuilder, where: string): (digits: string) => void {\n return (digits) => {\n report.warnOnce(\n \"precision\",\n PRECISION_CODE,\n `an aspect kept in ${where} holds ${digits}, beyond 2^53; it is kept as the nearest double`,\n { element: where },\n `${PRECISION_CODE}:kept`,\n );\n };\n}\n\n/**\n * Warn once (W_MULTI_ASPECT_FRAGMENT) about a block that shares its array member with another\n * aspect (`{\"nodes\": [...], \"edges\": [...]}`); a block of its own needs nothing.\n * @param report - the report\n * @param event - the \"block\" event\n * @category Plugin helpers\n */\nexport function reportSharedBlock(report: ImportReportBuilder, event: Extract<AspectEvent, { kind: \"block\" }>): void {\n if (event.shared) {\n report.warnOnce(\n \"coercion\",\n MULTI_ASPECT_FRAGMENT_CODE,\n `the \"${event.aspect}\" aspect shares an array member with another aspect; each is read as its own fragment`,\n { line: event.line, element: event.aspect },\n );\n }\n}\n\n// ============================================================ the CX id rule (design section 1.0.2)\n\n/** How an id was spelled, when it was not a plain safe integer. */\ntype CxIdNote = \"text\" | \"precision\" | null;\n\n/** A CX id, decided per id: a safe integer is a number, a larger one its exact digits. */\ninterface CxId {\n /** The node or edge id. */\n readonly id: NodeId;\n /** \"text\": spelled as a string or a non-integer literal (W_ID_TEXT_TYPE); \"precision\": beyond 2^53 (W_PRECISION). */\n readonly note: CxIdNote;\n}\n\nconst DECIMAL_INTEGER_TEXT = /^-?(0|[1-9][0-9]*)$/;\n\n/**\n * Apply the CX id rule (design section 1.0.2) to a parsed value: a safe integer is the id; an\n * integer beyond 2^53 is its exact digit string; a string spelling a decimal integer is that\n * integer (as Jackson coerces it); anything else is E_INVALID_ID.\n * @param raw - the parsed value\n * @param inexactLiteral - whether the number was written as a non-integer literal (`5.0`, `1e3`)\n * @returns the id and how it was spelled\n * @category Plugin helpers\n */\nexport function cxId(raw: unknown, inexactLiteral = false): CxId {\n if (raw instanceof ExactInteger) {\n return { id: raw.digits, note: \"precision\" };\n }\n if (typeof raw === \"number\") {\n if (Number.isSafeInteger(raw)) {\n return { id: raw === 0 ? 0 : raw, note: inexactLiteral ? \"text\" : null };\n }\n throw invalidCxId(raw, Number.isInteger(raw) ? \"an integer beyond 2^53 written inexactly\" : \"not an integer\");\n }\n if (typeof raw === \"string\" && raw !== \"-0\" && DECIMAL_INTEGER_TEXT.test(raw)) {\n const n = Number(raw);\n return Number.isSafeInteger(n) ? { id: n, note: \"text\" } : { id: raw, note: \"precision\" };\n }\n throw invalidCxId(raw, \"a CX id is an integer\");\n}\n\n/**\n * The E_INVALID_ID error of a rejected CX id.\n * @param raw - the value\n * @param reason - why\n * @returns the error\n */\nfunction invalidCxId(raw: unknown, reason: string): GraphFormatError {\n const shown = typeof raw === \"string\" || typeof raw === \"number\" ? JSON.stringify(raw) : typeof raw;\n return new GraphFormatError(\"E_INVALID_ID\", `invalid id ${raw === null ? \"null\" : shown}: ${reason}`, {\n reason,\n });\n}\n\n/** A number literal at lastIndex (sticky). */\nconst NUMBER_LITERAL = /-?\\d[\\d.eE+-]*/y;\n\n/**\n * Whether the element text writes a top-level key's number as a non-integer literal (`\"id\": 5.0`,\n * `\"@id\": 1e3`). A lexical check, so the parsed value (5) cannot tell. Only a key of the element\n * itself counts, not one nested in its `v` (where `s` and `id` are ordinary attribute names).\n * @param text - the element's JSON text\n * @param key - the key\n * @param keyDepth - the nesting depth of the element's own keys: 1, or 2 for an element written as\n * a member's single object (`{\"nodes\": {\"@id\": 1.0}}`)\n * @returns true when the literal holds a fraction or an exponent\n * @category Plugin helpers\n */\nexport function inexactLiteral(text: string, key: string, keyDepth = 1): boolean {\n const quoted = JSON.stringify(key);\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const c = text[i];\n if (c === \"{\" || c === \"[\") {\n depth++;\n } else if (c === \"}\" || c === \"]\") {\n depth--;\n } else if (c === '\"') {\n const start = i;\n i = stringEnd(text, i + 1);\n // the key itself (a string at depth 1 followed by a colon), not a value spelled like it\n const colon = skipSpace(text, i + 1);\n if (depth === keyDepth && text.startsWith(quoted, start) && text[colon] === \":\") {\n return inexactNumberAt(text, skipSpace(text, colon + 1));\n }\n }\n i++;\n }\n return false;\n}\n\n/**\n * Whether a number literal at an offset holds a fraction or an exponent.\n * @param text - the text\n * @param at - the offset\n * @returns true when it does; false for an integer or no number\n */\nfunction inexactNumberAt(text: string, at: number): boolean {\n NUMBER_LITERAL.lastIndex = at;\n const match = NUMBER_LITERAL.exec(text);\n return match !== null && /[.eE]/.test(match[0]);\n}\n\n// ============================================================ Cytoscape positions (design section 1.0.2)\n\n/**\n * The name of the position column of every Cytoscape-family importer.\n * @category Plugin helpers\n */\nexport const POSITION_COLUMN = \"position\";\n\n/** The name of the stacking-order column (Cytoscape's NODE_Z_LOCATION). */\nconst Z_COLUMN = \"z\";\n\n/**\n * The position column of a Cytoscape-family importer: f64 x3, y-up, 2 source dimensions.\n * @param format - the importer's format name\n * @param sourceDims - 3 when z goes into the position (zAs \"position\")\n * @returns the declaration\n * @category Plugin helpers\n */\nexport function positionDecl(format: string, sourceDims = 2): ColumnDecl {\n return {\n name: POSITION_COLUMN,\n dtype: \"f64\",\n components: 3,\n role: \"position\",\n mutable: true,\n nullable: true,\n extra: { sourceDims, units: \"file\" },\n origin: { format, namespace: \"cytoscape\" },\n };\n}\n\n/**\n * The stacking-order column `z` (f64, origin namespace \"cytoscape\").\n * @param format - the importer's format name\n * @returns the declaration\n * @category Plugin helpers\n */\nexport function zDecl(format: string): ColumnDecl {\n return {\n name: Z_COLUMN,\n dtype: \"f64\",\n nullable: true,\n origin: { format, id: null, namespace: \"cytoscape\" },\n extra: { cytoscape: \"z\" },\n };\n}\n\n/**\n * The head of an input as text for a sniff: UTF-16 by its byte order mark, else UTF-8, never\n * failing, without the BOM.\n * @param head - the first bytes\n * @returns the text\n * @category Plugin helpers\n */\nexport function headText(head: Uint8Array): string {\n let encoding = \"utf-8\";\n if (head.byteLength >= 2 && head[0] === 0xff && head[1] === 0xfe) {\n encoding = \"utf-16le\";\n } else if (head.byteLength >= 2 && head[0] === 0xfe && head[1] === 0xff) {\n encoding = \"utf-16be\";\n }\n return new TextDecoder(encoding).decode(head).replace(/^\\uFEFF/, \"\");\n}\n\n/**\n * Cytoscape's screen y (growing downward) as graph-io's y (growing upward), and back: a negation\n * that never yields -0.\n * @param y - the coordinate\n * @returns the flipped coordinate\n * @category Plugin helpers\n */\nexport function flipY(y: number): number {\n return y === 0 ? 0 : -y;\n}\n\n// ============================================================ stream structure shared by CX1 and CX2\n\n/** The aspect names of the stream structure, not of the network. */\nconst STRUCTURE_ASPECTS: ReadonlySet<string> = new Set([\"metaData\", \"status\", \"numberVerification\"]);\n\n/**\n * The order, metadata and status rules CX1 and CX2 share (design section 1.0.1): a third\n * `metaData` or an aspect after the post-metadata or after `status` is W_ASPECT_ORDER; declared\n * element counts are compared with what was read (W_COUNT_MISMATCH); `status.success: false` is\n * fatal (E_STATUS_FAILED) and `success: true` with an error text a warning (W_STATUS_WARNING).\n * @category Plugin helpers\n */\nexport class CxStructure {\n private readonly report: ImportReportBuilder;\n\n /** Elements read per aspect name. */\n readonly counts = new Map<string, number>();\n\n /** Blocks read per aspect name. */\n readonly blocks = new Map<string, number>();\n\n /** The element counts the metadata declares, by aspect name (a later declaration wins). */\n private readonly declared = new Map<string, number>();\n\n private metaDataBlocks = 0;\n\n private networkBlocks = 0;\n\n private postMetadata = false;\n\n private statusSeen = false;\n\n /** The status element, when one was read. */\n status: unknown = undefined;\n\n /**\n * Whether a status block was read.\n * @returns true once a status block began\n */\n get hasStatus(): boolean {\n return this.statusSeen;\n }\n\n /** The name an aspect is counted under (CX1 reads old Cytoscape names under their cy names). */\n private readonly canonical: (name: string) => string;\n\n /**\n * Create the checker.\n * @param report - the report\n * @param canonical - the name an aspect named in metaData is counted under (default: itself)\n */\n constructor(report: ImportReportBuilder, canonical: (name: string) => string = (name) => name) {\n this.report = report;\n this.canonical = canonical;\n }\n\n /**\n * Record the start of a block (or a member holding one aspect).\n * @param aspect - the aspect name\n * @param line - its line\n */\n block(aspect: string, line: number): void {\n this.blocks.set(aspect, (this.blocks.get(aspect) ?? 0) + 1);\n if (this.statusSeen) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `the \"${aspect}\" block comes after the status block, which must be last; it is read`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:status`,\n );\n }\n if (aspect === \"metaData\") {\n this.metaDataBlocks++;\n if (this.networkBlocks > 0) {\n this.postMetadata = true;\n }\n if (this.metaDataBlocks > 2) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `${this.metaDataBlocks} metaData blocks; a document has at most two (pre and post)`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:metaData`,\n );\n }\n return;\n }\n if (aspect === \"status\") {\n this.statusSeen = true;\n return;\n }\n if (STRUCTURE_ASPECTS.has(aspect)) {\n return;\n }\n this.networkBlocks++;\n if (this.postMetadata) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n `the \"${aspect}\" block comes after the post-metadata, where only status is allowed; it is read`,\n { line, element: aspect },\n `${ASPECT_ORDER_CODE}:post`,\n );\n }\n }\n\n /**\n * Record one element of a block; metaData and status elements are checked here.\n * @param aspect - the aspect name\n * @param value - the parsed element\n * @param line - its line\n */\n element(aspect: string, value: unknown, line: number): void {\n this.counts.set(aspect, (this.counts.get(aspect) ?? 0) + 1);\n if (aspect === \"metaData\") {\n this.readCount(value, line);\n return;\n }\n if (aspect === \"status\") {\n this.readStatus(value, line);\n }\n }\n\n /**\n * Record the element count a metaData element declares; a count that is not an integer (text,\n * a fraction) or that names no aspect is W_COUNT_MISMATCH and not checked.\n * @param value - the metaData element\n * @param line - its line\n */\n private readCount(value: unknown, line: number): void {\n if (!isRecord(value) || value.elementCount === undefined || value.elementCount === null) {\n return;\n }\n const raw = value.elementCount;\n let count: number | null = null;\n if (raw instanceof ExactInteger) {\n count = Number(raw.digits);\n } else if (typeof raw === \"number\" && Number.isInteger(raw)) {\n count = raw;\n }\n if (typeof value.name !== \"string\" || count === null) {\n const shownCount = raw instanceof ExactInteger ? raw.digits : JSON.stringify(raw);\n this.report.warning(\n \"validation-error\",\n COUNT_MISMATCH_CODE,\n `a metaData element declares the elementCount ${shownCount} for the name ${JSON.stringify(value.name) ?? \"undefined\"}; not a count of a named aspect, not checked`,\n { line, element: \"metaData\" },\n );\n return;\n }\n this.declared.set(this.canonical(value.name), count);\n }\n\n /**\n * Check a status element: success false is fatal, an error text with success true a warning.\n * @param value - the element\n * @param line - its line\n */\n private readStatus(value: unknown, line: number): void {\n if (this.status !== undefined) {\n this.report.warnOnce(\n \"validation-error\",\n ASPECT_ORDER_CODE,\n \"more than one status element; the last is used\",\n { line, element: \"status\" },\n `${ASPECT_ORDER_CODE}:status2`,\n );\n }\n this.status = value;\n if (!isRecord(value)) {\n return;\n }\n const error = typeof value.error === \"string\" ? value.error : \"\";\n if (value.success === false) {\n const message = `the producer marked the document as failed${error.length > 0 ? `: ${error}` : \"\"}`;\n this.report.error(\"validation-error\", STATUS_FAILED_CODE, message, { line, element: \"status\" });\n throw this.report.abort(message, { code: STATUS_FAILED_CODE });\n }\n if (value.success === true && error.length > 0) {\n this.report.warning(\"validation-error\", STATUS_WARNING_CODE, `the producer reports a warning: ${error}`, {\n line,\n element: \"status\",\n });\n }\n }\n\n /**\n * Whether the status element is one object with a boolean success.\n * @returns true for a well-formed status\n */\n statusWellFormed(): boolean {\n return isRecord(this.status) && typeof this.status.success === \"boolean\";\n }\n\n /** Compare the declared element counts with what was read (W_COUNT_MISMATCH per aspect). */\n checkCounts(): void {\n for (const [name, declared] of this.declared) {\n const read = this.counts.get(name) ?? 0;\n if (read !== declared) {\n this.report.warning(\n \"validation-error\",\n COUNT_MISMATCH_CODE,\n `metaData declares ${declared} \"${name}\" element${plural(declared)}; ${read} ${agree(read, \"was\", \"were\")} read`,\n { element: name },\n );\n }\n }\n }\n}\n\n/**\n * Whether a value is a plain JSON object.\n * @param value - any value\n * @returns true for a non-null, non-array object\n * @category Plugin helpers\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof ExactInteger);\n}\n\n/**\n * Declare a column under a name no column of the table holds yet: a per-element visual property\n * whose name an attribute already has gets `<name>#2` (W_COLUMN_RENAMED), even when the two share a\n * shape, so the visual value never overwrites the attribute.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the declaration\n * @param report - the report the rename is recorded in\n * @returns the handle\n * @category Plugin helpers\n */\nexport function declareFresh(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n decl: ColumnDecl,\n report: ImportReportBuilder,\n): ColumnHandle {\n const taken = (name: string): boolean =>\n (domain === \"node\" ? sink.nodeColumn(name) : sink.edgeColumn(name)) !== INVALID_INDEX;\n let fresh = decl;\n if (taken(decl.name)) {\n fresh = { ...decl, name: uniqueColumnName(decl.name, null, taken) };\n report.warning(\n \"coercion\",\n COLUMN_RENAMED_CODE,\n `${domain} column \"${decl.name}\" renamed to \"${fresh.name}\": an attribute holds that name`,\n { element: decl.name },\n );\n }\n return declareResolved(sink, domain, fresh, report).handle;\n}\n"],"names":["members","inexactLiteral"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { GraphFormatError } from "@graphty/graph-format";
2
- import { a as checkCapabilities, L as LOSS, at as COLUMN_AS_PROPERTY_VALUE_CODE, au as ROLE_ASSUMED_CODE, av as formatNumber } from "./export-CVi12U7J.js";
2
+ import { a as checkCapabilities, L as LOSS, at as COLUMN_AS_PROPERTY_VALUE_CODE, au as ROLE_ASSUMED_CODE, av as formatNumber } from "./export-ZucVKiU4.js";
3
3
  const OBO_NODE_COLUMNS = Object.freeze({
4
4
  type: { dtype: "dict" },
5
5
  name: { dtype: "string", role: "label" },
@@ -113,7 +113,8 @@ const SUPERSEDED = /* @__PURE__ */ new Set([
113
113
  LOSS.GRAPH_ATTRIBUTES
114
114
  ]);
115
115
  const SLOT_ROLES = /* @__PURE__ */ new Set(["label", "kind"]);
116
- const ROLE_NAMES = Object.freeze({ label: "name", kind: "relation" });
116
+ const NODE_ROLE_NAMES = Object.freeze({ label: "name" });
117
+ const EDGE_ROLE_NAMES = Object.freeze({ kind: "relation" });
117
118
  const UNWRITTEN_ROLES = /* @__PURE__ */ new Set([
118
119
  "directed",
119
120
  "pair",
@@ -253,8 +254,13 @@ function capabilityNotes(snapshot, common, spec) {
253
254
  const { label, note } = spec;
254
255
  let fatal = null;
255
256
  const kind = snapshot.edges.byRole("kind");
256
- for (const gen of checkCapabilities(snapshot, spec.caps, common, { roles: SLOT_ROLES, roleNames: ROLE_NAMES })) {
257
- if (SUPERSEDED.has(gen.code) || gen.code === LOSS.COLUMN_NAME_CHANGED && !isSlotRename(gen, label, kind)) {
257
+ const extras = {
258
+ roles: SLOT_ROLES,
259
+ nodeRoleNames: label === null ? {} : NODE_ROLE_NAMES,
260
+ edgeRoleNames: kind !== null && isText(kind) ? EDGE_ROLE_NAMES : {}
261
+ };
262
+ for (const gen of checkCapabilities(snapshot, spec.caps, common, extras)) {
263
+ if (SUPERSEDED.has(gen.code)) {
258
264
  continue;
259
265
  }
260
266
  if (gen.code === LOSS.MIXED_DIRECTION_ERROR && fatal === null) {
@@ -294,10 +300,6 @@ function slotlessRoleNotes(snapshot, spec) {
294
300
  );
295
301
  }
296
302
  }
297
- function isSlotRename(gen, label, kind) {
298
- const isLabel = label !== null && gen.column === label.meta.name;
299
- return isLabel || kind !== null && gen.column === kind.meta.name && isText(kind);
300
- }
301
303
  function emptyColumnNotes(snapshot, rows, frameless, written, note) {
302
304
  const tables = [
303
305
  [
@@ -354,4 +356,4 @@ export {
354
356
  textOf as t,
355
357
  valuesOf as v
356
358
  };
357
- //# sourceMappingURL=ontology-export-b0GLNIj-.js.map
359
+ //# sourceMappingURL=ontology-export-BRErBO6J.js.map