@modernrelay/orbit-omnigraph 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  Optional Omnigraph data-source adapter for Orbit. Framework-agnostic:
4
4
  depends only on the framework-free ingestion/error surface of `@modernrelay/orbit-core`
5
- and the official TypeScript SDK `@modernrelay/omnigraph` (0.9.x) — never on React, and
5
+ and the official TypeScript SDK `@modernrelay/omnigraph` (0.14.0) — never on React, and
6
6
  core never depends on it.
7
7
 
8
+ Requires Node.js 22+ for server/CLI use and the OmniGraph HTTP API contract `0.14`.
9
+ Upgrade the server and SDK together: 0.13 servers are rejected before graph reads.
10
+
8
11
  ## The v1 rule
9
12
 
10
13
  > **v1 loads graphs via `og.export()`; queries only ever resolve ids** for search and
@@ -52,10 +55,32 @@ const result = await source.load(instance /*, abortSignal */);
52
55
  (`datasetKey` = `og:<graphId>:<branch>`): edge lines arrive first (lexicographic table-key
53
56
  order) and pass straight through — core's pending-endpoint index links them as nodes land. Every append is awaited, and the atomic load has a finite whole-export byte budget
54
57
  (`maxPendingBytes`, 512 MiB by default); exceeding it aborts without publishing a partial
55
- graph. Ids are namespaced through the codec, and wire encodings are normalized to
56
- the query-path string forms. Byte progress and budgets use re-serialized UTF-8 NDJSON size,
58
+ graph. Ids are namespaced through the codec, and export values are normalized as
59
+ described below. Byte progress and budgets use re-serialized UTF-8 NDJSON size,
57
60
  including one line break per export row.
58
61
 
62
+ OmniGraph 0.14 exports physical identity at the top level:
63
+
64
+ ```json
65
+ {"type":"Signal","id":"signal-001","data":{"id":42,"title":"Latency spike"}}
66
+ {"edge":"Correlates","id":"edge-001","from":"signal-001","to":"signal-002","data":{"confidence":0.9}}
67
+ ```
68
+
69
+ The node becomes `id: encodeSourceId('Signal', 'signal-001')` with
70
+ `attrs: { id: 42, title: 'Latency spike', 'orbit:type': 'Signal' }`.
71
+ Physical IDs never come from `data.id`; that field is an ordinary schema property.
72
+ Generated props contain only schema properties, retaining a declared `id` type and
73
+ nullability. Use `GraphNode.id` / `GraphEdge.id` for physical identity.
74
+
75
+ `Date` calendar strings are retained, with signed years padded to ISO's six digits.
76
+ UTC `DateTime` strings from the server omit
77
+ their zone, so the adapter adds explicit UTC and canonical milliseconds
78
+ (`2024-01-01T12:00:00` → `2024-01-01T12:00:00.000Z`), including list elements.
79
+ Omitted nullable schema properties become explicit `null`, matching generated
80
+ `T | null` fields. Inline `base64:` blobs become data URIs; external URIs pass through.
81
+ Null list elements are retained, so generated `[T]` attributes use `(T | null)[]`.
82
+ Vectors retain `number[]`: the server rejects null vector elements.
83
+
59
84
  The `target` is structural (`IngestTarget`: `beginIngest` + `getRevisions` +
60
85
  optional `getDiagnostics`) — deliberately decoupled from `GraphInstance`, which satisfies
61
86
  it structurally; so does any recorder or headless pipeline.
@@ -63,9 +88,12 @@ it structurally; so does any recorder or headless pipeline.
63
88
  ## `sourceRevision` and drift
64
89
 
65
90
  The canonical `sourceRevision` is a hash of
66
- `{ graphId, branch, headBefore, headAfter, schemaFingerprint }` — but `/export` is
67
- branch-only (no snapshot isolation), so `headAfter` is only knowable after the stream,
91
+ `{ graphId, branch, headBefore, headAfter, schemaFingerprint }` (plus `typeNames`
92
+ for partial exports). `/export` accepts a branch and pins an internal snapshot, but
93
+ does not accept the caller's sampled head, so `headAfter` is only knowable after the stream,
68
94
  while core's `beginIngest` requires `sourceRevision` up front for a replace session.
95
+ Each head check requests a fresh commit-history page with `limit: 1` and reads its
96
+ captured `headCommitId`; it does not fetch the full history or follow continuation tokens.
69
97
 
70
98
  Resolution: under `'reject'` and `'retry-once'`, a session begins under the **provisional**
71
99
  revision (the canonical hash with `headAfter := headBefore`), the stream appends into it,
@@ -105,21 +133,26 @@ const search = createOmnigraphSearchService({
105
133
  branch: 'main', // default
106
134
  queryName: 'search-intel', // registry name; invoked as POST /queries/search-intel
107
135
  // params: (q, limit) => ({ q, limit }), // default — reshape for your query's params
108
- typeOf: { $s: 'Signal' }, // REQUIRED: column → node type for the id encoding
109
- // labelColumn: '$s.title', // default: the first string-valued column per row
136
+ typeOf: { s: 'Signal' }, // REQUIRED: result column → node type for the id encoding
137
+ // labelColumn: 's.title', // default: the first string-valued column per row
110
138
  });
111
139
 
112
140
  const instance = createGraphInstance({ engine, services: { search } });
