@graphty/graph-io 0.3.3 → 0.3.5

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 (87) hide show
  1. package/README.md +17 -4
  2. package/dist/chunks/{escape-DXbOoLKl.js → escape-BjmIaFFo.js} +2 -2
  3. package/dist/chunks/{escape-DXbOoLKl.js.map → escape-BjmIaFFo.js.map} +1 -1
  4. package/dist/chunks/{importer-BELrICWM.js → importer-BOxwmef_.js} +352 -19
  5. package/dist/chunks/importer-BOxwmef_.js.map +1 -0
  6. package/dist/chunks/{importer-B9qF4e32.js → importer-BddBW3sf.js} +2 -2
  7. package/dist/chunks/{importer-B9qF4e32.js.map → importer-BddBW3sf.js.map} +1 -1
  8. package/dist/chunks/{importer-xoHpRG5x.js → importer-BtHFICnf.js} +2 -2
  9. package/dist/chunks/{importer-xoHpRG5x.js.map → importer-BtHFICnf.js.map} +1 -1
  10. package/dist/chunks/{importer-C2caVvl6.js → importer-DfI8POXb.js} +272 -63
  11. package/dist/chunks/importer-DfI8POXb.js.map +1 -0
  12. package/dist/csv.js +1 -1
  13. package/dist/dot.js +1 -1
  14. package/dist/gexf.js +1 -1
  15. package/dist/gml.js +1 -1
  16. package/dist/graph-io.js +11 -10
  17. package/dist/graph-io.js.map +1 -1
  18. package/dist/graphml.js +1 -1
  19. package/dist/json.js +1 -1
  20. package/dist/neo4j.js +51 -16
  21. package/dist/neo4j.js.map +1 -1
  22. package/dist/pajek.js +1 -1
  23. package/dist/src/common/escape.d.ts +4 -3
  24. package/dist/src/common/escape.d.ts.map +1 -1
  25. package/dist/src/common/escape.js +5 -4
  26. package/dist/src/common/escape.js.map +1 -1
  27. package/dist/src/formats/json/dialect.d.ts +21 -4
  28. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  29. package/dist/src/formats/json/dialect.js +29 -4
  30. package/dist/src/formats/json/dialect.js.map +1 -1
  31. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  32. package/dist/src/formats/json/exporter.js +27 -7
  33. package/dist/src/formats/json/exporter.js.map +1 -1
  34. package/dist/src/formats/json/importer.d.ts +17 -4
  35. package/dist/src/formats/json/importer.d.ts.map +1 -1
  36. package/dist/src/formats/json/importer.js +356 -8
  37. package/dist/src/formats/json/importer.js.map +1 -1
  38. package/dist/src/formats/json/index.d.ts +1 -1
  39. package/dist/src/formats/json/index.d.ts.map +1 -1
  40. package/dist/src/formats/json/index.js +1 -1
  41. package/dist/src/formats/json/index.js.map +1 -1
  42. package/dist/src/formats/neo4j/exporter.d.ts +3 -1
  43. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  44. package/dist/src/formats/neo4j/exporter.js +34 -6
  45. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  46. package/dist/src/formats/neo4j/importer.d.ts +16 -3
  47. package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
  48. package/dist/src/formats/neo4j/importer.js +49 -11
  49. package/dist/src/formats/neo4j/importer.js.map +1 -1
  50. package/dist/src/formats/neo4j/index.d.ts +3 -3
  51. package/dist/src/formats/neo4j/index.d.ts.map +1 -1
  52. package/dist/src/formats/neo4j/index.js +3 -3
  53. package/dist/src/formats/neo4j/index.js.map +1 -1
  54. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  55. package/dist/src/formats/pajek/exporter.js +3 -2
  56. package/dist/src/formats/pajek/exporter.js.map +1 -1
  57. package/dist/src/formats/pajek/importer.d.ts +14 -8
  58. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  59. package/dist/src/formats/pajek/importer.js +233 -61
  60. package/dist/src/formats/pajek/importer.js.map +1 -1
  61. package/dist/src/formats/pajek/syntax.d.ts +39 -8
  62. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  63. package/dist/src/formats/pajek/syntax.js +81 -11
  64. package/dist/src/formats/pajek/syntax.js.map +1 -1
  65. package/dist/src/index.d.ts +2 -2
  66. package/dist/src/index.d.ts.map +1 -1
  67. package/dist/src/index.js +1 -1
  68. package/dist/src/index.js.map +1 -1
  69. package/dist/src/sniff.d.ts +3 -3
  70. package/dist/src/sniff.d.ts.map +1 -1
  71. package/dist/src/sniff.js.map +1 -1
  72. package/package.json +2 -2
  73. package/src/common/escape.ts +5 -4
  74. package/src/formats/json/dialect.ts +40 -6
  75. package/src/formats/json/exporter.ts +28 -7
  76. package/src/formats/json/importer.ts +411 -21
  77. package/src/formats/json/index.ts +7 -1
  78. package/src/formats/neo4j/exporter.ts +43 -6
  79. package/src/formats/neo4j/importer.ts +56 -11
  80. package/src/formats/neo4j/index.ts +10 -3
  81. package/src/formats/pajek/exporter.ts +3 -1
  82. package/src/formats/pajek/importer.ts +284 -60
  83. package/src/formats/pajek/syntax.ts +98 -12
  84. package/src/index.ts +2 -0
  85. package/src/sniff.ts +3 -3
  86. package/dist/chunks/importer-BELrICWM.js.map +0 -1
  87. package/dist/chunks/importer-C2caVvl6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"graph-io.js","sources":["../src/sniff.ts","../src/registry.ts"],"sourcesContent":["/**\n * Format detection (design sections 8.2 and 13.1: the registry's `sniff()`, the successor of\n * graphty-element's `format-detection.ts`): which of the registered importers a file is for, from\n * any combination of a filename (its extension), a MIME type and the first bytes of the content.\n *\n * Every importer declares `extensions`, `mimeTypes` and a `sniff(head)` content confidence in\n * 0..1 (design section 8.4); this module combines them into one ranked answer so that content\n * always beats a hint: a `.csv` whose first line is a neo4j-admin header is Neo4j, a `.xml` is\n * GEXF or GraphML by its root element, a `.txt` holding `graph [` is GML. The confidence of a\n * candidate is\n *\n * - `0.5 + 0.35 * content + 0.1 * [extension matches] + 0.05 * [MIME type matches]` when the\n * importer recognises the content (`content > 0`), so a content match always scores at least\n * 0.5 and at most 1;\n * - `0.3 * [extension matches] + 0.1 * [MIME type matches]` when the content is absent or the\n * importer rejects it, so a hint alone never reaches 0.5;\n * - nothing (the format is not a candidate) otherwise.\n *\n * Ties are broken by the order the importers were registered in, which is why the default\n * registry lists the common formats first. The JSON dialect is sniffed from the head as a hint\n * (the importer's detection on the parsed document is authoritative, design section 8.2).\n */\n\nimport { type JsonDialect, sniffJsonDialect } from \"./formats/json/dialect.js\";\nimport { type GraphImporter } from \"./types.js\";\n\n/** The format names of the eight built-in importers and exporters. */\nexport type GraphFormatName = \"gexf\" | \"graphml\" | \"gml\" | \"dot\" | \"pajek\" | \"csv\" | \"json\" | \"neo4j\";\n\n/**\n * The built-in format names in the default registry's order, which is also the tie-break order of\n * sniffing: the more common format wins an extension two formats claim (`.xml` GraphML before GEXF,\n * `.csv` CSV before Neo4j) when the content does not decide.\n */\nexport const GRAPH_FORMATS: readonly GraphFormatName[] = Object.freeze([\n \"json\",\n \"graphml\",\n \"gexf\",\n \"csv\",\n \"gml\",\n \"dot\",\n \"pajek\",\n \"neo4j\",\n]);\n\n/** How many bytes of the input the sniffers look at; the registry reads no more than this before deciding. */\nexport const SNIFF_HEAD_BYTES = 8192;\n\n/** What is known about an input before it is read. */\nexport interface SniffHints {\n /** A file name or path; only its extension is used. */\n readonly filename?: string | null | undefined;\n /** A MIME type, with or without parameters (`text/csv; charset=utf-8`). */\n readonly mimeType?: string | null | undefined;\n /** The first bytes (or characters) of the content. */\n readonly head?: Uint8Array | string | null | undefined;\n}\n\n/** One ranked candidate of a sniff. */\nexport interface SniffResult {\n /** The importer's format name. */\n readonly format: string;\n /** The combined confidence in 0..1 (at least 0.5 when the content was recognised). */\n readonly confidence: number;\n /** The importer's own content confidence, 0 when no head was given or it rejected the head. */\n readonly content: number;\n /** Whether the filename's extension is one the importer claims. */\n readonly extension: boolean;\n /** Whether the MIME type is one the importer claims. */\n readonly mimeType: boolean;\n /** For the JSON format: the dialect the head suggests, or null when unknown; always null for other formats. */\n readonly dialect: JsonDialect | null;\n}\n\n/**\n * The lower-cased extension of a file name or path, with its dot.\n * @param filename - the name or path\n * @returns the extension (`.gexf`), or null when the name has none\n */\nexport function extensionOf(filename: string): string | null {\n const base = filename.slice(Math.max(filename.lastIndexOf(\"/\"), filename.lastIndexOf(\"\\\\\")) + 1);\n const dot = base.lastIndexOf(\".\");\n if (dot <= 0 || dot === base.length - 1) {\n return null;\n }\n return base.slice(dot).toLowerCase();\n}\n\n/**\n * A MIME type without parameters, lower-cased, for comparison with an importer's list.\n * @param mimeType - the type as received (`Text/CSV; charset=utf-8`)\n * @returns the bare type (`text/csv`)\n */\nexport function normalizeMimeType(mimeType: string): string {\n const semicolon = mimeType.indexOf(\";\");\n return (semicolon < 0 ? mimeType : mimeType.slice(0, semicolon)).trim().toLowerCase();\n}\n\n/**\n * The head as bytes for the importers' sniff functions: at most SNIFF_HEAD_BYTES, a string\n * encoded as UTF-8, a UTF-16 head with a byte order mark transcoded to UTF-8.\n * @param head - the head as given\n * @returns the bytes\n */\nexport function headBytes(head: Uint8Array | string): Uint8Array {\n if (typeof head === \"string\") {\n return new TextEncoder().encode(head.slice(0, SNIFF_HEAD_BYTES)).subarray(0, SNIFF_HEAD_BYTES);\n }\n if (head.byteLength >= 2 && ((head[0] === 0xff && head[1] === 0xfe) || (head[0] === 0xfe && head[1] === 0xff))) {\n // UTF-16 with a BOM (Excel's \"Unicode text\"): the sniffers read UTF-8, so transcode the head\n const encoding = head[0] === 0xff ? \"utf-16le\" : \"utf-16be\";\n return headBytes(new TextDecoder(encoding).decode(head.subarray(0, SNIFF_HEAD_BYTES)));\n }\n return head.byteLength > SNIFF_HEAD_BYTES ? head.subarray(0, SNIFF_HEAD_BYTES) : head;\n}\n\n/**\n * Rank the registered importers for an input by the rule of the module comment: every importer\n * whose content confidence is positive or whose extension / MIME type matches is a candidate,\n * ordered by confidence (ties in registration order).\n * @param hints - what is known about the input\n * @param importers - the registered importers, in registration order\n * @returns the candidates, best first; empty when nothing matches\n */\nexport function rankFormats(hints: SniffHints, importers: Iterable<GraphImporter>): readonly SniffResult[] {\n const extension = typeof hints.filename === \"string\" ? extensionOf(hints.filename) : null;\n const mime = typeof hints.mimeType === \"string\" ? normalizeMimeType(hints.mimeType) : null;\n const head = hints.head === undefined || hints.head === null ? null : headBytes(hints.head);\n const candidates: { readonly result: SniffResult; readonly rank: number }[] = [];\n let rank = 0;\n for (const importer of importers) {\n const extensionMatch = extension !== null && importer.extensions.some((e) => e.toLowerCase() === extension);\n const mimeMatch = mime !== null && importer.mimeTypes.some((m) => m.toLowerCase() === mime);\n let content = 0;\n if (head !== null && head.byteLength > 0 && typeof importer.sniff === \"function\") {\n content = clamp(importer.sniff(head));\n }\n let confidence: number;\n if (content > 0) {\n confidence = 0.5 + 0.35 * content + (extensionMatch ? 0.1 : 0) + (mimeMatch ? 0.05 : 0);\n } else if (extensionMatch || mimeMatch) {\n confidence = (extensionMatch ? 0.3 : 0) + (mimeMatch ? 0.1 : 0);\n } else {\n continue;\n }\n const dialect = importer.format === \"json\" && head !== null ? sniffJsonDialectHead(head) : null;\n const result: SniffResult = Object.freeze({\n format: importer.format,\n confidence: Math.min(1, confidence),\n content,\n extension: extensionMatch,\n mimeType: mimeMatch,\n dialect,\n });\n candidates.push({ result, rank: rank++ });\n }\n candidates.sort((a, b) => b.result.confidence - a.result.confidence || a.rank - b.rank);\n return candidates.map((c) => c.result);\n}\n\n/**\n * The best candidate of rankFormats(), or null when no importer matches.\n * @param hints - what is known about the input\n * @param importers - the registered importers, in registration order\n * @returns the best candidate, or null\n */\nexport function sniffFormat(hints: SniffHints, importers: Iterable<GraphImporter>): SniffResult | null {\n const ranked = rankFormats(hints, importers);\n return ranked.length > 0 ? ranked[0] : null;\n}\n\n/**\n * The JSON dialect a head suggests. A head that parses as a whole document is classified exactly\n * (sniffJsonDialect); a truncated head is scanned for its top-level keys, the keys of `graph` and\n * `options`, and the keys of the first object under `nodes` / `edges` / `links`, and the same\n * rule is applied to that skeleton. The result is a hint: the importer decides on the parsed\n * document.\n * @param head - the first bytes or characters of the document\n * @returns the dialect, or null when the head is not a JSON graph document\n */\nexport function sniffJsonDialectHead(head: Uint8Array | string): JsonDialect | null {\n const text = typeof head === \"string\" ? head : new TextDecoder(\"utf-8\", { fatal: false }).decode(head);\n const body = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n const trimmed = body.trimStart();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) {\n return null;\n }\n let root: unknown;\n try {\n root = JSON.parse(trimmed);\n } catch {\n root = skeletonOf(trimmed);\n }\n return sniffJsonDialect(root);\n}\n\n/** Where a key was seen while scanning a truncated head. */\ntype KeyPath = \"\" | \"graph\" | \"options\" | \"nodes[0]\" | \"edges[0]\" | \"links[0]\";\n\n/**\n * A partial document rebuilt from the keys a truncated head reveals: every key gets a placeholder\n * value of the shape the dialect rule tests for (an object for `graph`, `options`, `attributes`\n * and `data`, an array holding the first element's skeleton for `nodes` / `edges` / `links` /\n * `graphs`, `true` otherwise), so sniffJsonDialect() can classify it.\n * @param text - the head, starting with `{` or `[`\n * @returns the skeleton, or null when the scan finds no key at all\n */\nfunction skeletonOf(text: string): unknown {\n const keys = scanKeys(text);\n if (text.startsWith(\"[\")) {\n // a cut array whose first element has not been seen tells nothing (a whole `[]` is Cytoscape)\n const first = keys.get(\"nodes[0]\");\n return first === undefined ? null : [objectOf(first)];\n }\n const rootKeys = keys.get(\"\");\n if (rootKeys === undefined) {\n return null;\n }\n const root: Record<string, unknown> = {};\n for (const key of rootKeys) {\n switch (key) {\n case \"graph\":\n case \"options\":\n root[key] = objectOf(keys.get(key));\n break;\n case \"graphs\":\n root[key] = [];\n break;\n case \"nodes\":\n case \"edges\":\n case \"links\": {\n const first = keys.get(`${key}[0]`);\n root[key] = first === undefined ? [] : [objectOf(first)];\n break;\n }\n default:\n root[key] = true;\n break;\n }\n }\n return root;\n}\n\n/**\n * An object holding placeholder values for a key set: `{}` for the keys the rule tests with\n * isJsonObject (`attributes`, `data`), `true` otherwise.\n * @param names - the keys, possibly undefined\n * @returns the object\n */\nfunction objectOf(names: ReadonlySet<string> | undefined): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (names !== undefined) {\n for (const name of names) {\n out[name] = name === \"attributes\" || name === \"data\" ? {} : true;\n }\n }\n return out;\n}\n\n/**\n * Scan a (possibly truncated) JSON text for the keys at the paths the dialect rule reads. The\n * scanner tracks a container stack (the key each object sits under, the index of each array\n * element) and records a key when it is at one of the watched paths; a head cut inside a string\n * or a number simply ends the scan. For a top-level array the first element's keys are recorded\n * under `nodes[0]`.\n * @param text - the head, starting with `{` or `[`\n * @returns key sets by path\n */\nfunction scanKeys(text: string): Map<KeyPath, Set<string>> {\n const found = new Map<KeyPath, Set<string>>();\n // one frame per open container: its kind, the key or index it sits under, and the element index\n const kinds: (\"object\" | \"array\")[] = [];\n const labels: string[] = [];\n const counts: number[] = [];\n let pendingKey: string | null = null;\n let expectingKey = false;\n const pathOf = (): KeyPath | null => {\n const depth = kinds.length;\n if (depth === 1) {\n return kinds[0] === \"object\" ? \"\" : null;\n }\n if (depth === 2 && kinds[0] === \"array\" && kinds[1] === \"object\" && labels[1] === \"0\") {\n return \"nodes[0]\";\n }\n if (depth === 2 && kinds[0] === \"object\" && kinds[1] === \"object\") {\n return labels[1] === \"graph\" || labels[1] === \"options\" ? labels[1] : null;\n }\n if (depth === 3 && kinds[0] === \"object\" && kinds[1] === \"array\" && kinds[2] === \"object\") {\n const section = labels[1];\n if ((section === \"nodes\" || section === \"edges\" || section === \"links\") && labels[2] === \"0\") {\n return `${section}[0]`;\n }\n }\n return null;\n };\n let i = 0;\n const n = text.length;\n while (i < n) {\n const ch = text[i];\n if (ch === '\"') {\n const end = scanString(text, i + 1);\n if (end < 0) {\n break;\n }\n const literal = text.slice(i + 1, end);\n i = end + 1;\n if (expectingKey) {\n pendingKey = decodeKey(literal);\n expectingKey = false;\n }\n continue;\n }\n if (ch === \"{\" || ch === \"[\") {\n let label = \"\";\n if (kinds.length > 0) {\n label = kinds[kinds.length - 1] === \"object\" ? (pendingKey ?? \"\") : String(counts[counts.length - 1]);\n }\n kinds.push(ch === \"{\" ? \"object\" : \"array\");\n labels.push(label);\n counts.push(0);\n expectingKey = ch === \"{\";\n pendingKey = null;\n } else if (ch === \"}\" || ch === \"]\") {\n kinds.pop();\n labels.pop();\n counts.pop();\n expectingKey = false;\n pendingKey = null;\n } else if (ch === \":\") {\n if (pendingKey !== null) {\n const path = pathOf();\n if (path !== null) {\n let set = found.get(path);\n if (set === undefined) {\n set = new Set();\n found.set(path, set);\n }\n set.add(pendingKey);\n }\n }\n } else if (ch === \",\") {\n const top = kinds.length - 1;\n if (top >= 0) {\n counts[top]++;\n expectingKey = kinds[top] === \"object\";\n }\n pendingKey = null;\n }\n i++;\n }\n return found;\n}\n\n/**\n * The index of the closing quote of a JSON string starting after its opening quote.\n * @param text - the text\n * @param from - the index after the opening quote\n * @returns the index of the closing quote, or -1 when the text ends first\n */\nfunction scanString(text: string, from: number): number {\n let i = from;\n while (i < text.length) {\n const ch = text[i];\n if (ch === \"\\\\\") {\n i += 2;\n continue;\n }\n if (ch === '\"') {\n return i;\n }\n i++;\n }\n return -1;\n}\n\n/**\n * A key literal decoded when it holds escapes, kept as written otherwise.\n * @param literal - the text between the quotes\n * @returns the key\n */\nfunction decodeKey(literal: string): string {\n if (!literal.includes(\"\\\\\")) {\n return literal;\n }\n try {\n return JSON.parse(`\"${literal}\"`) as string;\n } catch {\n return literal;\n }\n}\n\n/**\n * Coerce a sniff confidence into 0..1 (NaN counts as 0).\n * @param value - the importer's answer\n * @returns the clamped value\n */\nfunction clamp(value: number): number {\n if (!(value > 0)) {\n return 0;\n }\n return value > 1 ? 1 : value;\n}\n","/**\n * The importer / exporter registry (design sections 8.2 and 8.4): the eight built-in formats\n * registered by name, `sniff()` over them, and the two conveniences for callers who do not own a\n * sink: `importGraph()` sniffs the format, creates a builder seeded from the common options\n * (`directed: true` as a placeholder; the importer sets the real value), imports and freezes;\n * `exportGraph()` looks the exporter up by name. A caller who owns a builder uses the importer\n * objects directly (the subpath exports) and this module never touches their sink.\n */\n\nimport {\n type FreezeOptions,\n type FreezeReport,\n GraphBuilder,\n type GraphBuilderOptions,\n GraphFormatError,\n type GraphSnapshot,\n} from \"@graphty/graph-format\";\n\nimport { throwIfAborted } from \"./common/input.js\";\nimport { ImportReportBuilder } from \"./common/report.js\";\nimport { csvExporter, csvImporter } from \"./formats/csv/index.js\";\nimport { dotExporter, dotImporter } from \"./formats/dot/index.js\";\nimport { gexfExporter, gexfImporter } from \"./formats/gexf/index.js\";\nimport { gmlExporter, gmlImporter } from \"./formats/gml/index.js\";\nimport { graphmlExporter, graphmlImporter } from \"./formats/graphml/index.js\";\nimport { jsonExporter, jsonImporter } from \"./formats/json/index.js\";\nimport { neo4jExporter, neo4jImporter } from \"./formats/neo4j/index.js\";\nimport { pajekExporter, pajekImporter } from \"./formats/pajek/index.js\";\nimport { rankFormats, SNIFF_HEAD_BYTES, type SniffHints, type SniffResult } from \"./sniff.js\";\nimport {\n type CommonExportOptions,\n type CommonImportOptions,\n type GraphExporter,\n type GraphImporter,\n type ImportInput,\n type ImportReport,\n type LossNote,\n} from \"./types.js\";\n\n/** The issue code of an input whose format no registered importer recognises. */\nexport const UNKNOWN_FORMAT_CODE = \"E_UNKNOWN_FORMAT\";\n\n/** The builder options importGraph() accepts beyond the ones the common import options seed. */\nexport type BuilderSeed = Omit<\n GraphBuilderOptions,\n \"directed\" | \"addMissingNodes\" | \"duplicateEdges\" | \"selfLoops\" | \"weightDtype\"\n>;\n\n/**\n * The options of importGraph(): the common import options (which also seed the registry's\n * builder, design section 8.4), the format choice and the hints sniffing uses, the builder and\n * freeze options, and any format-specific option (`delimiter`, `dialect`, ...) passed through to\n * the importer unchanged.\n */\nexport interface ImportGraphOptions extends CommonImportOptions {\n /** The format name, or \"auto\" (default) to sniff it from the filename, MIME type and content. */\n readonly format?: string | undefined;\n /** The file name or path the input came from, a hint for sniffing. */\n readonly filename?: string | null | undefined;\n /** The MIME type the input was served as, a hint for sniffing. */\n readonly mimeType?: string | null | undefined;\n /** Builder options the common options do not cover (`weighted`, `expectedNodes`, ...). */\n readonly builder?: BuilderSeed | undefined;\n /** Options of the freeze that follows the import. */\n readonly freeze?: FreezeOptions | undefined;\n /** Format-specific options, passed to the importer as they are. */\n readonly [formatOption: string]: unknown;\n}\n\n/** What importGraph() returns (design section 8.4). */\nexport interface ImportGraphResult {\n /** The format the input was read as. */\n readonly format: string;\n /** The sniff that chose the importer, or null when the caller named the format. */\n readonly sniff: SniffResult | null;\n /** The frozen snapshot. */\n readonly snapshot: GraphSnapshot;\n /** The importer's report. */\n readonly report: ImportReport;\n /** The freeze report (design section 6.6). */\n readonly freeze: FreezeReport;\n}\n\n/** The options of exportGraph(): the common export options plus any format-specific option, passed through. */\nexport interface ExportGraphOptions extends CommonExportOptions {\n /** Format-specific options, passed to the exporter as they are. */\n readonly [formatOption: string]: unknown;\n}\n\n/** The keys of ImportGraphOptions that belong to the registry, never to an importer. */\nconst REGISTRY_KEYS: ReadonlySet<string> = new Set([\"format\", \"filename\", \"mimeType\", \"builder\", \"freeze\"]);\n\n/**\n * A registry of importers and exporters by format name. Registration order is the tie-break\n * order of sniffing (design section 8.2); the default registry lists the built-in formats in the\n * order of GRAPH_FORMATS.\n */\nexport class FormatRegistry {\n private readonly importerMap = new Map<string, GraphImporter>();\n\n private readonly exporterMap = new Map<string, GraphExporter>();\n\n /**\n * Register an importer under its format name, replacing one of the same name in place (the\n * original registration order is kept).\n * @param importer - the importer\n * @returns this registry, for chaining\n */\n registerImporter(importer: GraphImporter): this {\n this.importerMap.set(importer.format, importer);\n return this;\n }\n\n /**\n * Register an exporter under its format name, replacing one of the same name.\n * @param exporter - the exporter\n * @returns this registry, for chaining\n */\n registerExporter(exporter: GraphExporter): this {\n this.exporterMap.set(exporter.format, exporter);\n return this;\n }\n\n /**\n * The importer of a format.\n * @param format - the format name\n * @returns the importer; E_UNSUPPORTED when none is registered\n */\n importer(format: string): GraphImporter {\n const importer = this.importerMap.get(format);\n if (importer === undefined) {\n throw unknownFormat(\"importer\", format, this.importerMap.keys());\n }\n return importer;\n }\n\n /**\n * The exporter of a format.\n * @param format - the format name\n * @returns the exporter; E_UNSUPPORTED when none is registered\n */\n exporter(format: string): GraphExporter {\n const exporter = this.exporterMap.get(format);\n if (exporter === undefined) {\n throw unknownFormat(\"exporter\", format, this.exporterMap.keys());\n }\n return exporter;\n }\n\n /**\n * Whether an importer is registered for a format.\n * @param format - the format name\n * @returns true when importer(format) would succeed\n */\n hasImporter(format: string): boolean {\n return this.importerMap.has(format);\n }\n\n /**\n * Whether an exporter is registered for a format.\n * @param format - the format name\n * @returns true when exporter(format) would succeed\n */\n hasExporter(format: string): boolean {\n return this.exporterMap.has(format);\n }\n\n /**\n * Every registered importer, in registration order.\n * @returns the importers\n */\n importers(): readonly GraphImporter[] {\n return [...this.importerMap.values()];\n }\n\n /**\n * Every registered exporter, in registration order.\n * @returns the exporters\n */\n exporters(): readonly GraphExporter[] {\n return [...this.exporterMap.values()];\n }\n\n /**\n * The names of every format with an importer or an exporter, importers' order first.\n * @returns the format names, each once\n */\n formats(): readonly string[] {\n return [...new Set([...this.importerMap.keys(), ...this.exporterMap.keys()])];\n }\n\n /**\n * Rank the registered importers for an input (design section 8.2; the successor of\n * graphty-element's detectFormat()).\n * @param hints - the filename, MIME type and / or head of the input\n * @returns the candidates, best first; empty when nothing matches\n */\n sniffAll(hints: SniffHints): readonly SniffResult[] {\n return rankFormats(hints, this.importerMap.values());\n }\n\n /**\n * The best importer for an input, or null when no registered importer claims it.\n * @param hints - the filename, MIME type and / or head of the input\n * @returns the best candidate, or null\n */\n sniff(hints: SniffHints): SniffResult | null {\n const ranked = this.sniffAll(hints);\n return ranked.length > 0 ? ranked[0] : null;\n }\n\n /**\n * Read an input into a fresh builder and freeze it (design section 8.4: for callers who do not\n * own a sink). The format is the one named in the options, else sniffed from the filename,\n * the MIME type and the first bytes of the content; the builder is seeded from the common\n * options with `directed: true` as a placeholder that the importer overrides from the file.\n * An input holding several graphs yields the first, with a warning naming how many were\n * skipped; importAllGraphs() returns every one.\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns the snapshot, the import report and the freeze report\n */\n async importGraph(input: ImportInput, options: ImportGraphOptions = {}): Promise<ImportGraphResult> {\n const chosen = await this.choose(input, options);\n const builder = seededBuilder(options);\n let report: ImportReport;\n try {\n report = await chosen.importer.import(chosen.source, builder, importerOptions(options));\n } catch (err) {\n await chosen.peeked?.close();\n throw err;\n }\n return result(chosen, builder, report, options);\n }\n\n /**\n * Read every graph of an input (a DOT file with several graphs, a Pajek project with several\n * networks, a JGF document with a `graphs` array), each into its own fresh builder, frozen.\n * A format whose importer has no importAll() holds one graph per input, so the result has one\n * entry. Options, sniffing and the builder seed are those of importGraph().\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns one result per graph, in document order\n */\n async importAllGraphs(input: ImportInput, options: ImportGraphOptions = {}): Promise<ImportGraphResult[]> {\n const chosen = await this.choose(input, options);\n const { importer } = chosen;\n const builders: GraphBuilder[] = [];\n let reports: ImportReport[];\n try {\n if (importer.importAll === undefined) {\n builders.push(seededBuilder(options));\n reports = [await importer.import(chosen.source, builders[0], importerOptions(options))];\n } else {\n reports = await importer.importAll(\n chosen.source,\n () => {\n const builder = seededBuilder(options);\n builders.push(builder);\n return builder;\n },\n importerOptions(options),\n );\n }\n } catch (err) {\n await chosen.peeked?.close();\n throw err;\n }\n return reports.map((report, i) => result(chosen, builders[i], report, options));\n }\n\n /**\n * The importer for an input: the named format, or the sniffed one.\n * @param input - the input\n * @param options - the importGraph options\n * @returns the importer, the sniff, and the input to read (replayed for a stream)\n */\n private async choose(input: ImportInput, options: ImportGraphOptions): Promise<ChosenImporter> {\n const requested = options.format ?? \"auto\";\n if (requested !== \"auto\") {\n return { importer: this.importer(requested), sniff: null, source: input, peeked: null };\n }\n const peeked = await peekHead(input, SNIFF_HEAD_BYTES, options.signal ?? null);\n const sniff = this.sniff({ filename: options.filename, mimeType: options.mimeType, head: peeked.head });\n if (sniff === null) {\n const report = new ImportReportBuilder(\"unknown\", 0);\n return report.fail(\n UNKNOWN_FORMAT_CODE,\n `no registered importer recognises the input${describeHints(options)}; pass the format explicitly`,\n undefined,\n { formats: this.formats() },\n );\n }\n return { importer: this.importer(sniff.format), sniff, source: peeked.input, peeked };\n }\n\n /**\n * Write a snapshot in a format, as UTF-8 chunks (design section 8.5).\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the encoded chunks\n */\n exportGraph(snapshot: GraphSnapshot, format: string, options?: ExportGraphOptions): AsyncIterable<Uint8Array> {\n return this.exporter(format).export(snapshot, options);\n }\n\n /**\n * Write a snapshot in a format, as one string.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the whole document\n */\n async exportGraphToString(snapshot: GraphSnapshot, format: string, options?: ExportGraphOptions): Promise<string> {\n return this.exporter(format).exportToString(snapshot, options);\n }\n\n /**\n * What exporting a snapshot in a format would lose, without writing anything.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the loss notes, empty when the export is exact\n */\n checkExport(snapshot: GraphSnapshot, format: string, options?: ExportGraphOptions): readonly LossNote[] {\n return this.exporter(format).check(snapshot, options);\n }\n}\n\n/**\n * A registry holding the eight built-in importers and exporters in the order of GRAPH_FORMATS.\n * @returns a new registry\n */\nexport function createRegistry(): FormatRegistry {\n return new FormatRegistry()\n .registerImporter(jsonImporter)\n .registerExporter(jsonExporter)\n .registerImporter(graphmlImporter)\n .registerExporter(graphmlExporter)\n .registerImporter(gexfImporter)\n .registerExporter(gexfExporter)\n .registerImporter(csvImporter)\n .registerExporter(csvExporter)\n .registerImporter(gmlImporter)\n .registerExporter(gmlExporter)\n .registerImporter(dotImporter)\n .registerExporter(dotExporter)\n .registerImporter(pajekImporter)\n .registerExporter(pajekExporter)\n .registerImporter(neo4jImporter)\n .registerExporter(neo4jExporter);\n}\n\n/** The default registry: every built-in format. */\nexport const registry: FormatRegistry = createRegistry();\n\n/**\n * Read an input into a fresh builder and freeze it, through the default registry (design section\n * 8.4: `importGraph(input, { format?, ...options })`).\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns the snapshot, the import report and the freeze report\n */\nexport function importGraph(input: ImportInput, options?: ImportGraphOptions): Promise<ImportGraphResult> {\n return registry.importGraph(input, options);\n}\n\n/**\n * Read every graph of an input, each into its own frozen snapshot, through the default registry.\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns one result per graph, in document order\n */\nexport function importAllGraphs(input: ImportInput, options?: ImportGraphOptions): Promise<ImportGraphResult[]> {\n return registry.importAllGraphs(input, options);\n}\n\n/**\n * Write a snapshot in a format through the default registry, as UTF-8 chunks.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the encoded chunks\n */\nexport function exportGraph(\n snapshot: GraphSnapshot,\n format: string,\n options?: ExportGraphOptions,\n): AsyncIterable<Uint8Array> {\n return registry.exportGraph(snapshot, format, options);\n}\n\n/**\n * Write a snapshot in a format through the default registry, as one string.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the whole document\n */\nexport async function exportGraphToString(\n snapshot: GraphSnapshot,\n format: string,\n options?: ExportGraphOptions,\n): Promise<string> {\n return registry.exportGraphToString(snapshot, format, options);\n}\n\n/**\n * What exporting a snapshot in a format would lose, through the default registry.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the loss notes, empty when the export is exact\n */\nexport function checkExport(\n snapshot: GraphSnapshot,\n format: string,\n options?: ExportGraphOptions,\n): readonly LossNote[] {\n return registry.checkExport(snapshot, format, options);\n}\n\n/**\n * Sniff an input's format through the default registry.\n * @param hints - the filename, MIME type and / or head of the input\n * @returns the best candidate, or null when no built-in importer claims it\n */\nexport function sniff(hints: SniffHints): SniffResult | null {\n return registry.sniff(hints);\n}\n\n/**\n * The options handed to the importer: everything but the registry's own keys.\n * @param options - the importGraph options\n * @returns the importer's options\n */\nfunction importerOptions(options: ImportGraphOptions): CommonImportOptions {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(options)) {\n if (!REGISTRY_KEYS.has(key)) {\n out[key] = value;\n }\n }\n return out;\n}\n\n/** The importer chosen for an input, and the input to hand it. */\ninterface ChosenImporter {\n /** The importer. */\n readonly importer: GraphImporter;\n /** The sniff that chose it, or null when the caller named the format. */\n readonly sniff: SniffResult | null;\n /** The input to read: the original, or a replaying iterable for a peeked stream. */\n readonly source: ImportInput;\n /** The peeked head of a sniffed input, or null. */\n readonly peeked: PeekedInput | null;\n}\n\n/**\n * A fresh builder seeded from the common options, `directed: true` as a placeholder the importer\n * overrides from the file.\n * @param options - the importGraph options\n * @returns the builder\n */\nfunction seededBuilder(options: ImportGraphOptions): GraphBuilder {\n return new GraphBuilder({\n weightDtype: options.weightDtype ?? \"f64\",\n ...options.builder,\n directed: true,\n addMissingNodes: options.addMissingNodes ?? true,\n duplicateEdges: options.duplicateEdges ?? \"keep\",\n selfLoops: options.selfLoops ?? \"keep\",\n });\n}\n\n/**\n * Freeze an imported builder into an importGraph result.\n * @param chosen - the importer and its sniff\n * @param builder - the filled builder\n * @param report - the importer's report\n * @param options - the importGraph options\n * @returns the frozen result\n */\nfunction result(\n chosen: ChosenImporter,\n builder: GraphBuilder,\n report: ImportReport,\n options: ImportGraphOptions,\n): ImportGraphResult {\n const frozen = builder.freezeWithReport(options.freeze);\n return Object.freeze({\n format: chosen.importer.format,\n sniff: chosen.sniff,\n snapshot: frozen.snapshot,\n report,\n freeze: frozen.report,\n });\n}\n\n/**\n * The E_UNSUPPORTED error of a format name nothing is registered under.\n * @param kind - importer or exporter\n * @param format - the requested name\n * @param known - the registered names\n * @returns the error\n */\nfunction unknownFormat(kind: \"importer\" | \"exporter\", format: string, known: Iterable<string>): GraphFormatError {\n const supported = [...known];\n return new GraphFormatError(\n \"E_UNSUPPORTED\",\n `no ${kind} is registered for format ${JSON.stringify(format)}; known: ${supported.join(\", \")}`,\n { option: \"format\", found: format, supported },\n );\n}\n\n/**\n * The hints of an import, for the unknown-format message.\n * @param options - the importGraph options\n * @returns \" (filename ..., MIME type ...)\" or an empty string\n */\nfunction describeHints(options: ImportGraphOptions): string {\n const parts: string[] = [];\n if (typeof options.filename === \"string\" && options.filename.length > 0) {\n parts.push(`filename ${JSON.stringify(options.filename)}`);\n }\n if (typeof options.mimeType === \"string\" && options.mimeType.length > 0) {\n parts.push(`MIME type ${JSON.stringify(options.mimeType)}`);\n }\n return parts.length === 0 ? \"\" : ` (${parts.join(\", \")})`;\n}\n\n/** A head read from an input, and the input to hand the importer (the same bytes, replayed for a stream). */\ninterface PeekedInput {\n /** The first bytes (or, for a text input, characters) of the content. */\n readonly head: Uint8Array | string;\n /** The input to read: the original for in-memory input, a replaying iterable for a stream. */\n readonly input: ImportInput;\n /**\n * Close the source when the importer never iterated the replaying input (it threw first, an\n * abort for instance): a stream's reader is cancelled and released, an async generator finalised.\n * @returns when the source is closed\n */\n close(): Promise<void>;\n}\n\n/**\n * The first `bytes` of an input without consuming it: in-memory input is sliced; a stream or an\n * async iterable is read until enough is buffered and then replayed (the buffered chunks first,\n * the rest as it arrives) through a new async iterable that cancels the source when the importer\n * stops early.\n * @param input - the input\n * @param bytes - how much to peek\n * @param signal - the cancellation signal, or null\n * @returns the head and the input to read\n */\nasync function peekHead(input: ImportInput, bytes: number, signal: AbortSignal | null): Promise<PeekedInput> {\n if (typeof input === \"string\") {\n return { head: input.slice(0, bytes), input, close: (): Promise<void> => Promise.resolve() };\n }\n if (input instanceof Uint8Array) {\n return { head: input.subarray(0, bytes), input, close: (): Promise<void> => Promise.resolve() };\n }\n throwIfAborted(signal);\n const source: AsyncIterator<string | Uint8Array> =\n typeof (input as { getReader?: unknown }).getReader === \"function\"\n ? readerIterator(input as ReadableStream<Uint8Array>)\n : (input as AsyncIterable<string | Uint8Array>)[Symbol.asyncIterator]();\n const buffered: (string | Uint8Array)[] = [];\n let size = 0;\n let finished = false;\n while (size < bytes) {\n if (signal !== null && signal.aborted) {\n await source.return?.();\n throwIfAborted(signal);\n }\n const next = await source.next();\n if (next.done === true) {\n finished = true;\n break;\n }\n buffered.push(next.value);\n size += typeof next.value === \"string\" ? next.value.length : next.value.byteLength;\n }\n if (!finished && signal !== null && signal.aborted) {\n // an abort that landed on the read completing the head: close the source before rethrowing\n await source.return?.();\n throwIfAborted(signal);\n }\n const replayed = replay(buffered, source, finished);\n return { head: joinHead(buffered, bytes), input: replayed, close: (): Promise<void> => replayed.close() };\n}\n\n/**\n * The head text or bytes of the buffered chunks: bytes when every chunk is bytes, text otherwise\n * (byte chunks decoded leniently; the importer decodes the real input strictly).\n * @param chunks - the buffered chunks\n * @param bytes - the head size\n * @returns the head\n */\nfunction joinHead(chunks: readonly (string | Uint8Array)[], bytes: number): Uint8Array | string {\n if (chunks.every((c) => c instanceof Uint8Array)) {\n const total = Math.min(\n bytes,\n chunks.reduce((sum, c) => sum + c.byteLength, 0),\n );\n const head = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n if (offset >= total) {\n break;\n }\n const part = chunk.subarray(0, Math.min(chunk.byteLength, total - offset));\n head.set(part, offset);\n offset += part.byteLength;\n }\n return head;\n }\n const decoder = new TextDecoder(\"utf-8\", { fatal: false });\n let text = \"\";\n for (const chunk of chunks) {\n text += typeof chunk === \"string\" ? chunk : decoder.decode(chunk, { stream: true });\n if (text.length >= bytes) {\n break;\n }\n }\n return text.slice(0, bytes);\n}\n\n/**\n * An async iterable that yields the buffered chunks, then the rest of the source; the source is\n * closed when the consumer stops early.\n * @param buffered - the chunks already read\n * @param source - the iterator positioned after them\n * @param finished - whether the source is already exhausted\n * @returns the replaying iterable\n */\nfunction replay(\n buffered: readonly (string | Uint8Array)[],\n source: AsyncIterator<string | Uint8Array>,\n finished: boolean,\n): AsyncIterable<string | Uint8Array> & { close(): Promise<void> } {\n let exhausted = finished;\n const close = async (): Promise<void> => {\n if (!exhausted) {\n exhausted = true;\n await source.return?.();\n }\n };\n return {\n close,\n [Symbol.asyncIterator](): AsyncIterator<string | Uint8Array> {\n let position = 0;\n return {\n async next(): Promise<IteratorResult<string | Uint8Array>> {\n if (position < buffered.length) {\n return { done: false, value: buffered[position++] };\n }\n if (exhausted) {\n return { done: true, value: undefined };\n }\n const next = await source.next();\n if (next.done === true) {\n exhausted = true;\n return { done: true, value: undefined };\n }\n return next;\n },\n async return(): Promise<IteratorResult<string | Uint8Array>> {\n await close();\n return { done: true, value: undefined };\n },\n };\n },\n };\n}\n\n/**\n * An async iterator over a ReadableStream's chunks that cancels the stream when closed early.\n * @param stream - the stream\n * @returns the iterator\n */\nfunction readerIterator(stream: ReadableStream<Uint8Array>): AsyncIterator<Uint8Array> {\n const reader = stream.getReader();\n let done = false;\n return {\n async next(): Promise<IteratorResult<Uint8Array>> {\n if (done) {\n return { done: true, value: undefined };\n }\n const result = await reader.read();\n if (result.done) {\n done = true;\n reader.releaseLock();\n return { done: true, value: undefined };\n }\n return { done: false, value: result.value };\n },\n async return(): Promise<IteratorResult<Uint8Array>> {\n if (!done) {\n done = true;\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n return { done: true, value: undefined };\n },\n };\n}\n"],"names":["e","m","result","a","b","c","i","n","sniff"],"mappings":"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"}