113
141
  ```
114
142
 
143
+ For the repository's `search_signals` fixture query, use `queryName: 'search_signals'`
144
+ and `params: (q) => ({ q })`: that query declares only `q` and has a fixed server
145
+ limit of 10. The adapter still caps returned results at the caller's requested limit.
146
+
115
147
  Row mapping (override wholesale with `mapRow: (row) => SearchResult | null`, `null` skips):
116
148
 
117
149
  - **id** — Omnigraph ids are unique per type only and query rows carry no type
118
150
  discriminator, so the adapter **requires** a caller-supplied column→node-type mapping
119
151
  (`typeOf`): a record keyed by the bare-variable projection columns holding node structs
120
- (`return { $s }` → `{ $s: 'Signal' }`; the first listed column present wins), or a
152
+ (`return { $s }` → `{ s: 'Signal' }`; the first listed column present wins), or a
121
153
  per-row function returning the type name (the row's first node-struct column supplies the
122
- physical id). Every id is qualified via `encodeSourceId(nodeType, physicalId)` — it
154
+ physical `@id`). For example, `{ s: { '@id': 'signal-001', id: 42 } }` identifies
155
+ `signal-001`; its ordinary `id` property is not used. Every physical id is qualified via `encodeSourceId(nodeType, physicalId)` — it
123
156
  round-trips `decodeSourceId` and matches export-loaded node ids exactly.
124
157
  - **score** — a finite number in the row's `score` column (the score lane never doubles as
125
158
  a label).
@@ -135,26 +168,35 @@ rethrow as plain `omnigraph:`-prefixed `Error`s; aborts surface as `AbortError`.
135
168
  > `activateSearchResult` classifies those as `'not-loaded'` — the host may remedy
136
169
  > that only by re-running export for the relevant types (query-path single-node fetch is
137
170
  > not implemented in v1). Constrain the stored query to the loaded types, or load the
138
- > full graph, to avoid the mismatch. Discovery via `og.queries.list()` returns only the
139
- > `mcp.expose == true` registry subset — invoking a known `queryName` works regardless.
171
+ > full graph, to avoid the mismatch. Discovery via `og.queries.list()` returns the
172
+ > full stored-query registry; MCP exposure does not restrict SDK discovery or invocation.
140
173
 
141
174
  ## Server-only entry — do not import from the browser
142
175
 
143
176
  `@modernrelay/orbit-omnigraph/server` (`createOmnigraphServerClient`) is the only
144
- authenticated SDK construction and must stay server-side: `omnigraph-server` ships no CORS
145
- configuration and uses static bearer tokens (secret material). Browser deployments
177
+ authenticated SDK construction supplied by this adapter and must stay server-side:
178
+ the helper accepts a static bearer token (secret material). Browser deployments
146
179
  pass a preconfigured safe same-origin/public client to `createOmnigraphSource` — the
147
180
  browser entry deliberately has no `baseUrl`/`token` option — and typically route reads
148
181
  through a proxy/BFF. A client-bundle exclusion gate (`scripts/pack-smoke.mjs`,
149
182
  `treeshake:omnigraph-client`) asserts the server entry never reaches a client bundle.
150
183
 
184
+ The SDK sends `Omnigraph-Http-Api: 0.14` and checks the matching response header,
185
+ including on streams and errors. It performs an unauthenticated `HEAD /healthz`
186
+ contract check before each graph request; `GET /healthz` is also public. A proxy
187
+ must preserve these methods and headers. Configure the server access policy to
188
+ permit the adapter's schema, commit, export, and stored-query reads; supplying a
189
+ static token alone does not grant every operation under the server's access policy.
190
+
151
191
  ## Error surface
152
192
 
153
193
  SDK typed errors (`NetworkError`, `ConflictError`, …) never cross this package's public
154
194
  surface — failures rethrow as plain `Error`s with a stable `omnigraph:` message prefix.
155
195
  Cancellation (`AbortSignal`) surfaces as an `AbortError`; the session is aborted and the
156
- graph left untouched. SDK/server `major.minor` version mismatches (`og.health()` vs the
157
- SDK's pinned `SERVER_VERSION`) surface as a result warning, never a hard failure.
196
+ graph left untouched. This includes cancellation during contract discovery and
197
+ stored read-query invocation. Missing or mismatched HTTP contract headers fail closed
198
+ before ingestion can commit. A build-version `major.minor` mismatch (`og.health()`
199
+ vs the SDK's pinned `SERVER_VERSION`) warns only when the HTTP contract checks pass.
158
200
 
159
201
  ## Also in the box
160
202
 
@@ -403,7 +403,7 @@ function bigIntKeyWarnings(schema) {
403
403
  const out = [];
404
404
  for (const t of [...schema.nodes, ...schema.edges]) {
405
405
  for (const p of t.properties) {
406
- if ((p.type === "I64" || p.type === "U64") && (p.key || p.name === "id")) {
406
+ if ((p.type === "I64" || p.type === "U64") && p.key) {
407
407
  out.push({ type: t.name, property: p.name });
408
408
  }
409
409
  }
@@ -435,60 +435,75 @@ function classifyExportLine(line) {
435
435
  if (!isRecord(line) || !isRecord(line["data"])) return { kind: "unknown" };
436
436
  const data = line["data"];
437
437
  if (typeof line["edge"] === "string" && typeof line["from"] === "string" && typeof line["to"] === "string") {
438
- return { kind: "edge", edge: line["edge"], from: line["from"], to: line["to"], data };
438
+ return {
439
+ kind: "edge",
440
+ edge: line["edge"],
441
+ id: requireStringId(line, `Edge '${line["edge"]}'`),
442
+ from: line["from"],
443
+ to: line["to"],
444
+ data
445
+ };
439
446
  }
440
447
  if (typeof line["type"] === "string") {
441
- return { kind: "node", type: line["type"], data };
448
+ return { kind: "node", type: line["type"], id: requireStringId(line, `Node '${line["type"]}'`), data };
442
449
  }
443
450
  return { kind: "unknown" };
444
451
  }
445
- var MS_PER_DAY = 864e5;
446
- function formatEpochDay(days) {
447
- const d = new Date(days * MS_PER_DAY);
448
- const t = d.getTime();
449
- if (!Number.isFinite(t)) return String(days);
450
- const y = d.getUTCFullYear();
451
- const year = y < 0 ? `-${String(-y).padStart(6, "0")}` : y > 9999 ? `+${String(y).padStart(6, "0")}` : String(y).padStart(4, "0");
452
- const month = String(d.getUTCMonth() + 1).padStart(2, "0");
453
- const day = String(d.getUTCDate()).padStart(2, "0");
454
- return `${year}-${month}-${day}`;
452
+ function isoYear(wireYear) {
453
+ const year = Number(wireYear);
454
+ return year >= 0 && year <= 9999 ? String(year).padStart(4, "0") : `${year < 0 ? "-" : "+"}${String(Math.abs(year)).padStart(6, "0")}`;
455
455
  }
456
456
  function normalizeValue(type, value) {
457
457
  if (type === void 0 || value === null || value === void 0) return value;
458
- if (type === "Date" && typeof value === "number" && Number.isInteger(value)) {
459
- return formatEpochDay(value);
458
+ if (typeof type === "object" && type.kind === "list" && Array.isArray(value)) {
459
+ return value.map((item) => normalizeValue(type.element, item));
460
+ }
461
+ if (type === "Date") {
462
+ const date = typeof value === "string" ? /^([+-]?\d{4,})(-\d{2}-\d{2})$/.exec(value) : null;
463
+ if (date === null) {
464
+ throw new InvalidExportLineError("Date export values must be calendar-date strings");
465
+ }
466
+ return `${isoYear(date[1])}${date[2]}`;
460
467
  }
461
- if (type === "DateTime" && typeof value === "number" && Number.isFinite(value)) {
462
- const d = new Date(value);
463
- return Number.isFinite(d.getTime()) ? d.toISOString() : value;
468
+ if (type === "DateTime") {
469
+ const timestamp = typeof value === "string" ? /^([+-]?\d{4,})(-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,3}))?$/.exec(value) : null;
470
+ if (timestamp === null) {
471
+ throw new InvalidExportLineError("DateTime export values must be UTC timestamp strings without a timezone suffix");
472
+ }
473
+ return `${isoYear(timestamp[1])}${timestamp[2]}.${(timestamp[3] ?? "").padEnd(3, "0")}Z`;
464
474
  }
465
475
  if (type === "Blob" && typeof value === "string" && value.startsWith("base64:")) {
466
476
  return `data:application/octet-stream;base64,${value.slice("base64:".length)}`;
467
477
  }
468
478
  return value;
469
479
  }
470
- function propTypeMap(t) {
480
+ function propMap(t) {
471
481
  const map = /* @__PURE__ */ new Map();
472
- if (t) for (const p of t.properties) map.set(p.name, p.type);
482
+ if (t) for (const p of t.properties) map.set(p.name, p);
473
483
  return map;
474
484
  }
475
- function normalizeData(data, types) {
485
+ function normalizeData(data, properties) {
476
486
  const out = {};
477
487
  for (const [k, v] of Object.entries(data)) {
478
488
  Object.defineProperty(out, k, {
479
- value: normalizeValue(types.get(k), v),
489
+ value: normalizeValue(properties.get(k)?.type, v),
480
490
  enumerable: true,
481
491
  writable: true,
482
492
  configurable: true
483
493
  });
484
494
  }
495
+ for (const property of properties.values()) {
496
+ if (property.optional && !Object.prototype.hasOwnProperty.call(data, property.name)) {
497
+ Object.defineProperty(out, property.name, { value: null, enumerable: true, writable: true, configurable: true });
498
+ }
499
+ }
485
500
  return out;
486
501
  }
487
- function requireStringId(data, what) {
488
- const id = data["id"];
502
+ function requireStringId(line, what) {
503
+ const id = line.id;
489
504
  if (typeof id !== "string") {
490
505
  throw new InvalidExportLineError(
491
- `${what} export line has ${id === void 0 ? "no" : "a non-string"} 'data.id' \u2014 cannot build a stable orbit id; data.id must be a string.`
506
+ `${what} export line has ${id === void 0 ? "no" : "a non-string"} top-level 'id' \u2014 cannot build a stable orbit id; id must be a string, separate from user properties.`
492
507
  );
493
508
  }
494
509
  return id;
@@ -496,13 +511,13 @@ function requireStringId(data, what) {
496
511
  var ORBIT_TYPE_KEY = "orbit:type";
497
512
  function normalizeNode(line, schema) {
498
513
  const nodeType = schema.nodes.find((n) => n.name === line.type);
499
- const id = encodeSourceId(line.type, requireStringId(line.data, `Node '${line.type}'`));
514
+ const id = encodeSourceId(line.type, requireStringId(line, `Node '${line.type}'`));
500
515
  return {
501
516
  id,
502
517
  // The discriminator is adapter-owned identity metadata. It lands LAST so
503
518
  // a forward-compatible export field literally named `orbit:type` cannot
504
519
  // forge the value the generated attrs unions promise.
505
- attrs: { ...normalizeData(line.data, propTypeMap(nodeType)), [ORBIT_TYPE_KEY]: line.type }
520
+ attrs: { ...normalizeData(line.data, propMap(nodeType)), [ORBIT_TYPE_KEY]: line.type }
506
521
  };
507
522
  }
508
523
  function normalizeEdge(line, schema) {
@@ -510,17 +525,17 @@ function normalizeEdge(line, schema) {
510
525
  if (endpoints === null) throw new UnknownEdgeTypeError(line.edge);
511
526
  const lower = line.edge.toLowerCase();
512
527
  const edgeType = schema.edges.find((e) => e.name === line.edge) ?? schema.edges.find((e) => e.name.toLowerCase() === lower);
513
- const id = encodeSourceId(line.edge, requireStringId(line.data, `Edge '${line.edge}'`));
528
+ const id = encodeSourceId(line.edge, requireStringId(line, `Edge '${line.edge}'`));
514
529
  return {
515
530
  id,
516
531
  source: encodeSourceId(endpoints.from, line.from),
517
532
  target: encodeSourceId(endpoints.to, line.to),
518
533
  // As on nodes, the adapter's resolved edge name lands last and always
519
534
  // wins the discriminator key; a source `type` property is ordinary data.
520
- attrs: { ...normalizeData(line.data, propTypeMap(edgeType)), [ORBIT_TYPE_KEY]: line.edge }
535
+ attrs: { ...normalizeData(line.data, propMap(edgeType)), [ORBIT_TYPE_KEY]: line.edge }
521
536
  };
522
537
  }
523
538
 
524
539
  export { InvalidExportLineError, ORBIT_TYPE_KEY, UnknownEdgeTypeError, bigIntKeyWarnings, classifyExportLine, decodeSourceId, edgeEndpointTypes, encodeSourceId, encodeSyntheticEdgeId, normalizeEdge, normalizeNode, parsePgSchema, schemaFingerprint };
525
- //# sourceMappingURL=chunk-WMBDYPK2.js.map
526
- //# sourceMappingURL=chunk-WMBDYPK2.js.map
540
+ //# sourceMappingURL=chunk-TFBKN675.js.map
541
+ //# sourceMappingURL=chunk-TFBKN675.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/idCodec.ts","../src/pgSchema.ts","../src/normalize.ts"],"names":["anns","end","body","braceIdx","blockEnd"],"mappings":"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","file":"chunk-TFBKN675.js","sourcesContent":["/**\n * Identity codec.\n *\n * Omnigraph node ids are unique **per type only** (derived from the `@key`\n * tuple within each type's table), so unqualified ids are unsound as orbit\n * `NodeId`s. Every adapter path — nodes, edges, search results, view state,\n * services — MUST qualify ids through this one collision-proof tuple codec.\n * `JSON.stringify` of a fixed-arity string tuple is injective over its inputs\n * (quotes, brackets, commas, and unicode in either component are escaped), so\n * no `(kind, sourceId)` pair can collide with a different pair.\n *\n * Human-readable labels stay separate from identity; there is deliberately no\n * `namespaceIds:false` escape hatch.\n */\n\nexport interface DecodedSourceId {\n /** The namespace component: a node type name or an edge type name. */\n kind: string;\n /** The physical Omnigraph id within that type's table. */\n sourceId: string;\n}\n\n/**\n * Encode a `(kind, sourceId)` pair as a collision-proof orbit id.\n *\n * - Nodes: `encodeSourceId(NodeType, id)` using the top-level export id\n * - Edges: `encodeSourceId(EdgeName, id)` using the top-level export id; endpoints use\n * `encodeSourceId(endpointType, from|to)`.\n */\nexport function encodeSourceId(kind: string, sourceId: string): string {\n return JSON.stringify([kind, sourceId]);\n}\n\n/**\n * Decode an id produced by {@link encodeSourceId}.\n *\n * Returns `null` for anything that does not conform exactly (non-JSON input,\n * non-array JSON, wrong arity — including synthetic edge ids, which are\n * 4-tuples — or non-string elements). Never throws.\n */\nexport function decodeSourceId(id: string): DecodedSourceId | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(id);\n } catch {\n return null;\n }\n if (!Array.isArray(parsed) || parsed.length !== 2) return null;\n const [kind, sourceId] = parsed as unknown[];\n if (typeof kind !== 'string' || typeof sourceId !== 'string') return null;\n return { kind, sourceId };\n}\n\n/**\n * Synthetic edge id for **query-derived** subgraphs.\n *\n * The GQ grammar has no edge variable, so the query path can never surface\n * physical edge ids; query-derived edges are identified by\n * `['synthetic-edge', EdgeName, source, target]` where `source`/`target` are\n * the already-encoded endpoint node ids.\n *\n * Exported for the future query path.\n * Caveats: parallel edges collapse under this scheme, and a dataset\n * MUST NOT mix synthetic ids with physical `/export` ids for the same edges\n * one scheme per dataset, never both. Because a synthetic id is a 4-tuple,\n * {@link decodeSourceId} rejects it (`null`), keeping the two schemes\n * mechanically un-confusable.\n */\nexport function encodeSyntheticEdgeId(edgeName: string, source: string, target: string): string {\n return JSON.stringify(['synthetic-edge', edgeName, source, target]);\n}\n","/**\n * `.pg` schema model + tolerant parser.\n *\n * The adapter needs three things from the graph schema, all served here:\n * 1. edge endpoint-type resolution (export edge lines carry bare `from`/`to`\n * ids without endpoint types),\n * 2. per-property wire-type knowledge for temporal/blob normalization,\n * 3. a stable schema fingerprint for the export revision stamp.\n *\n * The parser is deliberately tolerant: it extracts the declarations it\n * understands (interfaces, node blocks, edge declarations, properties,\n * constraints) and preserves anything else — unknown annotations survive as\n * raw strings, unknown type spellings become `{ kind: 'unknown' }` — so a\n * newer server grammar degrades gracefully instead of failing the load.\n *\n * Browser-safe: no `node:` imports. The fingerprint is a pure FNV-1a 64-bit\n * hash rather than `node:crypto` sha256 so this module can sit on the\n * browser entry's critical path.\n */\n\n// ---------------------------------------------------------------------------\n// Model\n// ---------------------------------------------------------------------------\n\n/** Closed scalar set per the `.pg` grammar (docs/user/schema). */\nexport type PgScalarName =\n | 'String'\n | 'Blob'\n | 'Bool'\n | 'I32'\n | 'I64'\n | 'U32'\n | 'U64'\n | 'F32'\n | 'F64'\n | 'Date'\n | 'DateTime';\n\nexport type PgType =\n | PgScalarName\n | { kind: 'vector'; dim: number }\n | { kind: 'enum'; values: readonly string[] }\n | { kind: 'list'; element: PgScalarName }\n | { kind: 'unknown'; raw: string };\n\nexport interface PgProperty {\n name: string;\n type: PgType;\n /** `T?` nullability. */\n optional: boolean;\n /** Set by inline `@key` or a body-level `@key(...)` naming this property. */\n key: boolean;\n /** Set by inline `@unique` or a body-level `@unique(...)` naming this property. */\n unique: boolean;\n /** Set by inline `@index` or a body-level `@index(...)` naming this property. */\n index: boolean;\n /** Every annotation as written (`'@key'`, `'@embed(\"body\")'`) — unknown ones preserved verbatim. */\n annotations: readonly string[];\n}\n\nexport interface PgInterfaceType {\n name: string;\n properties: readonly PgProperty[];\n /** Body-level constraint declarations preserved verbatim (e.g. `'@unique(a, b)'`). */\n constraints: readonly string[];\n}\n\nexport interface PgNodeType {\n name: string;\n /** Interface names from the `implements` clause (already expanded into `properties`). */\n implements: readonly string[];\n properties: readonly PgProperty[];\n constraints: readonly string[];\n}\n\nexport interface PgEdgeType {\n name: string;\n /** Source (from) node type name. */\n from: string;\n /** Destination (to) node type name. */\n to: string;\n /** Raw `@card` bounds, e.g. `'1..1'`, `'0..*'`. Absent means the default `0..*`. */\n card?: string;\n /** Header annotations as written (including the `@card(...)` one, if any). */\n annotations: readonly string[];\n properties: readonly PgProperty[];\n constraints: readonly string[];\n}\n\nexport interface PgSchema {\n interfaces: readonly PgInterfaceType[];\n nodes: readonly PgNodeType[];\n edges: readonly PgEdgeType[];\n}\n\nconst SCALARS: ReadonlySet<string> = new Set<PgScalarName>([\n 'String',\n 'Blob',\n 'Bool',\n 'I32',\n 'I64',\n 'U32',\n 'U64',\n 'F32',\n 'F64',\n 'Date',\n 'DateTime',\n]);\n\n// ---------------------------------------------------------------------------\n// Lexical helpers (string-aware; `.pg` strings are double-quoted with `\\` escapes)\n// ---------------------------------------------------------------------------\n\n/** Blank out line and block comments, preserving newlines and string literals. */\nfunction stripComments(src: string): string {\n let out = '';\n let i = 0;\n const n = src.length;\n while (i < n) {\n const ch = src.charAt(i);\n if (ch === '\"') {\n out += ch;\n i++;\n while (i < n) {\n const c = src.charAt(i);\n out += c;\n i++;\n if (c === '\\\\' && i < n) {\n out += src.charAt(i);\n i++;\n } else if (c === '\"') break;\n }\n } else if (ch === '/' && src.charAt(i + 1) === '/') {\n while (i < n && src.charAt(i) !== '\\n') i++;\n } else if (ch === '/' && src.charAt(i + 1) === '*') {\n i += 2;\n while (i < n && !(src.charAt(i) === '*' && src.charAt(i + 1) === '/')) {\n out += src.charAt(i) === '\\n' ? '\\n' : ' ';\n i++;\n }\n i += 2;\n } else {\n out += ch;\n i++;\n }\n }\n return out;\n}\n\n/** Index just past the `}` matching the `{` at `openIdx`, or `src.length` if unbalanced. */\nfunction findBlockEnd(src: string, openIdx: number): number {\n let depth = 0;\n let i = openIdx;\n const n = src.length;\n while (i < n) {\n const ch = src.charAt(i);\n if (ch === '\"') {\n i++;\n while (i < n) {\n const c = src.charAt(i);\n i++;\n if (c === '\\\\') i++;\n else if (c === '\"') break;\n }\n continue;\n }\n if (ch === '{') depth++;\n else if (ch === '}') {\n depth--;\n if (depth === 0) return i + 1;\n }\n i++;\n }\n return n;\n}\n\ninterface RawAnnotation {\n name: string;\n args?: string;\n raw: string;\n}\n\nconst BODY_CONSTRAINT_NAMES: ReadonlySet<string> = new Set([\n 'key',\n 'unique',\n 'index',\n 'range',\n 'check',\n]);\n\n/** Scan a run of `@ident` / `@ident(...)` annotations starting at `start`. */\nfunction scanAnnotations(\n src: string,\n start: number,\n stopBeforeBodyConstraint = false,\n): { anns: RawAnnotation[]; end: number } {\n const anns: RawAnnotation[] = [];\n let i = start;\n const n = src.length;\n for (;;) {\n let j = i;\n while (j < n && /\\s/.test(src.charAt(j))) j++;\n if (src.charAt(j) !== '@') break;\n // `annotation` and `body_constraint` spell the sigil and name as\n // separate Pest sequence terms (`\"@\" ~ ident`), so implicit whitespace\n // is legal here too (`@ key`, `@ rename_from (...)`).\n const m = /^@\\s*([A-Za-z_]\\w*)/.exec(src.slice(j));\n if (!m || m[1] === undefined) break;\n const name = m[1];\n let end = j + m[0].length;\n let args: string | undefined;\n // Pest inserts the silent WHITESPACE rule around `~`, so all schema\n // annotations permit whitespace between their name and argument list\n // (`@card (1..1)`, `@embed (\"body\")`, ...). Keep that whitespace in the\n // preserved raw spelling while using the actual `(` as the parse cursor.\n let open = end;\n while (open < n && /\\s/.test(src.charAt(open))) open++;\n if (src.charAt(open) === '(') {\n let depth = 0;\n let k = open;\n while (k < n) {\n const ch = src.charAt(k);\n if (ch === '\"') {\n k++;\n while (k < n) {\n const c = src.charAt(k);\n k++;\n if (c === '\\\\') k++;\n else if (c === '\"') break;\n }\n continue;\n }\n if (ch === '(') depth++;\n else if (ch === ')') {\n depth--;\n if (depth === 0) {\n k++;\n break;\n }\n }\n k++;\n }\n args = src.slice(open + 1, k - 1);\n end = k;\n }\n // In a property declaration these call-shaped keywords begin the next\n // `body_constraint`; the Pest annotation rule explicitly excludes them.\n // Bare `@key`/`@unique`/`@index` remain valid property annotations.\n if (stopBeforeBodyConstraint && args !== undefined && BODY_CONSTRAINT_NAMES.has(name)) {\n break;\n }\n const entry: RawAnnotation = { name, raw: src.slice(j, end) };\n if (args !== undefined) entry.args = args;\n anns.push(entry);\n i = end;\n }\n return { anns, end: i };\n}\n\n/** Split on top-level commas (outside parens/strings), trim, drop empties. */\nfunction splitArgs(args: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let current = '';\n for (let i = 0; i < args.length; i++) {\n const ch = args.charAt(i);\n if (ch === '\"') {\n current += ch;\n i++;\n while (i < args.length) {\n const c = args.charAt(i);\n current += c;\n if (c === '\\\\') {\n i++;\n current += args.charAt(i);\n } else if (c === '\"') break;\n i++;\n }\n continue;\n }\n if (ch === '(' || ch === '[') depth++;\n else if (ch === ')' || ch === ']') depth = Math.max(0, depth - 1);\n if (ch === ',' && depth === 0) {\n out.push(current);\n current = '';\n } else {\n current += ch;\n }\n }\n out.push(current);\n return out.map((s) => s.trim()).filter((s) => s.length > 0);\n}\n\n// ---------------------------------------------------------------------------\n// Statement parsing\n// ---------------------------------------------------------------------------\n\ninterface MutableTypeBody {\n properties: PgProperty[];\n constraints: string[];\n}\n\nfunction parseTypeRef(text: string): { type: PgType; optional: boolean; end: number } {\n let i = 0;\n const n = text.length;\n while (i < n && /\\s/.test(text.charAt(i))) i++;\n let type: PgType;\n if (text.charAt(i) === '[') {\n const m = /^\\[\\s*([A-Za-z_]\\w*)\\s*\\]/.exec(text.slice(i));\n if (m && m[1] !== undefined && SCALARS.has(m[1])) {\n type = { kind: 'list', element: m[1] as PgScalarName };\n i += m[0].length;\n } else {\n const close = text.indexOf(']', i);\n const nextLine = text.indexOf('\\n', i);\n let end = close === -1 ? n : close + 1;\n if (nextLine !== -1 && nextLine < end) end = nextLine;\n type = { kind: 'unknown', raw: text.slice(i, end).trim() };\n i = end;\n }\n } else {\n const m = /^([A-Za-z_]\\w*)/.exec(text.slice(i));\n if (!m || m[1] === undefined) {\n const nextLine = text.indexOf('\\n', i);\n const end = nextLine === -1 ? n : nextLine;\n return {\n type: { kind: 'unknown', raw: text.slice(i, end).trim() },\n optional: false,\n end,\n };\n }\n const ident = m[1];\n i += m[0].length;\n // As with annotations, the server grammar's silent WHITESPACE rule makes\n // `Vector (3)` and `enum (a, b)` equivalent to their compact spellings.\n let open = i;\n while (open < n && /\\s/.test(text.charAt(open))) open++;\n if ((ident === 'enum' || ident === 'Vector') && text.charAt(open) === '(') {\n let depth = 0;\n let k = open;\n while (k < n) {\n const ch = text.charAt(k);\n if (ch === '(') depth++;\n else if (ch === ')') {\n depth--;\n if (depth === 0) {\n k++;\n break;\n }\n }\n k++;\n }\n const inner = text.slice(open + 1, k - 1);\n i = k;\n if (ident === 'enum') {\n const values = splitArgs(inner).map((v) => v.replace(/^\"(.*)\"$/s, '$1'));\n type = { kind: 'enum', values };\n } else {\n const dim = Number.parseInt(inner.trim(), 10);\n type = Number.isFinite(dim)\n ? { kind: 'vector', dim }\n : { kind: 'unknown', raw: `Vector(${inner})` };\n }\n } else if (SCALARS.has(ident)) {\n type = ident as PgScalarName;\n } else {\n type = { kind: 'unknown', raw: ident };\n }\n }\n let optional = false;\n // `type_ref = { core_type ~ \"?\"? }`: implicit whitespace also applies\n // before the optional marker (`String ?`, `[String] ?`, `Vector (3) ?`).\n while (i < n && /\\s/.test(text.charAt(i))) i++;\n if (text.charAt(i) === '?') {\n optional = true;\n i++;\n }\n return { type, optional, end: i };\n}\n\nfunction parseBody(body: string): MutableTypeBody {\n const properties: PgProperty[] = [];\n const constraints: string[] = [];\n // Pest's implicit WHITESPACE includes newlines, so a newline is not a\n // statement delimiter: `String\\n?`, `Vector\\n(3)`, and same-line adjacent\n // declarations are all legal. Consume the body as one token stream.\n let cursor = body.trimStart();\n while (cursor !== '') {\n if (cursor.startsWith('@')) {\n const { anns, end } = scanAnnotations(cursor, 0);\n if (anns.length === 0 || end === 0) break;\n for (const a of anns) constraints.push(a.raw);\n cursor = cursor.slice(end).trimStart();\n continue;\n }\n\n const head = /^([A-Za-z_]\\w*)\\s*:\\s*/.exec(cursor);\n if (!head || head[1] === undefined) {\n // Tolerant recovery: discard only the malformed physical line, then\n // resume. Valid declarations do not need newlines, but this preserves\n // the historical ability to salvage later lines after unknown syntax.\n const nextLine = cursor.indexOf('\\n');\n if (nextLine === -1) break;\n cursor = cursor.slice(nextLine + 1).trimStart();\n continue;\n }\n const name = head[1];\n const rest = cursor.slice(head[0].length);\n const { type, optional, end } = parseTypeRef(rest);\n const { anns, end: annsEnd } = scanAnnotations(rest, end, true);\n properties.push({\n name,\n type,\n optional,\n key: anns.some((a) => a.name === 'key'),\n unique: anns.some((a) => a.name === 'unique'),\n index: anns.some((a) => a.name === 'index'),\n annotations: anns.map((a) => a.raw),\n });\n cursor = rest.slice(annsEnd).trimStart();\n }\n // Body-level @key(a, b) / @unique(a) / @index(a) constraints flag the named properties.\n for (const raw of constraints) {\n const m = /^@\\s*(key|unique|index)\\s*\\((.*)\\)$/s.exec(raw);\n if (!m || m[1] === undefined || m[2] === undefined) continue;\n const flag = m[1] as 'key' | 'unique' | 'index';\n for (const propName of splitArgs(m[2])) {\n const prop = properties.find((p) => p.name === propName);\n if (prop) prop[flag] = true;\n }\n }\n return { properties, constraints };\n}\n\n// ---------------------------------------------------------------------------\n// Top-level parse\n// ---------------------------------------------------------------------------\n\n/**\n * Parse `.pg` source into a {@link PgSchema}. Tolerant by design: malformed\n * or unrecognized declarations are skipped, not fatal; `implements` clauses\n * are expanded into node properties (node-declared properties win on name\n * collisions, matching the server's table layout).\n */\nexport function parsePgSchema(source: string): PgSchema {\n const src = stripComments(source);\n const interfaces: PgInterfaceType[] = [];\n const nodes: (PgNodeType & { properties: PgProperty[] })[] = [];\n const edges: PgEdgeType[] = [];\n\n const headRe = /\\b(interface|node|edge)\\s+([A-Za-z_]\\w*)/g;\n let m: RegExpExecArray | null;\n while ((m = headRe.exec(src)) !== null) {\n const kw = m[1];\n const name = m[2];\n if (kw === undefined || name === undefined) continue;\n let pos = headRe.lastIndex;\n\n if (kw === 'edge') {\n const em = /^\\s*:\\s*([A-Za-z_]\\w*)\\s*->\\s*([A-Za-z_]\\w*)/.exec(src.slice(pos));\n if (!em || em[1] === undefined || em[2] === undefined) continue; // tolerant skip\n pos += em[0].length;\n const { anns, end } = scanAnnotations(src, pos);\n pos = end;\n let body: MutableTypeBody = { properties: [], constraints: [] };\n let braceIdx = pos;\n while (braceIdx < src.length && /\\s/.test(src.charAt(braceIdx))) braceIdx++;\n if (src.charAt(braceIdx) === '{') {\n const blockEnd = findBlockEnd(src, braceIdx);\n body = parseBody(src.slice(braceIdx + 1, blockEnd - 1));\n pos = blockEnd;\n }\n const card = anns.find((a) => a.name === 'card')?.args?.trim();\n const edge: PgEdgeType = {\n name,\n from: em[1],\n to: em[2],\n annotations: anns.map((a) => a.raw),\n properties: body.properties,\n constraints: body.constraints,\n ...(card !== undefined ? { card } : {}),\n };\n edges.push(edge);\n headRe.lastIndex = pos;\n continue;\n }\n\n // interface / node: optional `implements A, B` (node only), then a block.\n let impls: string[] = [];\n // The grammar carries head annotations (`@card`/`@unique` prove the\n // family). `node Account @rename_from(\"User\") { … }` previously hit the\n // tolerant skip and silently dropped the whole declaration.\n // Annotations are consumed (and ignored — migration metadata) on either\n // side of `implements`, through the SAME scanner the body lane uses. It\n // honors nested parens and escaped quotes; a first-`)` regex would\n // truncate `@rename_from(\"User (legacy)\")` and drop the node.\n const skipAnnotations = (): void => {\n pos = scanAnnotations(src, pos).end;\n };\n skipAnnotations();\n if (kw === 'node') {\n const im = /^\\s*implements\\s+([A-Za-z_]\\w*(?:\\s*,\\s*[A-Za-z_]\\w*)*)/.exec(src.slice(pos));\n if (im && im[1] !== undefined) {\n impls = im[1].split(',').map((s) => s.trim());\n pos += im[0].length;\n }\n skipAnnotations();\n }\n let braceIdx = pos;\n while (braceIdx < src.length && /\\s/.test(src.charAt(braceIdx))) braceIdx++;\n if (src.charAt(braceIdx) !== '{') continue; // tolerant skip\n const blockEnd = findBlockEnd(src, braceIdx);\n const body = parseBody(src.slice(braceIdx + 1, blockEnd - 1));\n headRe.lastIndex = blockEnd;\n if (kw === 'interface') {\n interfaces.push({ name, properties: body.properties, constraints: body.constraints });\n } else {\n nodes.push({\n name,\n implements: impls,\n properties: body.properties,\n constraints: body.constraints,\n });\n }\n }\n\n // Expand implements clauses: interface properties precede the node's own,\n // except where the node re-declares a name (node declaration wins).\n for (const node of nodes) {\n if (node.implements.length === 0) continue;\n const own = new Set(node.properties.map((p) => p.name));\n const inherited: PgProperty[] = [];\n for (const ifaceName of node.implements) {\n const iface = interfaces.find((i) => i.name === ifaceName);\n if (!iface) continue; // tolerant: unknown interface\n for (const p of iface.properties) {\n if (!own.has(p.name) && !inherited.some((q) => q.name === p.name)) inherited.push(p);\n }\n }\n node.properties = [...inherited, ...node.properties];\n }\n\n return { interfaces, nodes, edges };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve an edge type's declared endpoint node types. Export edge\n * lines carry bare `from`/`to` ids, so endpoint types come from here. Edge\n * names match exactly first, then case-insensitively (the server matches edge\n * names case-insensitively). Returns `null` for an unknown edge name.\n */\nexport function edgeEndpointTypes(\n schema: PgSchema,\n edgeName: string,\n): { from: string; to: string } | null {\n const lower = edgeName.toLowerCase();\n const edge =\n schema.edges.find((e) => e.name === edgeName) ??\n schema.edges.find((e) => e.name.toLowerCase() === lower);\n return edge ? { from: edge.from, to: edge.to } : null;\n}\n\n/**\n * Stable fingerprint of `.pg` source for the adapter revision stamp: FNV-1a\n * 64-bit over the UTF-8 bytes of the source, as 16 lowercase hex chars.\n *\n * Pure and browser-safe (no `node:crypto`). Hashes the source **verbatim**:\n * any textual change — including comments or whitespace — changes the\n * fingerprint, which is the conservative choice for drift detection.\n */\nexport function schemaFingerprint(source: string): string {\n const MASK64 = 0xffffffffffffffffn;\n let h = 0xcbf29ce484222325n;\n const bytes = new TextEncoder().encode(source);\n for (const b of bytes) {\n h ^= BigInt(b);\n h = (h * 0x100000001b3n) & MASK64;\n }\n return h.toString(16).padStart(16, '0');\n}\n\nexport interface BigIntKeyWarning {\n /** Node or edge type name. */\n type: string;\n /** The hazardous property. */\n property: string;\n}\n\n/**\n * Find `I64`/`U64` properties used as schema keys (`@key`). The\n * HTTP path emits native JSON numbers even past ±2^53, where `JSON.parse`\n * silently rounds them. Physical entity ids are separate strings and stay\n * exact; warn that key attributes themselves may lose precision.\n */\nexport function bigIntKeyWarnings(schema: PgSchema): BigIntKeyWarning[] {\n const out: BigIntKeyWarning[] = [];\n for (const t of [...schema.nodes, ...schema.edges]) {\n for (const p of t.properties) {\n if ((p.type === 'I64' || p.type === 'U64') && p.key) {\n out.push({ type: t.name, property: p.name });\n }\n }\n }\n return out;\n}\n","/**\n * Export-line classification and normalization.\n *\n * `/export` streams NDJSON — one JSON object per line, `data` passed through\n * **verbatim** by the SDK. This module turns those lines into orbit\n * `GraphNode`/`GraphEdge` values:\n *\n * - ids are namespaced through the adapter identity codec (`encodeSourceId`),\n * - the node/edge KIND is injected as {@link ORBIT_TYPE_KEY} — namespaced so\n * a schema's own `type` property passes through as ordinary data,\n * - edge endpoint types are resolved from the `.pg` schema because edge lines\n * carry bare `from`/`to` ids,\n * - attr values are normalized for generated types and temporal dimensions:\n * * `Date` — export sends `'YYYY-MM-DD'`; signed years use ISO's six digits\n * * `DateTime` — export sends a UTC string without a timezone suffix →\n * ISO 8601 with explicit UTC (`'YYYY-MM-DDTHH:MM:SS.mmmZ'`)\n * * `Blob` — inline internal blobs arrive as `'base64:<data>'` →\n * `'data:application/octet-stream;base64,<data>'`;\n * external-URI refs pass through verbatim\n * * missing nullable schema properties — explicit `null`\n * * everything else — verbatim. Physical identity is top-level `id`;\n * `data.id`, when present, is an ordinary user property.\n *\n * The server rejects non-finite stored floats during export. Computed\n * non-finite query values render as null; neither path uses string sentinels.\n */\n\nimport type { GraphEdge, GraphNode } from '@modernrelay/orbit-core';\nimport { encodeSourceId } from './idCodec';\nimport {\n edgeEndpointTypes,\n type PgEdgeType,\n type PgNodeType,\n type PgProperty,\n type PgSchema,\n type PgType,\n} from './pgSchema';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/** An edge line names an edge type the `.pg` schema does not declare. */\nexport class UnknownEdgeTypeError extends Error {\n override readonly name = 'UnknownEdgeTypeError';\n readonly edgeName: string;\n constructor(edgeName: string) {\n super(\n `Unknown edge type '${edgeName}': not declared in the graph schema, so its ` +\n `endpoint node types cannot be resolved.`,\n );\n this.edgeName = edgeName;\n }\n}\n\n/** An export line is structurally unusable (e.g. missing/non-string `id`). */\nexport class InvalidExportLineError extends Error {\n override readonly name = 'InvalidExportLineError';\n constructor(message: string) {\n super(message);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Classification\n// ---------------------------------------------------------------------------\n\nexport interface NodeExportLine {\n kind: 'node';\n type: string;\n id: string;\n data: Record<string, unknown>;\n}\n\nexport interface EdgeExportLine {\n kind: 'edge';\n edge: string;\n id: string;\n from: string;\n to: string;\n data: Record<string, unknown>;\n}\n\nexport type ClassifiedExportLine = NodeExportLine | EdgeExportLine | { kind: 'unknown' };\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Classify one parsed export line using the adapter's wire shapes:\n * - node line: `{\"type\": \"<NodeType>\", \"id\": <id>, \"data\": {…props}}`\n * - edge line: `{\"edge\": \"<EdgeName>\", \"id\": <id>, \"from\": <src id>, \"to\": <dst id>, \"data\": {…props}}`\n * Unknown envelopes return `{ kind: 'unknown' }`. Recognized entities without\n * a physical identity reject instead of falling back to a user property.\n */\nexport function classifyExportLine(line: unknown): ClassifiedExportLine {\n if (!isRecord(line) || !isRecord(line['data'])) return { kind: 'unknown' };\n const data = line['data'];\n if (\n typeof line['edge'] === 'string' &&\n typeof line['from'] === 'string' &&\n typeof line['to'] === 'string'\n ) {\n return {\n kind: 'edge', edge: line['edge'], id: requireStringId(line, `Edge '${line['edge']}'`),\n from: line['from'], to: line['to'], data,\n };\n }\n if (typeof line['type'] === 'string') {\n return { kind: 'node', type: line['type'], id: requireStringId(line, `Node '${line['type']}'`), data };\n }\n return { kind: 'unknown' };\n}\n\n// ---------------------------------------------------------------------------\n// Value normalization\n// ---------------------------------------------------------------------------\n\n/** Chrono uses -0001/+10000; ECMAScript ISO dates require -000001/+010000. */\nfunction isoYear(wireYear: string): string {\n const year = Number(wireYear);\n return year >= 0 && year <= 9999\n ? String(year).padStart(4, '0')\n : `${year < 0 ? '-' : '+'}${String(Math.abs(year)).padStart(6, '0')}`;\n}\n\nfunction normalizeValue(type: PgType | undefined, value: unknown): unknown {\n if (type === undefined || value === null || value === undefined) return value;\n if (typeof type === 'object' && type.kind === 'list' && Array.isArray(value)) {\n return value.map((item) => normalizeValue(type.element, item));\n }\n if (type === 'Date') {\n const date = typeof value === 'string' ? /^([+-]?\\d{4,})(-\\d{2}-\\d{2})$/.exec(value) : null;\n if (date === null) {\n throw new InvalidExportLineError('Date export values must be calendar-date strings');\n }\n return `${isoYear(date[1]!)}${date[2]}`;\n }\n if (type === 'DateTime') {\n // Arrow's timestamp spelling is UTC despite omitting the zone. Letting\n // Date.parse consume it unchanged would use the consumer's local zone.\n const timestamp = typeof value === 'string'\n ? /^([+-]?\\d{4,})(-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})(?:\\.(\\d{1,3}))?$/.exec(value)\n : null;\n if (timestamp === null) {\n throw new InvalidExportLineError('DateTime export values must be UTC timestamp strings without a timezone suffix');\n }\n return `${isoYear(timestamp[1]!)}${timestamp[2]}.${(timestamp[3] ?? '').padEnd(3, '0')}Z`;\n }\n if (type === 'Blob' && typeof value === 'string' && value.startsWith('base64:')) {\n return `data:application/octet-stream;base64,${value.slice('base64:'.length)}`;\n }\n return value;\n}\n\nfunction propMap(t: PgNodeType | PgEdgeType | undefined): ReadonlyMap<string, PgProperty> {\n const map = new Map<string, PgProperty>();\n if (t) for (const p of t.properties) map.set(p.name, p);\n return map;\n}\n\nfunction normalizeData(\n data: Record<string, unknown>,\n properties: ReadonlyMap<string, PgProperty>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n // Source data can carry '__proto__' as an own key (JSON.parse creates it);\n // defineProperty writes the own property, never the setter.\n for (const [k, v] of Object.entries(data)) {\n Object.defineProperty(out, k, {\n value: normalizeValue(properties.get(k)?.type, v),\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n for (const property of properties.values()) {\n if (property.optional && !Object.prototype.hasOwnProperty.call(data, property.name)) {\n Object.defineProperty(out, property.name, { value: null, enumerable: true, writable: true, configurable: true });\n }\n }\n return out;\n}\n\nfunction requireStringId(line: { id?: unknown }, what: string): string {\n const id = line.id;\n if (typeof id !== 'string') {\n throw new InvalidExportLineError(\n `${what} export line has ${id === undefined ? 'no' : 'a non-string'} top-level 'id' — ` +\n `cannot build a stable orbit id; id must be a string, separate from user properties.`,\n );\n }\n return id;\n}\n\n// ---------------------------------------------------------------------------\n// Node / edge normalization\n// ---------------------------------------------------------------------------\n\n/**\n * The adapter's node/edge KIND discriminator key.\n *\n * Namespaced on purpose: the adapter injects this field into someone else's\n * data, so it takes the awkward key and leaves the generic word `type` to the\n * schema author, who means something real by it (`Company.type = investor`).\n * A colon is illegal in `.pg` identifiers, so no schema property can ever\n * claim this key — the collision class is closed by construction, and a\n * source-declared `type` now flows through untouched with no preservation\n * mechanism, no warning, and no schema migration. Same convention as\n * GraphQL's `__typename` / JSON-LD's `@type`.\n */\nexport const ORBIT_TYPE_KEY = 'orbit:type';\n\n/**\n * Normalize a node export line to a `GraphNode`:\n * `id = encodeSourceId(type, id)`, `attrs = { …data, 'orbit:type' }`\n * with value normalization driven by the schema's property types. A node type\n * absent from the schema is tolerated — its attrs pass through verbatim\n * (identity never needs the schema on the node path).\n */\nexport function normalizeNode(\n line: { type: string; id: string; data: Record<string, unknown> },\n schema: PgSchema,\n): GraphNode {\n const nodeType = schema.nodes.find((n) => n.name === line.type);\n const id = encodeSourceId(line.type, requireStringId(line, `Node '${line.type}'`));\n return {\n id,\n // The discriminator is adapter-owned identity metadata. It lands LAST so\n // a forward-compatible export field literally named `orbit:type` cannot\n // forge the value the generated attrs unions promise.\n attrs: { ...normalizeData(line.data, propMap(nodeType)), [ORBIT_TYPE_KEY]: line.type },\n };\n}\n\n/**\n * Normalize an edge export line to a `GraphEdge`:\n * `id = encodeSourceId(edge, id)`; `source`/`target` namespace the bare\n * `from`/`to` ids with the endpoint node types resolved from the schema.\n * Throws {@link UnknownEdgeTypeError} when the schema does not declare\n * the edge — endpoint identity cannot be constructed without it.\n */\nexport function normalizeEdge(\n line: { edge: string; id: string; from: string; to: string; data: Record<string, unknown> },\n schema: PgSchema,\n): GraphEdge {\n const endpoints = edgeEndpointTypes(schema, line.edge);\n if (endpoints === null) throw new UnknownEdgeTypeError(line.edge);\n const lower = line.edge.toLowerCase();\n const edgeType =\n schema.edges.find((e) => e.name === line.edge) ??\n schema.edges.find((e) => e.name.toLowerCase() === lower);\n const id = encodeSourceId(line.edge, requireStringId(line, `Edge '${line.edge}'`));\n return {\n id,\n source: encodeSourceId(endpoints.from, line.from),\n target: encodeSourceId(endpoints.to, line.to),\n // As on nodes, the adapter's resolved edge name lands last and always\n // wins the discriminator key; a source `type` property is ordinary data.\n attrs: { ...normalizeData(line.data, propMap(edgeType)), [ORBIT_TYPE_KEY]: line.edge },\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { parsePgSchema, schemaFingerprint, ORBIT_TYPE_KEY } from './chunk-WMBDYPK2.js';
2
+ import { parsePgSchema, schemaFingerprint, ORBIT_TYPE_KEY } from './chunk-TFBKN675.js';
3
3
  import { readFileSync, writeFileSync } from 'fs';
4
4
  import process from 'process';
5
5
  import { Omnigraph } from '@modernrelay/omnigraph';
@@ -29,7 +29,7 @@ function tsType(type) {
29
29
  case "enum":
30
30
  return type.values.length === 0 ? "string" : type.values.map(tsStringLiteral).join(" | ");
31
31
  case "list":
32
- return `${SCALAR_TS[type.element]}[]`;
32
+ return `(${SCALAR_TS[type.element]} | null)[]`;
33
33
  case "unknown":
34
34
  return "unknown";
35
35
  }
@@ -71,21 +71,12 @@ function emitProperty(lines, prop) {
71
71
  }
72
72
  function emitPropsInterface(lines, interfaceName, kindLabel, typeName, properties) {
73
73
  lines.push(`/** Normalized attrs for ${kindLabel} type \`${typeName}\`. */`);
74
- lines.push(`export interface ${interfaceName} {`);
75
- if (!properties.some((p) => p.name === "id")) {
76
- lines.push(
77
- ` /** Physical Omnigraph id \u2014 injected into every export line's \`data\`; unique per ${kindLabel} type only. */`
78
- );
79
- lines.push(" id: string;");
74
+ if (properties.length === 0) {
75
+ lines.push(`export type ${interfaceName} = object;`, "");
76
+ return;
80
77
  }
78
+ lines.push(`export interface ${interfaceName} {`);
81
79
  for (const prop of properties) {
82
- if (prop.name === "id") {
83
- lines.push(
84
- ` /** Physical Omnigraph id \u2014 normalized as a required string regardless of its schema wire type. */`
85
- );
86
- lines.push(" id: string;");
87
- continue;
88
- }
89
80
  emitProperty(lines, prop);
90
81
  }
91
82
  lines.push("}");
@@ -121,10 +112,11 @@ function headerBanner(fingerprintLine, extra) {
121
112
  }
122
113
  lines.push(
123
114
  "//",
124
- "// Shapes describe attrs AFTER the adapter's wire normalization (both read",
125
- "// paths converge on the query-path string encodings):",
126
- "// Date \u2192 'YYYY-MM-DD' \xB7 DateTime \u2192 ISO 8601 \xB7 enum(...) \u2192 literal union",
115
+ "// Shapes describe export-loaded attrs AFTER the adapter's wire normalization:",
116
+ "// Physical entity ids live on GraphNode/GraphEdge, outside attrs.",
117
+ "// Date \u2192 'YYYY-MM-DD' \xB7 DateTime \u2192 ISO 8601 UTC \xB7 enum(...) \u2192 literal union",
127
118
  "// I64/U64/F32/F64 \u2192 number \xB7 Vector(n) \u2192 number[] \xB7 T? \u2192 T | null",
119
+ "// [T] \u2192 (T | null)[] (null list elements are retained)",
128
120
  "// Blob \u2192 string ('data:' URI for inline blobs, stored URI for external)",
129
121
  ""
130
122
  );
@@ -133,13 +125,34 @@ function headerBanner(fingerprintLine, extra) {
133
125
  function generateTypes(schema, opts) {
134
126
  const fingerprintLine = opts?.fingerprint !== void 0 ? `Schema fingerprint: ${opts.fingerprint}.` : `Schema fingerprint: ${schemaFingerprint(JSON.stringify(schema))} (parsed model \u2014 regenerate from .pg source for the source fingerprint).`;
135
127
  const lines = headerBanner(fingerprintLine, opts?.header);
128
+ const reserved = /* @__PURE__ */ new Set([
129
+ "NodeAttrs",
130
+ "EdgeAttrs",
131
+ "TypeMap",
132
+ "NodeTypeName",
133
+ "EdgeTypeName",
134
+ ...schema.nodes.map((n) => `${n.name}Props`),
135
+ ...schema.edges.map((e) => `${e.name}EdgeProps`)
136
+ ]);
137
+ const used = /* @__PURE__ */ new Set();
138
+ function allocateName(preferred) {
139
+ let name = preferred;
140
+ let suffix = 2;
141
+ while (used.has(name)) {
142
+ do {
143
+ name = `${preferred}_${suffix++}`;
144
+ } while (reserved.has(name));
145
+ }
146
+ used.add(name);
147
+ return name;
148
+ }
136
149
  const nodeMembers = schema.nodes.map((n) => ({
137
150
  typeName: n.name,
138
- interfaceName: `${n.name}Props`
151
+ interfaceName: allocateName(`${n.name}Props`)
139
152
  }));
140
153
  const edgeMembers = schema.edges.map((e) => ({
141
154
  typeName: e.name,
142
- interfaceName: `${e.name}EdgeProps`
155
+ interfaceName: allocateName(`${e.name}EdgeProps`)
143
156
  }));
144
157
  schema.nodes.forEach((n, i) => {
145
158
  const member = nodeMembers[i];