1
+ {"version":3,"file":"graph-io.js","sources":["../src/sniff.ts","../src/registry.ts"],"sourcesContent":["/**\n * Format detection (design sections 8.2 and 13.1: the registry's `sniff()`, the successor of\n * graphty-element's `format-detection.ts`): which of the registered importers a file is for, from\n * any combination of a filename (its extension), a MIME type and the first bytes of the content.\n *\n * Every importer declares `extensions`, `mimeTypes` and a `sniff(head)` content confidence in\n * 0..1 (design section 8.4); this module combines them into one ranked answer so that content\n * always beats a hint: a `.csv` whose first line is a neo4j-admin header is Neo4j, a `.xml` is\n * GEXF or GraphML by its root element, a `.txt` holding `graph [` is GML. The confidence of a\n * candidate is\n *\n * - `0.5 + 0.35 * content + 0.1 * [extension matches] + 0.05 * [MIME type matches]` when the\n * importer recognises the content (`content > 0`), so a content match always scores at least\n * 0.5 and at most 1;\n * - `0.3 * [extension matches] + 0.1 * [MIME type matches]` when the content is absent or the\n * importer rejects it, so a hint alone never reaches 0.5;\n * - nothing (the format is not a candidate) otherwise.\n *\n * Ties are broken by the order the importers were registered in, which is why the default\n * registry lists the common formats first. The JSON dialect is sniffed from the head as a hint\n * (the importer's detection on the parsed document is authoritative, design section 8.2).\n */\n\nimport { type JsonImportDialect, sniffJsonDialect } from \"./formats/json/dialect.js\";\nimport { type GraphImporter } from \"./types.js\";\n\n/** The format names of the eight built-in importers and exporters. */\nexport type GraphFormatName = \"gexf\" | \"graphml\" | \"gml\" | \"dot\" | \"pajek\" | \"csv\" | \"json\" | \"neo4j\";\n\n/**\n * The built-in format names in the default registry's order, which is also the tie-break order of\n * sniffing: the more common format wins an extension two formats claim (`.xml` GraphML before GEXF,\n * `.csv` CSV before Neo4j) when the content does not decide.\n */\nexport const GRAPH_FORMATS: readonly GraphFormatName[] = Object.freeze([\n \"json\",\n \"graphml\",\n \"gexf\",\n \"csv\",\n \"gml\",\n \"dot\",\n \"pajek\",\n \"neo4j\",\n]);\n\n/** How many bytes of the input the sniffers look at; the registry reads no more than this before deciding. */\nexport const SNIFF_HEAD_BYTES = 8192;\n\n/** What is known about an input before it is read. */\nexport interface SniffHints {\n /** A file name or path; only its extension is used. */\n readonly filename?: string | null | undefined;\n /** A MIME type, with or without parameters (`text/csv; charset=utf-8`). */\n readonly mimeType?: string | null | undefined;\n /** The first bytes (or characters) of the content. */\n readonly head?: Uint8Array | string | null | undefined;\n}\n\n/** One ranked candidate of a sniff. */\nexport interface SniffResult {\n /** The importer's format name. */\n readonly format: string;\n /** The combined confidence in 0..1 (at least 0.5 when the content was recognised). */\n readonly confidence: number;\n /** The importer's own content confidence, 0 when no head was given or it rejected the head. */\n readonly content: number;\n /** Whether the filename's extension is one the importer claims. */\n readonly extension: boolean;\n /** Whether the MIME type is one the importer claims. */\n readonly mimeType: boolean;\n /** For the JSON format: the dialect the head suggests, or null when unknown; always null for other formats. */\n readonly dialect: JsonImportDialect | null;\n}\n\n/**\n * The lower-cased extension of a file name or path, with its dot.\n * @param filename - the name or path\n * @returns the extension (`.gexf`), or null when the name has none\n */\nexport function extensionOf(filename: string): string | null {\n const base = filename.slice(Math.max(filename.lastIndexOf(\"/\"), filename.lastIndexOf(\"\\\\\")) + 1);\n const dot = base.lastIndexOf(\".\");\n if (dot <= 0 || dot === base.length - 1) {\n return null;\n }\n return base.slice(dot).toLowerCase();\n}\n\n/**\n * A MIME type without parameters, lower-cased, for comparison with an importer's list.\n * @param mimeType - the type as received (`Text/CSV; charset=utf-8`)\n * @returns the bare type (`text/csv`)\n */\nexport function normalizeMimeType(mimeType: string): string {\n const semicolon = mimeType.indexOf(\";\");\n return (semicolon < 0 ? mimeType : mimeType.slice(0, semicolon)).trim().toLowerCase();\n}\n\n/**\n * The head as bytes for the importers' sniff functions: at most SNIFF_HEAD_BYTES, a string\n * encoded as UTF-8, a UTF-16 head with a byte order mark transcoded to UTF-8.\n * @param head - the head as given\n * @returns the bytes\n */\nexport function headBytes(head: Uint8Array | string): Uint8Array {\n if (typeof head === \"string\") {\n return new TextEncoder().encode(head.slice(0, SNIFF_HEAD_BYTES)).subarray(0, SNIFF_HEAD_BYTES);\n }\n if (head.byteLength >= 2 && ((head[0] === 0xff && head[1] === 0xfe) || (head[0] === 0xfe && head[1] === 0xff))) {\n // UTF-16 with a BOM (Excel's \"Unicode text\"): the sniffers read UTF-8, so transcode the head\n const encoding = head[0] === 0xff ? \"utf-16le\" : \"utf-16be\";\n return headBytes(new TextDecoder(encoding).decode(head.subarray(0, SNIFF_HEAD_BYTES)));\n }\n return head.byteLength > SNIFF_HEAD_BYTES ? head.subarray(0, SNIFF_HEAD_BYTES) : head;\n}\n\n/**\n * Rank the registered importers for an input by the rule of the module comment: every importer\n * whose content confidence is positive or whose extension / MIME type matches is a candidate,\n * ordered by confidence (ties in registration order).\n * @param hints - what is known about the input\n * @param importers - the registered importers, in registration order\n * @returns the candidates, best first; empty when nothing matches\n */\nexport function rankFormats(hints: SniffHints, importers: Iterable<GraphImporter>): readonly SniffResult[] {\n const extension = typeof hints.filename === \"string\" ? extensionOf(hints.filename) : null;\n const mime = typeof hints.mimeType === \"string\" ? normalizeMimeType(hints.mimeType) : null;\n const head = hints.head === undefined || hints.head === null ? null : headBytes(hints.head);\n const candidates: { readonly result: SniffResult; readonly rank: number }[] = [];\n let rank = 0;\n for (const importer of importers) {\n const extensionMatch = extension !== null && importer.extensions.some((e) => e.toLowerCase() === extension);\n const mimeMatch = mime !== null && importer.mimeTypes.some((m) => m.toLowerCase() === mime);\n let content = 0;\n if (head !== null && head.byteLength > 0 && typeof importer.sniff === \"function\") {\n content = clamp(importer.sniff(head));\n }\n let confidence: number;\n if (content > 0) {\n confidence = 0.5 + 0.35 * content + (extensionMatch ? 0.1 : 0) + (mimeMatch ? 0.05 : 0);\n } else if (extensionMatch || mimeMatch) {\n confidence = (extensionMatch ? 0.3 : 0) + (mimeMatch ? 0.1 : 0);\n } else {\n continue;\n }\n const dialect = importer.format === \"json\" && head !== null ? sniffJsonDialectHead(head) : null;\n const result: SniffResult = Object.freeze({\n format: importer.format,\n confidence: Math.min(1, confidence),\n content,\n extension: extensionMatch,\n mimeType: mimeMatch,\n dialect,\n });\n candidates.push({ result, rank: rank++ });\n }\n candidates.sort((a, b) => b.result.confidence - a.result.confidence || a.rank - b.rank);\n return candidates.map((c) => c.result);\n}\n\n/**\n * The best candidate of rankFormats(), or null when no importer matches.\n * @param hints - what is known about the input\n * @param importers - the registered importers, in registration order\n * @returns the best candidate, or null\n */\nexport function sniffFormat(hints: SniffHints, importers: Iterable<GraphImporter>): SniffResult | null {\n const ranked = rankFormats(hints, importers);\n return ranked.length > 0 ? ranked[0] : null;\n}\n\n/**\n * The JSON dialect a head suggests. A head that parses as a whole document is classified exactly\n * (sniffJsonDialect); a truncated head is scanned for its top-level keys, the keys of `graph` and\n * `options`, and the keys of the first object under `nodes` / `edges` / `links`, and the same\n * rule is applied to that skeleton. The result is a hint: the importer decides on the parsed\n * document.\n * @param head - the first bytes or characters of the document\n * @returns the dialect, or null when the head is not a JSON graph document\n */\nexport function sniffJsonDialectHead(head: Uint8Array | string): JsonImportDialect | null {\n const text = typeof head === \"string\" ? head : new TextDecoder(\"utf-8\", { fatal: false }).decode(head);\n const body = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n const trimmed = body.trimStart();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) {\n return null;\n }\n let root: unknown;\n try {\n root = JSON.parse(trimmed);\n } catch {\n root = skeletonOf(trimmed);\n }\n return sniffJsonDialect(root);\n}\n\n/** Where a key was seen while scanning a truncated head. */\ntype KeyPath = \"\" | \"graph\" | \"options\" | \"nodes[0]\" | \"edges[0]\" | \"links[0]\";\n\n/**\n * A partial document rebuilt from the keys a truncated head reveals: every key gets a placeholder\n * value of the shape the dialect rule tests for (an object for `graph`, `options`, `attributes`\n * and `data`, an array holding the first element's skeleton for `nodes` / `edges` / `links` /\n * `graphs`, `true` otherwise), so sniffJsonDialect() can classify it.\n * @param text - the head, starting with `{` or `[`\n * @returns the skeleton, or null when the scan finds no key at all\n */\nfunction skeletonOf(text: string): unknown {\n const keys = scanKeys(text);\n if (text.startsWith(\"[\")) {\n // a cut array whose first element has not been seen tells nothing (a whole `[]` is Cytoscape)\n const first = keys.get(\"nodes[0]\");\n return first === undefined ? null : [objectOf(first)];\n }\n const rootKeys = keys.get(\"\");\n if (rootKeys === undefined) {\n return null;\n }\n const root: Record<string, unknown> = {};\n for (const key of rootKeys) {\n switch (key) {\n case \"graph\":\n case \"options\":\n root[key] = objectOf(keys.get(key));\n break;\n case \"graphs\":\n root[key] = [];\n break;\n case \"nodes\":\n case \"edges\":\n case \"links\": {\n const first = keys.get(`${key}[0]`);\n root[key] = first === undefined ? [] : [objectOf(first)];\n break;\n }\n default:\n root[key] = true;\n break;\n }\n }\n return root;\n}\n\n/**\n * An object holding placeholder values for a key set: `{}` for the keys the rule tests with\n * isJsonObject (`attributes`, `data`), `true` otherwise.\n * @param names - the keys, possibly undefined\n * @returns the object\n */\nfunction objectOf(names: ReadonlySet<string> | undefined): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (names !== undefined) {\n for (const name of names) {\n out[name] = name === \"attributes\" || name === \"data\" ? {} : true;\n }\n }\n return out;\n}\n\n/**\n * Scan a (possibly truncated) JSON text for the keys at the paths the dialect rule reads. The\n * scanner tracks a container stack (the key each object sits under, the index of each array\n * element) and records a key when it is at one of the watched paths; a head cut inside a string\n * or a number simply ends the scan. For a top-level array the first element's keys are recorded\n * under `nodes[0]`.\n * @param text - the head, starting with `{` or `[`\n * @returns key sets by path\n */\nfunction scanKeys(text: string): Map<KeyPath, Set<string>> {\n const found = new Map<KeyPath, Set<string>>();\n // one frame per open container: its kind, the key or index it sits under, and the element index\n const kinds: (\"object\" | \"array\")[] = [];\n const labels: string[] = [];\n const counts: number[] = [];\n let pendingKey: string | null = null;\n let expectingKey = false;\n const pathOf = (): KeyPath | null => {\n const depth = kinds.length;\n if (depth === 1) {\n return kinds[0] === \"object\" ? \"\" : null;\n }\n if (depth === 2 && kinds[0] === \"array\" && kinds[1] === \"object\" && labels[1] === \"0\") {\n return \"nodes[0]\";\n }\n if (depth === 2 && kinds[0] === \"object\" && kinds[1] === \"object\") {\n return labels[1] === \"graph\" || labels[1] === \"options\" ? labels[1] : null;\n }\n if (depth === 3 && kinds[0] === \"object\" && kinds[1] === \"array\" && kinds[2] === \"object\") {\n const section = labels[1];\n if ((section === \"nodes\" || section === \"edges\" || section === \"links\") && labels[2] === \"0\") {\n return `${section}[0]`;\n }\n }\n return null;\n };\n let i = 0;\n const n = text.length;\n while (i < n) {\n const ch = text[i];\n if (ch === '\"') {\n const end = scanString(text, i + 1);\n if (end < 0) {\n break;\n }\n const literal = text.slice(i + 1, end);\n i = end + 1;\n if (expectingKey) {\n pendingKey = decodeKey(literal);\n expectingKey = false;\n }\n continue;\n }\n if (ch === \"{\" || ch === \"[\") {\n let label = \"\";\n if (kinds.length > 0) {\n label = kinds[kinds.length - 1] === \"object\" ? (pendingKey ?? \"\") : String(counts[counts.length - 1]);\n }\n kinds.push(ch === \"{\" ? \"object\" : \"array\");\n labels.push(label);\n counts.push(0);\n expectingKey = ch === \"{\";\n pendingKey = null;\n } else if (ch === \"}\" || ch === \"]\") {\n kinds.pop();\n labels.pop();\n counts.pop();\n expectingKey = false;\n pendingKey = null;\n } else if (ch === \":\") {\n if (pendingKey !== null) {\n const path = pathOf();\n if (path !== null) {\n let set = found.get(path);\n if (set === undefined) {\n set = new Set();\n found.set(path, set);\n }\n set.add(pendingKey);\n }\n }\n } else if (ch === \",\") {\n const top = kinds.length - 1;\n if (top >= 0) {\n counts[top]++;\n expectingKey = kinds[top] === \"object\";\n }\n pendingKey = null;\n }\n i++;\n }\n return found;\n}\n\n/**\n * The index of the closing quote of a JSON string starting after its opening quote.\n * @param text - the text\n * @param from - the index after the opening quote\n * @returns the index of the closing quote, or -1 when the text ends first\n */\nfunction scanString(text: string, from: number): number {\n let i = from;\n while (i < text.length) {\n const ch = text[i];\n if (ch === \"\\\\\") {\n i += 2;\n continue;\n }\n if (ch === '\"') {\n return i;\n }\n i++;\n }\n return -1;\n}\n\n/**\n * A key literal decoded when it holds escapes, kept as written otherwise.\n * @param literal - the text between the quotes\n * @returns the key\n */\nfunction decodeKey(literal: string): string {\n if (!literal.includes(\"\\\\\")) {\n return literal;\n }\n try {\n return JSON.parse(`\"${literal}\"`) as string;\n } catch {\n return literal;\n }\n}\n\n/**\n * Coerce a sniff confidence into 0..1 (NaN counts as 0).\n * @param value - the importer's answer\n * @returns the clamped value\n */\nfunction clamp(value: number): number {\n if (!(value > 0)) {\n return 0;\n }\n return value > 1 ? 1 : value;\n}\n","/**\n * The importer / exporter registry (design sections 8.2 and 8.4): the eight built-in formats\n * registered by name, `sniff()` over them, and the two conveniences for callers who do not own a\n * sink: `importGraph()` sniffs the format, creates a builder seeded from the common options\n * (`directed: true` as a placeholder; the importer sets the real value), imports and freezes;\n * `exportGraph()` looks the exporter up by name. A caller who owns a builder uses the importer\n * objects directly (the subpath exports) and this module never touches their sink.\n */\n\nimport {\n type FreezeOptions,\n type FreezeReport,\n GraphBuilder,\n type GraphBuilderOptions,\n GraphFormatError,\n type GraphSnapshot,\n} from \"@graphty/graph-format\";\n\nimport { throwIfAborted } from \"./common/input.js\";\nimport { ImportReportBuilder } from \"./common/report.js\";\nimport { csvExporter, csvImporter } from \"./formats/csv/index.js\";\nimport { dotExporter, dotImporter } from \"./formats/dot/index.js\";\nimport { gexfExporter, gexfImporter } from \"./formats/gexf/index.js\";\nimport { gmlExporter, gmlImporter } from \"./formats/gml/index.js\";\nimport { graphmlExporter, graphmlImporter } from \"./formats/graphml/index.js\";\nimport { jsonExporter, jsonImporter } from \"./formats/json/index.js\";\nimport { neo4jExporter, neo4jImporter } from \"./formats/neo4j/index.js\";\nimport { pajekExporter, pajekImporter } from \"./formats/pajek/index.js\";\nimport { rankFormats, SNIFF_HEAD_BYTES, type SniffHints, type SniffResult } from \"./sniff.js\";\nimport {\n type CommonExportOptions,\n type CommonImportOptions,\n type GraphExporter,\n type GraphImporter,\n type ImportInput,\n type ImportReport,\n type LossNote,\n} from \"./types.js\";\n\n/** The issue code of an input whose format no registered importer recognises. */\nexport const UNKNOWN_FORMAT_CODE = \"E_UNKNOWN_FORMAT\";\n\n/** The builder options importGraph() accepts beyond the ones the common import options seed. */\nexport type BuilderSeed = Omit<\n GraphBuilderOptions,\n \"directed\" | \"addMissingNodes\" | \"duplicateEdges\" | \"selfLoops\" | \"weightDtype\"\n>;\n\n/**\n * The options of importGraph(): the common import options (which also seed the registry's\n * builder, design section 8.4), the format choice and the hints sniffing uses, the builder and\n * freeze options, and any format-specific option (`delimiter`, `dialect`, ...) passed through to\n * the importer unchanged.\n */\nexport interface ImportGraphOptions extends CommonImportOptions {\n /** The format name, or \"auto\" (default) to sniff it from the filename, MIME type and content. */\n readonly format?: string | undefined;\n /** The file name or path the input came from, a hint for sniffing. */\n readonly filename?: string | null | undefined;\n /** The MIME type the input was served as, a hint for sniffing. */\n readonly mimeType?: string | null | undefined;\n /** Builder options the common options do not cover (`weighted`, `expectedNodes`, ...). */\n readonly builder?: BuilderSeed | undefined;\n /** Options of the freeze that follows the import. */\n readonly freeze?: FreezeOptions | undefined;\n /** Format-specific options, passed to the importer as they are. */\n readonly [formatOption: string]: unknown;\n}\n\n/** What importGraph() returns (design section 8.4). */\nexport interface ImportGraphResult {\n /** The format the input was read as. */\n readonly format: string;\n /** The sniff that chose the importer, or null when the caller named the format. */\n readonly sniff: SniffResult | null;\n /** The frozen snapshot. */\n readonly snapshot: GraphSnapshot;\n /** The importer's report. */\n readonly report: ImportReport;\n /** The freeze report (design section 6.6). */\n readonly freeze: FreezeReport;\n}\n\n/** The options of exportGraph(): the common export options plus any format-specific option, passed through. */\nexport interface ExportGraphOptions extends CommonExportOptions {\n /** Format-specific options, passed to the exporter as they are. */\n readonly [formatOption: string]: unknown;\n}\n\n/** The keys of ImportGraphOptions that belong to the registry, never to an importer. */\nconst REGISTRY_KEYS: ReadonlySet<string> = new Set([\"format\", \"filename\", \"mimeType\", \"builder\", \"freeze\"]);\n\n/**\n * A registry of importers and exporters by format name. Registration order is the tie-break\n * order of sniffing (design section 8.2); the default registry lists the built-in formats in the\n * order of GRAPH_FORMATS.\n */\nexport class FormatRegistry {\n private readonly importerMap = new Map<string, GraphImporter>();\n\n private readonly exporterMap = new Map<string, GraphExporter>();\n\n /**\n * Register an importer under its format name, replacing one of the same name in place (the\n * original registration order is kept).\n * @param importer - the importer\n * @returns this registry, for chaining\n */\n registerImporter(importer: GraphImporter): this {\n this.importerMap.set(importer.format, importer);\n return this;\n }\n\n /**\n * Register an exporter under its format name, replacing one of the same name.\n * @param exporter - the exporter\n * @returns this registry, for chaining\n */\n registerExporter(exporter: GraphExporter): this {\n this.exporterMap.set(exporter.format, exporter);\n return this;\n }\n\n /**\n * The importer of a format.\n * @param format - the format name\n * @returns the importer; E_UNSUPPORTED when none is registered\n */\n importer(format: string): GraphImporter {\n const importer = this.importerMap.get(format);\n if (importer === undefined) {\n throw unknownFormat(\"importer\", format, this.importerMap.keys());\n }\n return importer;\n }\n\n /**\n * The exporter of a format.\n * @param format - the format name\n * @returns the exporter; E_UNSUPPORTED when none is registered\n */\n exporter(format: string): GraphExporter {\n const exporter = this.exporterMap.get(format);\n if (exporter === undefined) {\n throw unknownFormat(\"exporter\", format, this.exporterMap.keys());\n }\n return exporter;\n }\n\n /**\n * Whether an importer is registered for a format.\n * @param format - the format name\n * @returns true when importer(format) would succeed\n */\n hasImporter(format: string): boolean {\n return this.importerMap.has(format);\n }\n\n /**\n * Whether an exporter is registered for a format.\n * @param format - the format name\n * @returns true when exporter(format) would succeed\n */\n hasExporter(format: string): boolean {\n return this.exporterMap.has(format);\n }\n\n /**\n * Every registered importer, in registration order.\n * @returns the importers\n */\n importers(): readonly GraphImporter[] {\n return [...this.importerMap.values()];\n }\n\n /**\n * Every registered exporter, in registration order.\n * @returns the exporters\n */\n exporters(): readonly GraphExporter[] {\n return [...this.exporterMap.values()];\n }\n\n /**\n * The names of every format with an importer or an exporter, importers' order first.\n * @returns the format names, each once\n */\n formats(): readonly string[] {\n return [...new Set([...this.importerMap.keys(), ...this.exporterMap.keys()])];\n }\n\n /**\n * Rank the registered importers for an input (design section 8.2; the successor of\n * graphty-element's detectFormat()).\n * @param hints - the filename, MIME type and / or head of the input\n * @returns the candidates, best first; empty when nothing matches\n */\n sniffAll(hints: SniffHints): readonly SniffResult[] {\n return rankFormats(hints, this.importerMap.values());\n }\n\n /**\n * The best importer for an input, or null when no registered importer claims it.\n * @param hints - the filename, MIME type and / or head of the input\n * @returns the best candidate, or null\n */\n sniff(hints: SniffHints): SniffResult | null {\n const ranked = this.sniffAll(hints);\n return ranked.length > 0 ? ranked[0] : null;\n }\n\n /**\n * Read an input into a fresh builder and freeze it (design section 8.4: for callers who do not\n * own a sink). The format is the one named in the options, else sniffed from the filename,\n * the MIME type and the first bytes of the content; the builder is seeded from the common\n * options with `directed: true` as a placeholder that the importer overrides from the file.\n * An input holding several graphs yields the first, with a warning naming how many were\n * skipped; importAllGraphs() returns every one.\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns the snapshot, the import report and the freeze report\n */\n async importGraph(input: ImportInput, options: ImportGraphOptions = {}): Promise<ImportGraphResult> {\n const chosen = await this.choose(input, options);\n const builder = seededBuilder(options);\n let report: ImportReport;\n try {\n report = await chosen.importer.import(chosen.source, builder, importerOptions(options));\n } catch (err) {\n await chosen.peeked?.close();\n throw err;\n }\n return result(chosen, builder, report, options);\n }\n\n /**\n * Read every graph of an input (a DOT file with several graphs, a Pajek project with several\n * networks, a JGF document with a `graphs` array), each into its own fresh builder, frozen.\n * A format whose importer has no importAll() holds one graph per input, so the result has one\n * entry. Options, sniffing and the builder seed are those of importGraph().\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns one result per graph, in document order\n */\n async importAllGraphs(input: ImportInput, options: ImportGraphOptions = {}): Promise<ImportGraphResult[]> {\n const chosen = await this.choose(input, options);\n const { importer } = chosen;\n const builders: GraphBuilder[] = [];\n let reports: ImportReport[];\n try {\n if (importer.importAll === undefined) {\n builders.push(seededBuilder(options));\n reports = [await importer.import(chosen.source, builders[0], importerOptions(options))];\n } else {\n reports = await importer.importAll(\n chosen.source,\n () => {\n const builder = seededBuilder(options);\n builders.push(builder);\n return builder;\n },\n importerOptions(options),\n );\n }\n } catch (err) {\n await chosen.peeked?.close();\n throw err;\n }\n return reports.map((report, i) => result(chosen, builders[i], report, options));\n }\n\n /**\n * The importer for an input: the named format, or the sniffed one.\n * @param input - the input\n * @param options - the importGraph options\n * @returns the importer, the sniff, and the input to read (replayed for a stream)\n */\n private async choose(input: ImportInput, options: ImportGraphOptions): Promise<ChosenImporter> {\n const requested = options.format ?? \"auto\";\n if (requested !== \"auto\") {\n return { importer: this.importer(requested), sniff: null, source: input, peeked: null };\n }\n const peeked = await peekHead(input, SNIFF_HEAD_BYTES, options.signal ?? null);\n const sniff = this.sniff({ filename: options.filename, mimeType: options.mimeType, head: peeked.head });\n if (sniff === null) {\n const report = new ImportReportBuilder(\"unknown\", 0);\n return report.fail(\n UNKNOWN_FORMAT_CODE,\n `no registered importer recognises the input${describeHints(options)}; pass the format explicitly`,\n undefined,\n { formats: this.formats() },\n );\n }\n return { importer: this.importer(sniff.format), sniff, source: peeked.input, peeked };\n }\n\n /**\n * Write a snapshot in a format, as UTF-8 chunks (design section 8.5).\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the encoded chunks\n */\n exportGraph(snapshot: GraphSnapshot, format: string, options?: ExportGraphOptions): AsyncIterable<Uint8Array> {\n return this.exporter(format).export(snapshot, options);\n }\n\n /**\n * Write a snapshot in a format, as one string.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the whole document\n */\n async exportGraphToString(snapshot: GraphSnapshot, format: string, options?: ExportGraphOptions): Promise<string> {\n return this.exporter(format).exportToString(snapshot, options);\n }\n\n /**\n * What exporting a snapshot in a format would lose, without writing anything.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the loss notes, empty when the export is exact\n */\n checkExport(snapshot: GraphSnapshot, format: string, options?: ExportGraphOptions): readonly LossNote[] {\n return this.exporter(format).check(snapshot, options);\n }\n}\n\n/**\n * A registry holding the eight built-in importers and exporters in the order of GRAPH_FORMATS.\n * @returns a new registry\n */\nexport function createRegistry(): FormatRegistry {\n return new FormatRegistry()\n .registerImporter(jsonImporter)\n .registerExporter(jsonExporter)\n .registerImporter(graphmlImporter)\n .registerExporter(graphmlExporter)\n .registerImporter(gexfImporter)\n .registerExporter(gexfExporter)\n .registerImporter(csvImporter)\n .registerExporter(csvExporter)\n .registerImporter(gmlImporter)\n .registerExporter(gmlExporter)\n .registerImporter(dotImporter)\n .registerExporter(dotExporter)\n .registerImporter(pajekImporter)\n .registerExporter(pajekExporter)\n .registerImporter(neo4jImporter)\n .registerExporter(neo4jExporter);\n}\n\n/** The default registry: every built-in format. */\nexport const registry: FormatRegistry = createRegistry();\n\n/**\n * Read an input into a fresh builder and freeze it, through the default registry (design section\n * 8.4: `importGraph(input, { format?, ...options })`).\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns the snapshot, the import report and the freeze report\n */\nexport function importGraph(input: ImportInput, options?: ImportGraphOptions): Promise<ImportGraphResult> {\n return registry.importGraph(input, options);\n}\n\n/**\n * Read every graph of an input, each into its own frozen snapshot, through the default registry.\n * @param input - the text, bytes, stream or chunks to read\n * @param options - the format, hints, common and format-specific import options\n * @returns one result per graph, in document order\n */\nexport function importAllGraphs(input: ImportInput, options?: ImportGraphOptions): Promise<ImportGraphResult[]> {\n return registry.importAllGraphs(input, options);\n}\n\n/**\n * Write a snapshot in a format through the default registry, as UTF-8 chunks.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the encoded chunks\n */\nexport function exportGraph(\n snapshot: GraphSnapshot,\n format: string,\n options?: ExportGraphOptions,\n): AsyncIterable<Uint8Array> {\n return registry.exportGraph(snapshot, format, options);\n}\n\n/**\n * Write a snapshot in a format through the default registry, as one string.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the whole document\n */\nexport async function exportGraphToString(\n snapshot: GraphSnapshot,\n format: string,\n options?: ExportGraphOptions,\n): Promise<string> {\n return registry.exportGraphToString(snapshot, format, options);\n}\n\n/**\n * What exporting a snapshot in a format would lose, through the default registry.\n * @param snapshot - the snapshot\n * @param format - the format name\n * @param options - the exporter's common and format-specific options\n * @returns the loss notes, empty when the export is exact\n */\nexport function checkExport(\n snapshot: GraphSnapshot,\n format: string,\n options?: ExportGraphOptions,\n): readonly LossNote[] {\n return registry.checkExport(snapshot, format, options);\n}\n\n/**\n * Sniff an input's format through the default registry.\n * @param hints - the filename, MIME type and / or head of the input\n * @returns the best candidate, or null when no built-in importer claims it\n */\nexport function sniff(hints: SniffHints): SniffResult | null {\n return registry.sniff(hints);\n}\n\n/**\n * The options handed to the importer: everything but the registry's own keys.\n * @param options - the importGraph options\n * @returns the importer's options\n */\nfunction importerOptions(options: ImportGraphOptions): CommonImportOptions {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(options)) {\n if (!REGISTRY_KEYS.has(key)) {\n out[key] = value;\n }\n }\n return out;\n}\n\n/** The importer chosen for an input, and the input to hand it. */\ninterface ChosenImporter {\n /** The importer. */\n readonly importer: GraphImporter;\n /** The sniff that chose it, or null when the caller named the format. */\n readonly sniff: SniffResult | null;\n /** The input to read: the original, or a replaying iterable for a peeked stream. */\n readonly source: ImportInput;\n /** The peeked head of a sniffed input, or null. */\n readonly peeked: PeekedInput | null;\n}\n\n/**\n * A fresh builder seeded from the common options, `directed: true` as a placeholder the importer\n * overrides from the file.\n * @param options - the importGraph options\n * @returns the builder\n */\nfunction seededBuilder(options: ImportGraphOptions): GraphBuilder {\n return new GraphBuilder({\n weightDtype: options.weightDtype ?? \"f64\",\n ...options.builder,\n directed: true,\n addMissingNodes: options.addMissingNodes ?? true,\n duplicateEdges: options.duplicateEdges ?? \"keep\",\n selfLoops: options.selfLoops ?? \"keep\",\n });\n}\n\n/**\n * Freeze an imported builder into an importGraph result.\n * @param chosen - the importer and its sniff\n * @param builder - the filled builder\n * @param report - the importer's report\n * @param options - the importGraph options\n * @returns the frozen result\n */\nfunction result(\n chosen: ChosenImporter,\n builder: GraphBuilder,\n report: ImportReport,\n options: ImportGraphOptions,\n): ImportGraphResult {\n const frozen = builder.freezeWithReport(options.freeze);\n return Object.freeze({\n format: chosen.importer.format,\n sniff: chosen.sniff,\n snapshot: frozen.snapshot,\n report,\n freeze: frozen.report,\n });\n}\n\n/**\n * The E_UNSUPPORTED error of a format name nothing is registered under.\n * @param kind - importer or exporter\n * @param format - the requested name\n * @param known - the registered names\n * @returns the error\n */\nfunction unknownFormat(kind: \"importer\" | \"exporter\", format: string, known: Iterable<string>): GraphFormatError {\n const supported = [...known];\n return new GraphFormatError(\n \"E_UNSUPPORTED\",\n `no ${kind} is registered for format ${JSON.stringify(format)}; known: ${supported.join(\", \")}`,\n { option: \"format\", found: format, supported },\n );\n}\n\n/**\n * The hints of an import, for the unknown-format message.\n * @param options - the importGraph options\n * @returns \" (filename ..., MIME type ...)\" or an empty string\n */\nfunction describeHints(options: ImportGraphOptions): string {\n const parts: string[] = [];\n if (typeof options.filename === \"string\" && options.filename.length > 0) {\n parts.push(`filename ${JSON.stringify(options.filename)}`);\n }\n if (typeof options.mimeType === \"string\" && options.mimeType.length > 0) {\n parts.push(`MIME type ${JSON.stringify(options.mimeType)}`);\n }\n return parts.length === 0 ? \"\" : ` (${parts.join(\", \")})`;\n}\n\n/** A head read from an input, and the input to hand the importer (the same bytes, replayed for a stream). */\ninterface PeekedInput {\n /** The first bytes (or, for a text input, characters) of the content. */\n readonly head: Uint8Array | string;\n /** The input to read: the original for in-memory input, a replaying iterable for a stream. */\n readonly input: ImportInput;\n /**\n * Close the source when the importer never iterated the replaying input (it threw first, an\n * abort for instance): a stream's reader is cancelled and released, an async generator finalised.\n * @returns when the source is closed\n */\n close(): Promise<void>;\n}\n\n/**\n * The first `bytes` of an input without consuming it: in-memory input is sliced; a stream or an\n * async iterable is read until enough is buffered and then replayed (the buffered chunks first,\n * the rest as it arrives) through a new async iterable that cancels the source when the importer\n * stops early.\n * @param input - the input\n * @param bytes - how much to peek\n * @param signal - the cancellation signal, or null\n * @returns the head and the input to read\n */\nasync function peekHead(input: ImportInput, bytes: number, signal: AbortSignal | null): Promise<PeekedInput> {\n if (typeof input === \"string\") {\n return { head: input.slice(0, bytes), input, close: (): Promise<void> => Promise.resolve() };\n }\n if (input instanceof Uint8Array) {\n return { head: input.subarray(0, bytes), input, close: (): Promise<void> => Promise.resolve() };\n }\n throwIfAborted(signal);\n const source: AsyncIterator<string | Uint8Array> =\n typeof (input as { getReader?: unknown }).getReader === \"function\"\n ? readerIterator(input as ReadableStream<Uint8Array>)\n : (input as AsyncIterable<string | Uint8Array>)[Symbol.asyncIterator]();\n const buffered: (string | Uint8Array)[] = [];\n let size = 0;\n let finished = false;\n while (size < bytes) {\n if (signal !== null && signal.aborted) {\n await source.return?.();\n throwIfAborted(signal);\n }\n const next = await source.next();\n if (next.done === true) {\n finished = true;\n break;\n }\n buffered.push(next.value);\n size += typeof next.value === \"string\" ? next.value.length : next.value.byteLength;\n }\n if (!finished && signal !== null && signal.aborted) {\n // an abort that landed on the read completing the head: close the source before rethrowing\n await source.return?.();\n throwIfAborted(signal);\n }\n const replayed = replay(buffered, source, finished);\n return { head: joinHead(buffered, bytes), input: replayed, close: (): Promise<void> => replayed.close() };\n}\n\n/**\n * The head text or bytes of the buffered chunks: bytes when every chunk is bytes, text otherwise\n * (byte chunks decoded leniently; the importer decodes the real input strictly).\n * @param chunks - the buffered chunks\n * @param bytes - the head size\n * @returns the head\n */\nfunction joinHead(chunks: readonly (string | Uint8Array)[], bytes: number): Uint8Array | string {\n if (chunks.every((c) => c instanceof Uint8Array)) {\n const total = Math.min(\n bytes,\n chunks.reduce((sum, c) => sum + c.byteLength, 0),\n );\n const head = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n if (offset >= total) {\n break;\n }\n const part = chunk.subarray(0, Math.min(chunk.byteLength, total - offset));\n head.set(part, offset);\n offset += part.byteLength;\n }\n return head;\n }\n const decoder = new TextDecoder(\"utf-8\", { fatal: false });\n let text = \"\";\n for (const chunk of chunks) {\n text += typeof chunk === \"string\" ? chunk : decoder.decode(chunk, { stream: true });\n if (text.length >= bytes) {\n break;\n }\n }\n return text.slice(0, bytes);\n}\n\n/**\n * An async iterable that yields the buffered chunks, then the rest of the source; the source is\n * closed when the consumer stops early.\n * @param buffered - the chunks already read\n * @param source - the iterator positioned after them\n * @param finished - whether the source is already exhausted\n * @returns the replaying iterable\n */\nfunction replay(\n buffered: readonly (string | Uint8Array)[],\n source: AsyncIterator<string | Uint8Array>,\n finished: boolean,\n): AsyncIterable<string | Uint8Array> & { close(): Promise<void> } {\n let exhausted = finished;\n const close = async (): Promise<void> => {\n if (!exhausted) {\n exhausted = true;\n await source.return?.();\n }\n };\n return {\n close,\n [Symbol.asyncIterator](): AsyncIterator<string | Uint8Array> {\n let position = 0;\n return {\n async next(): Promise<IteratorResult<string | Uint8Array>> {\n if (position < buffered.length) {\n return { done: false, value: buffered[position++] };\n }\n if (exhausted) {\n return { done: true, value: undefined };\n }\n const next = await source.next();\n if (next.done === true) {\n exhausted = true;\n return { done: true, value: undefined };\n }\n return next;\n },\n async return(): Promise<IteratorResult<string | Uint8Array>> {\n await close();\n return { done: true, value: undefined };\n },\n };\n },\n };\n}\n\n/**\n * An async iterator over a ReadableStream's chunks that cancels the stream when closed early.\n * @param stream - the stream\n * @returns the iterator\n */\nfunction readerIterator(stream: ReadableStream<Uint8Array>): AsyncIterator<Uint8Array> {\n const reader = stream.getReader();\n let done = false;\n return {\n async next(): Promise<IteratorResult<Uint8Array>> {\n if (done) {\n return { done: true, value: undefined };\n }\n const result = await reader.read();\n if (result.done) {\n done = true;\n reader.releaseLock();\n return { done: true, value: undefined };\n }\n return { done: false, value: result.value };\n },\n async return(): Promise<IteratorResult<Uint8Array>> {\n if (!done) {\n done = true;\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n return { done: true, value: undefined };\n },\n };\n}\n"],"names":["e","m","result","a","b","c","i","n","sniff"],"mappings":"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"}
package/dist/graphml.js CHANGED
@@ -1,4 +1,4 @@
1
- import { G, a, g, b } from "./chunks/importer-B9qF4e32.js";
1
+ import { G, a, g, b } from "./chunks/importer-BddBW3sf.js";
2
2
  export {
3
3
  G as GRAPHML_ISSUE,
4
4
  a as GRAPHML_LOSS,
package/dist/json.js CHANGED
@@ -1,4 +1,4 @@
1
- import { J, a, b, d, j, c, e } from "./chunks/importer-BELrICWM.js";
1
+ import { J, a, b, d, j, c, e } from "./chunks/importer-BOxwmef_.js";
2
2
  export {
3
3
  J as JSON_DIALECTS,
4
4
  a as JSON_ISSUE,
package/dist/neo4j.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { X as ROLE_TAKEN_CODE$1, o as ID_MERGED_CODE$1, k as DUPLICATE_NODE_CODE$1, M as MISSING_ENDPOINT_CODE$1, y as MISSING_ID_CODE$1, aF as resolveImportOptions, I as ImportReportBuilder, aC as reportSinkOptions, aD as reportUnusedOptions, a as throwIfAborted, v as IdCoercer, l as DirectionResolver, w as ImportError, aO as declareAttribute, b1 as declareOn, aL as uniqueColumnName, U as RENAMED_CODE, aA as parseWeightText, aP as parseDeclaredTemporal, aQ as parseDeclaredValue, aR as losesPrecision, ah as declareCompanion, T as PRECISION_CODE, aT as splitListText, au as isImportInput, b0 as takenIn, ar as inputLength, ac as capabilities, p as ID_TEXT_COLLISION_CODE, q as ID_TEXT_TYPE_CODE, ax as joinText, ak as encodeChunks, aE as resolveExportOptions, ad as checkCapabilities, az as pairFolding, L as LOSS, aX as mapDeclaredType, al as explicitWeights, at as isCanonicalIntegerText, aZ as formatTemporal, ao as formatF64, an as formatF32, aq as formatInteger, Y as SINK_OPTION_CODE, P as OPTION_IGNORED_CODE, C as COLUMN_RENAMED_CODE, a3 as UNKNOWN_ATTR_TYPE_CODE, a5 as UNKNOWN_ENCODING_CODE, n as ENCODING_FALLBACK_CODE, s as INVALID_ENCODING_CODE, u as INVALID_UTF8_CODE, a8 as WEIGHT_KEY_CLASH_CODE, W as ROLE_DROPPED_CODE, J as MUTUAL_EXPANDED_CODE } from "./chunks/writer-BdMak4_J.js";
2
2
  import { c as checkRecordSyntax, R as RecordReader, B as BAD_QUOTE_CODE, U as UNCLOSED_QUOTE_CODE } from "./chunks/records-BKSMowhR.js";
3
3
  import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
4
- import { n as quoteCsvCell } from "./chunks/escape-DXbOoLKl.js";
4
+ import { n as quoteCsvCell } from "./chunks/escape-BjmIaFFo.js";
5
5
  const STRUCTURAL = /* @__PURE__ */ new Set(["ID", "LABEL", "START_ID", "END_ID", "TYPE", "IGNORE"]);
6
6
  const SPACE_KINDS = /* @__PURE__ */ new Set(["ID", "START_ID", "END_ID"]);
7
7
  const HEADER_MARKER = /^[^:{}()]*:\s*(ID|START_ID|END_ID)\s*(\(|\{|$)/i;
@@ -89,6 +89,7 @@ function formatHeaderField(name, type, space) {
89
89
  const LABELS_COLUMN = "labels";
90
90
  const TYPE_COLUMN = "type";
91
91
  const ID_SPACE_COLUMN = "idSpace";
92
+ const ORIGINAL_ID_COLUMN = "originalId";
92
93
  const HEADER_CODE = "E_NEO4J_HEADER";
93
94
  const COLUMN_COUNT_CODE = "E_NEO4J_COLUMN_COUNT";
94
95
  const MISSING_ID_CODE = MISSING_ID_CODE$1;
@@ -137,6 +138,13 @@ const ID_SPACE_DECL = {
137
138
  role: "idSpace",
138
139
  origin: { format: NEO4J$1, id: ":ID", title: null, type: "ID", namespace: null }
139
140
  };
141
+ const ORIGINAL_ID_DECL = {
142
+ name: ORIGINAL_ID_COLUMN,
143
+ dtype: "string",
144
+ nullable: true,
145
+ // origin.type stays null so the column is never mistaken for a stored id (`name:ID`)
146
+ origin: { format: NEO4J$1, id: ":ID", title: null, type: null, namespace: null }
147
+ };
140
148
  const ARRAY_DELIMITERS = { ";": "semicolon", ",": "comma", "|": "pipe" };
141
149
  const NEO4J_DELIMITER_CANDIDATES = Object.freeze([",", " "]);
142
150
  function resolveNeo4jOptions(options) {
@@ -291,6 +299,7 @@ class Neo4jImportSession {
291
299
  this.labelsHandle = INVALID_INDEX;
292
300
  this.typeHandle = INVALID_INDEX;
293
301
  this.idSpaceHandle = INVALID_INDEX;
302
+ this.originalIdHandle = INVALID_INDEX;
294
303
  this.ignoredColumns = 0;
295
304
  this.headerSet = false;
296
305
  this.values = [];
@@ -510,6 +519,8 @@ class Neo4jImportSession {
510
519
  width: fields.length,
511
520
  startCell,
512
521
  endCell,
522
+ startSpace,
523
+ endSpace,
513
524
  startSpaceCode: startSpace === null ? 0 : this.registry.codeOf(startSpace),
514
525
  endSpaceCode: endSpace === null ? 0 : this.registry.codeOf(endSpace),
515
526
  typeCell,
@@ -603,6 +614,7 @@ class Neo4jImportSession {
603
614
  ensureIdSpace() {
604
615
  if (this.idSpaceHandle === INVALID_INDEX) {
605
616
  this.idSpaceHandle = this.declareReserved("node", ID_SPACE_DECL);
617
+ this.originalIdHandle = this.declareReserved("node", ORIGINAL_ID_DECL);
606
618
  }
607
619
  }
608
620
  /**
@@ -672,11 +684,12 @@ class Neo4jImportSession {
672
684
  report.counts.skippedNodes++;
673
685
  return;
674
686
  }
675
- const id = this.coerceId(idText2, line);
676
- if (id === null || !this.parseProperties(section.properties, cells, quoted, line, idText2)) {
687
+ const coerced = this.coerceId(idText2, line);
688
+ if (coerced === null || !this.parseProperties(section.properties, cells, quoted, line, idText2)) {
677
689
  report.counts.skippedNodes++;
678
690
  return;
679
691
  }
692
+ const id = qualify(coerced, section.space);
680
693
  let labels;
681
694
  if (section.labelCells.length > 0 || section.extraLabels.length > 0) {
682
695
  labels = this.labelsOf(section, cells, quoted);
@@ -709,6 +722,7 @@ class Neo4jImportSession {
709
722
  }
710
723
  if (section.space !== null) {
711
724
  sink.setNodeValue(this.idSpaceHandle, index, section.space);
725
+ sink.setNodeValue(this.originalIdHandle, index, idText2);
712
726
  }
713
727
  if (labels !== void 0) {
714
728
  sink.setNodeValue(this.labelsHandle, index, labels);
@@ -756,12 +770,14 @@ class Neo4jImportSession {
756
770
  return;
757
771
  }
758
772
  const element = `${startText}->${endText}`;
759
- const source = this.coerceId(startText, line);
760
- const target = source === null ? null : this.coerceId(endText, line);
761
- if (source === null || target === null) {
773
+ const start = this.coerceId(startText, line);
774
+ const end = start === null ? null : this.coerceId(endText, line);
775
+ if (start === null || end === null) {
762
776
  report.counts.skippedEdges++;
763
777
  return;
764
778
  }
779
+ const source = qualify(start, section.startSpace);
780
+ const target = qualify(end, section.endSpace);
765
781
  let wrongSpace = null;
766
782
  if (this.wrongSpace(source, section.startSpaceCode)) {
767
783
  wrongSpace = startText;
@@ -811,9 +827,9 @@ class Neo4jImportSession {
811
827
  report.counts.edges++;
812
828
  }
813
829
  /**
814
- * Whether an endpoint id belongs to a node a node row declared in another id space than the
815
- * endpoint's header declares. An endpoint without a declared space, or a node no row declared
816
- * yet, is looked up by id alone.
830
+ * Whether a qualified endpoint id belongs to a node a node row declared in another id space than
831
+ * the endpoint's header declares (an unspaced node whose id text is `Space:id`). An endpoint
832
+ * without a declared space, or a node no row declared yet, is looked up by id alone.
817
833
  * @param id - the endpoint id
818
834
  * @param spaceCode - the endpoint's space code, 0 for none
819
835
  * @returns true when the endpoint resolves to a node of another space
@@ -972,6 +988,9 @@ class Neo4jImportSession {
972
988
  return labels;
973
989
  }
974
990
  }
991
+ function qualify(id, space) {
992
+ return space === null ? id : `${space}:${String(id)}`;
993
+ }
975
994
  function emptyValue(spec) {
976
995
  if (spec.list) {
977
996
  return [];
@@ -1163,6 +1182,7 @@ class ExportPlan {
1163
1182
  this.options = options;
1164
1183
  this.common = common;
1165
1184
  this.idSpace = snapshot.nodes.byRole("idSpace");
1185
+ this.originalId = [...snapshot.nodes].find((column) => isIdText(column.meta)) ?? null;
1166
1186
  this.labels = snapshot.nodes.byRole("labels");
1167
1187
  this.kind = snapshot.edges.byRole("kind");
1168
1188
  for (const column of snapshot.nodes) {
@@ -1285,7 +1305,7 @@ class ExportPlan {
1285
1305
  if (companions.has(column) || role !== null && SKIPPED_ROLES.has(role)) {
1286
1306
  continue;
1287
1307
  }
1288
- if (domain === "node" && (isStoredId(meta) || column === this.idSpace || column === this.labels)) {
1308
+ if (domain === "node" && (isStoredId(meta) || column === this.idSpace || column === this.labels || column === this.originalId)) {
1289
1309
  continue;
1290
1310
  }
1291
1311
  if (domain === "edge" && (role === "id" || column === this.kind)) {
@@ -1657,6 +1677,19 @@ class ExportPlan {
1657
1677
  const value = idSpace.value(index);
1658
1678
  return typeof value === "string" ? value : String(value);
1659
1679
  }
1680
+ /**
1681
+ * The `:ID` / `:START_ID` / `:END_ID` text of a node: its originalId value when it has an id
1682
+ * space (the importer stored it as `Space:id`), its id otherwise.
1683
+ * @param index - the node index
1684
+ * @returns the id text
1685
+ */
1686
+ idTextOf(index) {
1687
+ const { originalId } = this;
1688
+ if (originalId !== null && originalId.isSet(index) && this.spaceOf(index) !== null) {
1689
+ return textOf(originalId, index);
1690
+ }
1691
+ return idText(this.snapshot.ids.idOf(index));
1692
+ }
1660
1693
  /**
1661
1694
  * The stored-id column of a node: the first one set in declaration order.
1662
1695
  * @param index - the node index
@@ -1694,7 +1727,6 @@ class ExportPlan {
1694
1727
  */
1695
1728
  *nodeLines() {
1696
1729
  const { snapshot, labels, nodeColumns } = this;
1697
- const { ids } = snapshot;
1698
1730
  const { delimiter } = this.options.syntax;
1699
1731
  const propertyHeaders = nodeColumns.map((plan2) => plan2.header).join(delimiter);
1700
1732
  let sections = 0;
@@ -1720,7 +1752,7 @@ class ExportPlan {
1720
1752
  `;
1721
1753
  }
1722
1754
  cells.length = 0;
1723
- cells.push(this.cell(idText(ids.idOf(i))));
1755
+ cells.push(this.cell(this.idTextOf(i)));
1724
1756
  if (labels !== null) {
1725
1757
  cells.push(this.cell(this.labelsText(i)));
1726
1758
  }
@@ -1749,7 +1781,6 @@ class ExportPlan {
1749
1781
  */
1750
1782
  *relationshipLines() {
1751
1783
  const { snapshot, kind, weights, edgeColumns, folding } = this;
1752
- const { ids } = snapshot;
1753
1784
  const { delimiter } = this.options.syntax;
1754
1785
  const list = snapshot.edgeList();
1755
1786
  const propertyHeaders = edgeColumns.map((plan2) => plan2.header).join(delimiter);
@@ -1787,7 +1818,7 @@ class ExportPlan {
1787
1818
  yield headerOf(startSpace, endSpace);
1788
1819
  }
1789
1820
  cells.length = 0;
1790
- cells.push(this.cell(idText(ids.idOf(u))), this.cell(idText(ids.idOf(v))));
1821
+ cells.push(this.cell(this.idTextOf(u)), this.cell(this.idTextOf(v)));
1791
1822
  if (kind !== null) {
1792
1823
  cells.push(this.cell(kind.isSet(e) ? textOf(kind, e) : null));
1793
1824
  }
@@ -1864,6 +1895,9 @@ class ExportPlan {
1864
1895
  return formatScalar(column.dtype === "json" ? column.values[row] : column.value(row), scalar, text);
1865
1896
  }
1866
1897
  }
1898
+ function isIdText(meta) {
1899
+ return meta.role === null && meta.dtype === "string" && meta.origin !== null && meta.origin.format === NEO4J && meta.origin.id === ":ID" && meta.origin.type === null;
1900
+ }
1867
1901
  function isStoredId(meta) {
1868
1902
  return meta.role === null && meta.dtype === "string" && meta.origin !== null && meta.origin.format === NEO4J && meta.origin.type === ID_TYPE;
1869
1903
  }
@@ -2050,9 +2084,9 @@ const NEO4J_ISSUE = Object.freeze({
2050
2084
  MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
2051
2085
  /** A node id repeated in one id space (last write wins). */
2052
2086
  DUPLICATE_NODE: DUPLICATE_NODE_CODE,
2053
- /** A node id declared in two id spaces. */
2087
+ /** A spaced node id `Space:id` that equals the text of an id declared without a space. */
2054
2088
  ID_SPACE_COLLISION: ID_SPACE_COLLISION_CODE,
2055
- /** A relationship endpoint whose id belongs to a node of another id space; the row is skipped. */
2089
+ /** A spaced relationship endpoint `Space:id` that names a node declared without a space; the row is skipped. */
2056
2090
  ENDPOINT_SPACE: ENDPOINT_SPACE_CODE,
2057
2091
  /** Two id texts merged into one number under ids "number". */
2058
2092
  ID_MERGED: ID_MERGED_CODE,
@@ -2103,6 +2137,7 @@ export {
2103
2137
  NEO4J_CAPABILITIES,
2104
2138
  NEO4J_ISSUE,
2105
2139
  NEO4J_LOSS,
2140
+ ORIGINAL_ID_COLUMN,
2106
2141
  TYPE_COLUMN,
2107
2142
  neo4jExporter,
2108
2143
  neo4jImporter