@modernrelay/orbit-omnigraph 0.2.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.
@@ -0,0 +1,540 @@
1
+ import { GraphEdge, GraphNode, BeginIngestOptions, IngestSession, Revisions, GraphDiagnostic, SearchResult, SearchService } from '@modernrelay/orbit-core';
2
+ import { Omnigraph } from '@modernrelay/omnigraph';
3
+
4
+ /**
5
+ * Identity codec (spec Appendix B.3 / B.4).
6
+ *
7
+ * Omnigraph node ids are unique **per type only** (derived from the `@key`
8
+ * tuple within each type's table), so unqualified ids are unsound as orbit
9
+ * `NodeId`s. Every adapter path — nodes, edges, search results, view state,
10
+ * services — MUST qualify ids through this one collision-proof tuple codec.
11
+ * `JSON.stringify` of a fixed-arity string tuple is injective over its inputs
12
+ * (quotes, brackets, commas, and unicode in either component are escaped), so
13
+ * no `(kind, sourceId)` pair can collide with a different pair.
14
+ *
15
+ * Human-readable labels stay separate from identity; there is deliberately no
16
+ * `namespaceIds:false` escape hatch (B.3).
17
+ */
18
+ interface DecodedSourceId {
19
+ /** The namespace component: a node type name or an edge type name. */
20
+ kind: string;
21
+ /** The physical Omnigraph id within that type's table. */
22
+ sourceId: string;
23
+ }
24
+ /**
25
+ * Encode a `(kind, sourceId)` pair as a collision-proof orbit id.
26
+ *
27
+ * - Nodes: `encodeSourceId(NodeType, data.id)`
28
+ * - Edges: `encodeSourceId(EdgeName, data.id)`; endpoints use
29
+ * `encodeSourceId(endpointType, from|to)` (B.3).
30
+ */
31
+ declare function encodeSourceId(kind: string, sourceId: string): string;
32
+ /**
33
+ * Decode an id produced by {@link encodeSourceId}.
34
+ *
35
+ * Returns `null` for anything that does not conform exactly (non-JSON input,
36
+ * non-array JSON, wrong arity — including synthetic edge ids, which are
37
+ * 4-tuples — or non-string elements). Never throws.
38
+ */
39
+ declare function decodeSourceId(id: string): DecodedSourceId | null;
40
+ /**
41
+ * Synthetic edge id for **query-derived** subgraphs (spec §5 rule, B.4).
42
+ *
43
+ * The GQ grammar has no edge variable, so the query path can never surface
44
+ * physical edge ids; query-derived edges are identified by
45
+ * `['synthetic-edge', EdgeName, source, target]` where `source`/`target` are
46
+ * the already-encoded endpoint node ids.
47
+ *
48
+ * Exported for the future query path (B.5 expansion recipe — post-v1).
49
+ * Caveats (B.4): parallel edges collapse under this scheme, and a dataset
50
+ * MUST NOT mix synthetic ids with physical `/export` ids for the same edges —
51
+ * one scheme per dataset, never both. Because a synthetic id is a 4-tuple,
52
+ * {@link decodeSourceId} rejects it (`null`), keeping the two schemes
53
+ * mechanically un-confusable.
54
+ */
55
+ declare function encodeSyntheticEdgeId(edgeName: string, source: string, target: string): string;
56
+
57
+ /**
58
+ * `.pg` schema model + tolerant parser (spec Appendix B.3 / B.6).
59
+ *
60
+ * The adapter needs three things from the graph schema, all served here:
61
+ * 1. edge endpoint-type resolution (B.3 — export edge lines carry bare
62
+ * `from`/`to` ids without endpoint types),
63
+ * 2. per-property wire-type knowledge for temporal/blob normalization (B.6),
64
+ * 3. a stable schema fingerprint for the export revision stamp (B.2).
65
+ *
66
+ * The parser is deliberately tolerant: it extracts the declarations it
67
+ * understands (interfaces, node blocks, edge declarations, properties,
68
+ * constraints) and preserves anything else — unknown annotations survive as
69
+ * raw strings, unknown type spellings become `{ kind: 'unknown' }` — so a
70
+ * newer server grammar degrades gracefully instead of failing the load.
71
+ *
72
+ * Browser-safe: no `node:` imports. The fingerprint is a pure FNV-1a 64-bit
73
+ * hash rather than `node:crypto` sha256 so this module can sit on the
74
+ * browser entry's critical path.
75
+ */
76
+ /** Closed scalar set per the `.pg` grammar (docs/user/schema). */
77
+ type PgScalarName = 'String' | 'Blob' | 'Bool' | 'I32' | 'I64' | 'U32' | 'U64' | 'F32' | 'F64' | 'Date' | 'DateTime';
78
+ type PgType = PgScalarName | {
79
+ kind: 'vector';
80
+ dim: number;
81
+ } | {
82
+ kind: 'enum';
83
+ values: readonly string[];
84
+ } | {
85
+ kind: 'list';
86
+ element: PgScalarName;
87
+ } | {
88
+ kind: 'unknown';
89
+ raw: string;
90
+ };
91
+ interface PgProperty {
92
+ name: string;
93
+ type: PgType;
94
+ /** `T?` nullability. */
95
+ optional: boolean;
96
+ /** Set by inline `@key` or a body-level `@key(...)` naming this property. */
97
+ key: boolean;
98
+ /** Set by inline `@unique` or a body-level `@unique(...)` naming this property. */
99
+ unique: boolean;
100
+ /** Set by inline `@index` or a body-level `@index(...)` naming this property. */
101
+ index: boolean;
102
+ /** Every annotation as written (`'@key'`, `'@embed("body")'`) — unknown ones preserved verbatim. */
103
+ annotations: readonly string[];
104
+ }
105
+ interface PgInterfaceType {
106
+ name: string;
107
+ properties: readonly PgProperty[];
108
+ /** Body-level constraint declarations preserved verbatim (e.g. `'@unique(a, b)'`). */
109
+ constraints: readonly string[];
110
+ }
111
+ interface PgNodeType {
112
+ name: string;
113
+ /** Interface names from the `implements` clause (already expanded into `properties`). */
114
+ implements: readonly string[];
115
+ properties: readonly PgProperty[];
116
+ constraints: readonly string[];
117
+ }
118
+ interface PgEdgeType {
119
+ name: string;
120
+ /** Source (from) node type name. */
121
+ from: string;
122
+ /** Destination (to) node type name. */
123
+ to: string;
124
+ /** Raw `@card` bounds, e.g. `'1..1'`, `'0..*'`. Absent means the default `0..*`. */
125
+ card?: string;
126
+ /** Header annotations as written (including the `@card(...)` one, if any). */
127
+ annotations: readonly string[];
128
+ properties: readonly PgProperty[];
129
+ constraints: readonly string[];
130
+ }
131
+ interface PgSchema {
132
+ interfaces: readonly PgInterfaceType[];
133
+ nodes: readonly PgNodeType[];
134
+ edges: readonly PgEdgeType[];
135
+ }
136
+ /**
137
+ * Parse `.pg` source into a {@link PgSchema}. Tolerant by design: malformed
138
+ * or unrecognized declarations are skipped, not fatal; `implements` clauses
139
+ * are expanded into node properties (node-declared properties win on name
140
+ * collisions, matching the server's table layout).
141
+ */
142
+ declare function parsePgSchema(source: string): PgSchema;
143
+ /**
144
+ * Resolve an edge type's declared endpoint node types (B.3). Export edge
145
+ * lines carry bare `from`/`to` ids, so endpoint types come from here. Edge
146
+ * names match exactly first, then case-insensitively (the server matches edge
147
+ * names case-insensitively). Returns `null` for an unknown edge name.
148
+ */
149
+ declare function edgeEndpointTypes(schema: PgSchema, edgeName: string): {
150
+ from: string;
151
+ to: string;
152
+ } | null;
153
+ /**
154
+ * Stable fingerprint of `.pg` source for the B.2 revision stamp: FNV-1a
155
+ * 64-bit over the UTF-8 bytes of the source, as 16 lowercase hex chars.
156
+ *
157
+ * Pure and browser-safe (no `node:crypto`). Hashes the source **verbatim**:
158
+ * any textual change — including comments or whitespace — changes the
159
+ * fingerprint, which is the conservative choice for drift detection.
160
+ */
161
+ declare function schemaFingerprint(source: string): string;
162
+ interface BigIntKeyWarning {
163
+ /** Node or edge type name. */
164
+ type: string;
165
+ /** The hazardous property. */
166
+ property: string;
167
+ }
168
+ /**
169
+ * B.6 hazard scan: `I64`/`U64` properties used as identity (`@key` or named
170
+ * `id`). The HTTP path emits native JSON numbers even past ±2^53 — silently
171
+ * rounded by `JSON.parse`, unrescued by the SDK — so two distinct big-int ids
172
+ * can collapse after rounding, violating §5. Surface these before loading.
173
+ */
174
+ declare function bigIntKeyWarnings(schema: PgSchema): BigIntKeyWarning[];
175
+
176
+ /**
177
+ * Export-line classification and normalization (spec Appendix B.2 / B.3 / B.6).
178
+ *
179
+ * `/export` streams NDJSON — one JSON object per line, `data` passed through
180
+ * **verbatim** by the SDK. This module turns those lines into orbit
181
+ * `GraphNode`/`GraphEdge` values:
182
+ *
183
+ * - ids are namespaced through the B.3 codec (`encodeSourceId`),
184
+ * - the node/edge KIND is injected as {@link ORBIT_TYPE_KEY} — namespaced so
185
+ * a schema's own `type` property passes through as ordinary data (B.6),
186
+ * - edge endpoint types are resolved from the `.pg` schema (edge lines carry
187
+ * bare `from`/`to` ids — B.3),
188
+ * - attr values are normalized to the query-path string forms (B.6) so
189
+ * generated types and §16.6 temporal dimensions see one encoding
190
+ * regardless of read path:
191
+ * * `Date` — export sends **days since Unix epoch** (number) →
192
+ * `'YYYY-MM-DD'` (UTC)
193
+ * * `DateTime` — export sends **epoch milliseconds** (number) →
194
+ * ISO 8601 (`'YYYY-MM-DDTHH:MM:SS.mmmZ'`)
195
+ * * `Blob` — inline internal blobs arrive as `'base64:<data>'` →
196
+ * `'data:application/octet-stream;base64,<data>'`;
197
+ * external-URI refs pass through verbatim (B.10)
198
+ * * everything else — verbatim.
199
+ *
200
+ * Non-finite float sentinels (`'NaN'`/`'Infinity'`/`'-Infinity'`) are a
201
+ * **query-path-only** encoding: a non-finite stored float aborts `/export`
202
+ * server-side, so export-loaded data never contains the sentinels (B.6).
203
+ * This module therefore does NOT special-case them.
204
+ */
205
+
206
+ /** An edge line names an edge type the `.pg` schema does not declare (B.3). */
207
+ declare class UnknownEdgeTypeError extends Error {
208
+ readonly name = "UnknownEdgeTypeError";
209
+ readonly edgeName: string;
210
+ constructor(edgeName: string);
211
+ }
212
+ /** An export line is structurally unusable (e.g. missing/non-string `data.id`). */
213
+ declare class InvalidExportLineError extends Error {
214
+ readonly name = "InvalidExportLineError";
215
+ constructor(message: string);
216
+ }
217
+ interface NodeExportLine {
218
+ kind: 'node';
219
+ type: string;
220
+ data: Record<string, unknown>;
221
+ }
222
+ interface EdgeExportLine {
223
+ kind: 'edge';
224
+ edge: string;
225
+ from: string;
226
+ to: string;
227
+ data: Record<string, unknown>;
228
+ }
229
+ type ClassifiedExportLine = NodeExportLine | EdgeExportLine | {
230
+ kind: 'unknown';
231
+ };
232
+ /**
233
+ * Classify one parsed export line per the B.2 shapes:
234
+ * - node line: `{"type": "<NodeType>", "data": {"id", …props}}`
235
+ * - edge line: `{"edge": "<EdgeName>", "from": <src id>, "to": <dst id>, "data": {"id", …props}}`
236
+ * Anything else is `{ kind: 'unknown' }`.
237
+ */
238
+ declare function classifyExportLine(line: unknown): ClassifiedExportLine;
239
+ /**
240
+ * The adapter's node/edge KIND discriminator key (B.3/B.6).
241
+ *
242
+ * Namespaced on purpose: the adapter injects this field into someone else's
243
+ * data, so it takes the awkward key and leaves the generic word `type` to the
244
+ * schema author, who means something real by it (`Company.type = investor`).
245
+ * A colon is illegal in `.pg` identifiers, so no schema property can ever
246
+ * claim this key — the collision class is closed by construction, and a
247
+ * source-declared `type` now flows through untouched with no preservation
248
+ * mechanism, no warning, and no schema migration. Same convention as
249
+ * GraphQL's `__typename` / JSON-LD's `@type`.
250
+ */
251
+ declare const ORBIT_TYPE_KEY = "orbit:type";
252
+ /**
253
+ * Normalize a node export line to a `GraphNode`:
254
+ * `id = encodeSourceId(type, data.id)`, `attrs = { …data, 'orbit:type' }`
255
+ * with B.6 value normalization driven by the schema's property types. A node
256
+ * type absent from the schema is tolerated — its attrs pass through verbatim
257
+ * (identity never needs the schema on the node path).
258
+ */
259
+ declare function normalizeNode(line: {
260
+ type: string;
261
+ data: Record<string, unknown>;
262
+ }, schema: PgSchema): GraphNode;
263
+ /**
264
+ * Normalize an edge export line to a `GraphEdge`:
265
+ * `id = encodeSourceId(edge, data.id)`; `source`/`target` namespace the bare
266
+ * `from`/`to` ids with the endpoint node types resolved from the schema
267
+ * (B.3). Throws {@link UnknownEdgeTypeError} when the schema does not declare
268
+ * the edge — endpoint identity cannot be constructed without it.
269
+ */
270
+ declare function normalizeEdge(line: {
271
+ edge: string;
272
+ from: string;
273
+ to: string;
274
+ data: Record<string, unknown>;
275
+ }, schema: PgSchema): GraphEdge;
276
+
277
+ /**
278
+ * Public types for the v1 export loader (spec Appendix B.1/B.2/B.8/B.10).
279
+ *
280
+ * The organizing rule of the v1 adapter (B.10): graphs load via `og.export()`;
281
+ * queries only ever resolve ids. These types describe that one load path —
282
+ * its options, its ingestion target seam, and its revision-stamped result.
283
+ */
284
+
285
+ /** Cumulative progress, reported after every appended batch (B.2). */
286
+ interface OmnigraphLoadProgress {
287
+ /** Export lines consumed so far (nodes + edges + skipped unknowns). */
288
+ lines: number;
289
+ /** Node lines normalized so far. */
290
+ nodes: number;
291
+ /** Edge lines normalized so far. */
292
+ edges: number;
293
+ /** Serialized UTF-8 NDJSON bytes consumed so far (including line breaks). */
294
+ bytes: number;
295
+ }
296
+ /**
297
+ * B.2 revision-stamp drift policy: what to do when the branch head observed
298
+ * after the export stream differs from the head observed before it.
299
+ *
300
+ * - `'reject'` (default): abort the session and throw — the graph is left
301
+ * untouched. The right choice for durable/shareable sessions.
302
+ * - `'accept-warn'`: buffer the export, then commit under the canonical final
303
+ * revision once `headAfter` is known; both heads are recorded in `dataRef`
304
+ * and a warning is added when they differ.
305
+ * - `'retry-once'`: abort, restart the whole load once; a second drift
306
+ * rejects.
307
+ */
308
+ type OmnigraphDriftPolicy = 'reject' | 'accept-warn' | 'retry-once';
309
+ interface OmnigraphSourceOptions {
310
+ /**
311
+ * A **preconfigured** SDK client. In the browser this must be a safe
312
+ * same-origin or public client — this option surface deliberately accepts
313
+ * no `baseUrl`/`token` pair (B.1): authenticated client construction lives
314
+ * ONLY in `@modernrelay/orbit-omnigraph/server`
315
+ * (`createOmnigraphServerClient`), which is excluded from client bundles.
316
+ */
317
+ client: Omnigraph;
318
+ /** Cluster graph id; every call is scoped to it via `client.graph(graphId)`. */
319
+ graphId: string;
320
+ /** Branch to export (B.2 — export is branch-only). Default `'main'`. */
321
+ branch?: string;
322
+ /**
323
+ * Partial per-type load (B.10): forwarded as the SDK `ExportInput.typeNames`
324
+ * field (wire form `type_names`). Omit to export every table.
325
+ */
326
+ typeNames?: readonly string[];
327
+ /** Export lines per `IngestBatch` append. Default 2000. */
328
+ batchSize?: number;
329
+ /**
330
+ * Whole-load byte budget for the atomic replace. Because atomic staging
331
+ * cannot drain before commit, exceeding this finite cap aborts the load
332
+ * without publishing a partial graph. Must be a positive safe integer.
333
+ * Default 64 MiB.
334
+ */
335
+ maxPendingBytes?: number;
336
+ /** B.2 drift policy. Default `'reject'`. */
337
+ driftPolicy?: OmnigraphDriftPolicy;
338
+ /** Called after every appended batch with cumulative counts. */
339
+ onProgress?: (p: OmnigraphLoadProgress) => void;
340
+ }
341
+ /**
342
+ * B.8 view-state `dataRef` (v1: branch-based). The readable object behind the
343
+ * canonical `sourceRevision` hash; retained so hosts can display/persist the
344
+ * exact coordinates a session was loaded from. `headBefore !== headAfter`
345
+ * only ever appears under `driftPolicy: 'accept-warn'`.
346
+ */
347
+ interface OmnigraphDataRef {
348
+ graphId: string;
349
+ branch: string;
350
+ /** Branch head commit id captured before the export stream. */
351
+ headBefore: string;
352
+ /** Branch head commit id captured after the stream, before commit. */
353
+ headAfter: string;
354
+ /** Fingerprint of the `.pg` schema source (see `schemaFingerprint`). */
355
+ schemaFingerprint: string;
356
+ }
357
+ interface OmnigraphLoadCounts {
358
+ lines: number;
359
+ nodes: number;
360
+ edges: number;
361
+ /** Serialized UTF-8 NDJSON bytes streamed (including line breaks). */
362
+ bytes: number;
363
+ }
364
+ interface OmnigraphLoadResult {
365
+ /**
366
+ * The committed source coordinate: the canonical hash of `dataRef` (B.2).
367
+ * Stable across identical replays — a second load of a quiescent branch
368
+ * commits idempotently (§5: same `{datasetKey, sourceRevision}` publishes
369
+ * nothing).
370
+ */
371
+ sourceRevision: string;
372
+ dataRef: OmnigraphDataRef;
373
+ counts: OmnigraphLoadCounts;
374
+ /** `og.health().version` — the server build the load ran against. */
375
+ serverVersion: string;
376
+ /**
377
+ * Non-fatal observations: SDK/server major.minor mismatch (B.1), accepted
378
+ * drift (B.2), B.6 big-int identity hazards, skipped unknown lines. Never
379
+ * hides a retry or accepted-drift decision.
380
+ */
381
+ warnings: string[];
382
+ }
383
+ /**
384
+ * Minimal structural ingestion target (§7.5) — deliberately decoupled from
385
+ * `GraphInstance`. The loader needs exactly the ingest seam and nothing else,
386
+ * so any object with these members works: a real `GraphInstance` (which
387
+ * satisfies this interface structurally), a recorder, or a headless pipeline.
388
+ */
389
+ interface IngestTarget {
390
+ beginIngest(opts: BeginIngestOptions): IngestSession;
391
+ getRevisions(): Revisions;
392
+ getDiagnostics?(): readonly GraphDiagnostic[];
393
+ }
394
+
395
+ /**
396
+ * v1 export loader (spec Appendix B.2 / B.8 / B.10).
397
+ *
398
+ * The organizing rule (B.10): graphs load via `og.export()` — one streamed
399
+ * NDJSON pass, edge lines first (lexicographic table-key order), driven
400
+ * straight into a `purpose:'replace'` ingest session on the target. Queries
401
+ * never introduce nodes or edges.
402
+ *
403
+ * Revision stamp (B.2): the canonical `sourceRevision` hashes
404
+ * `{ graphId, branch, headBefore, headAfter, schemaFingerprint }`, but
405
+ * `headAfter` is only knowable after the stream — while core's
406
+ * `BeginIngestOptions` requires `sourceRevision` up front for a replace
407
+ * session. Resolution: under the rejecting/retrying policies the session
408
+ * begins under the PROVISIONAL revision (the canonical hash with
409
+ * `headAfter := headBefore`), the stream appends into it, and `headAfter` is
410
+ * captured BEFORE `commit()`. `accept-warn` is the exception: normalized
411
+ * batches are buffered until `headAfter` is known, then appended once into a
412
+ * session opened with the canonical FINAL revision:
413
+ *
414
+ * - equal heads → provisional === final; commit cleanly. The session only
415
+ * ever commits a revision whose hash is truthful.
416
+ * - drifted heads → `driftPolicy` decides: `'reject'` aborts the session
417
+ * (graph untouched) and throws {@link OmnigraphDriftError};
418
+ * `'accept-warn'` commits the buffered export under the
419
+ * final revision, records BOTH heads in `dataRef`, and
420
+ * adds a warning;
421
+ * `'retry-once'` aborts and restarts the whole load once
422
+ * (a second drift rejects).
423
+ *
424
+ * This is sound because a replace session is atomic and invisible until
425
+ * commit (§7.5): nothing is published under a revision the policy did not
426
+ * explicitly accept.
427
+ *
428
+ * Error surface (B.9): SDK typed errors never cross this package's public
429
+ * surface — they are mapped to plain `Error`s with a stable `omnigraph:`
430
+ * message prefix.
431
+ */
432
+
433
+ /**
434
+ * B.2 drift under `driftPolicy: 'reject'` (or a second drift under
435
+ * `'retry-once'`): the branch head moved while the export streamed, the
436
+ * session was aborted, and the target graph is untouched.
437
+ */
438
+ declare class OmnigraphDriftError extends Error {
439
+ readonly name = "OmnigraphDriftError";
440
+ readonly graphId: string;
441
+ readonly branch: string;
442
+ readonly headBefore: string;
443
+ readonly headAfter: string;
444
+ constructor(graphId: string, branch: string, headBefore: string, headAfter: string);
445
+ }
446
+ /** The one load path a v1 source exposes (B.10). */
447
+ interface OmnigraphSource {
448
+ /**
449
+ * Stream one export of the configured graph/branch into `target` via a
450
+ * `purpose:'replace'` ingest session. Resolves with the revision-stamped
451
+ * result; rejects with the session aborted and the target untouched.
452
+ * `signal` aborts both the HTTP stream and the session.
453
+ */
454
+ load(target: IngestTarget, signal?: AbortSignal): Promise<OmnigraphLoadResult>;
455
+ }
456
+ /**
457
+ * Create a v1 export-backed data source (B.2/B.10). The client must be
458
+ * preconfigured (B.1): in the browser a safe same-origin or public client —
459
+ * there is deliberately no `baseUrl`/`token` option here. Authenticated
460
+ * construction lives only in `@modernrelay/orbit-omnigraph/server`.
461
+ */
462
+ declare function createOmnigraphSource(options: OmnigraphSourceOptions): OmnigraphSource;
463
+
464
+ /**
465
+ * B.7 stored-query `SearchService` (§16.5).
466
+ *
467
+ * A stored query using `bm25`/`fuzzy`/`nearest`/`rrf` with
468
+ * `order { score desc } limit K` returns entity rows plus a score column; this
469
+ * module wires it as a §16.5 `SearchService` via
470
+ * `og.queries.invoke(name, { params, branch })`, passing
471
+ * `RequestContext.signal` through SDK `CallOptions`. Core performs revision
472
+ * admission — the service only declares `revisionDependencies: ['source']`
473
+ * (results come from the server-side branch, so they are invalidated by a
474
+ * source change, never by client-side model/scope drift).
475
+ *
476
+ * Identity (B.3): Omnigraph ids are unique per type only, and a query row
477
+ * carries no type discriminator of its own — so the adapter REQUIRES a
478
+ * caller-supplied column→node-type mapping (`typeOf`), either a
479
+ * `{ column: NodeType }` record keyed by the projection columns that hold
480
+ * bare-variable node structs, or a per-row function. Every returned id is
481
+ * qualified through `encodeSourceId(nodeType, physicalId)` — the same codec
482
+ * every other adapter path uses — so results round-trip `decodeSourceId` and
483
+ * match export-loaded node ids exactly.
484
+ *
485
+ * v1 caveat (B.7): search runs server-side over the WHOLE branch, so against
486
+ * a partial export load it can return ids outside the loaded set. §16.5
487
+ * classifies those as `'not-loaded'` at activation; constrain the stored
488
+ * query to the loaded types, or load the full graph, to avoid the mismatch.
489
+ *
490
+ * Error surface (B.9): SDK typed errors never cross this package's public
491
+ * surface — they rethrow as plain `Error`s with the stable `omnigraph:`
492
+ * prefix. Abort rejections pass through unchanged.
493
+ */
494
+
495
+ /** One stored-query result row: projection column → value. Bare-variable
496
+ * projections (`return { $s }`) hold whole-node structs including `id`. */
497
+ type OmnigraphSearchRow = Record<string, unknown>;
498
+ /**
499
+ * The required B.3 column→node-type mapping:
500
+ *
501
+ * - a record `{ '$s': 'Signal' }` — the FIRST listed column present in a row
502
+ * with a node struct supplies the physical id, encoded under the mapped
503
+ * type name;
504
+ * - or a per-row function returning the node type name — the row's first
505
+ * node-struct column (row key order) supplies the physical id.
506
+ */
507
+ type OmnigraphSearchTypeOf = ((row: OmnigraphSearchRow) => string) | Readonly<Record<string, string>>;
508
+ interface OmnigraphSearchServiceOptions<N = Record<string, unknown>> {
509
+ /** A **preconfigured** SDK client (B.1) — no `baseUrl`/`token` here. */
510
+ client: Omnigraph;
511
+ /** Cluster graph id; the invoke is scoped via `client.graph(graphId)`. */
512
+ graphId: string;
513
+ /** Branch the stored query reads (B.7). Default `'main'`. */
514
+ branch?: string;
515
+ /** Registry name of the stored search query (`POST /queries/{name}`).
516
+ * Invoking a known name works whether or not it is `mcp.expose`d. */
517
+ queryName: string;
518
+ /** Builds the stored query's `params` object from the §16.5 call.
519
+ * Default: `(q, limit) => ({ q, limit })`. */
520
+ params?: (q: string, limit: number) => Record<string, unknown>;
521
+ /** REQUIRED B.3 mapping from row to node type — see
522
+ * {@link OmnigraphSearchTypeOf}. */
523
+ typeOf: OmnigraphSearchTypeOf;
524
+ /** Column whose value becomes `label` (String()-coerced when present).
525
+ * Default: the first string-valued column in row key order. */
526
+ labelColumn?: string;
527
+ /** Full custom row→result escape hatch: overrides the default mapping
528
+ * (including `typeOf`/`labelColumn`); return `null` to skip a row. The
529
+ * returned `id` MUST already be B.3-encoded via `encodeSourceId`. */
530
+ mapRow?: (row: OmnigraphSearchRow) => SearchResult<N> | null;
531
+ }
532
+ /**
533
+ * Create the B.7 stored-query search service. Plug it into core as
534
+ * `services.search`; the instance owns `RequestContext` creation,
535
+ * revision-keyed caching, supersede cancellation, and stale-result rejection
536
+ * at admission (§16.5).
537
+ */
538
+ declare function createOmnigraphSearchService<N = Record<string, unknown>>(options: OmnigraphSearchServiceOptions<N>): SearchService<N>;
539
+
540
+ export { type BigIntKeyWarning, type ClassifiedExportLine, type DecodedSourceId, type EdgeExportLine, type IngestTarget, InvalidExportLineError, type NodeExportLine, ORBIT_TYPE_KEY, type OmnigraphDataRef, OmnigraphDriftError, type OmnigraphDriftPolicy, type OmnigraphLoadCounts, type OmnigraphLoadProgress, type OmnigraphLoadResult, type OmnigraphSearchRow, type OmnigraphSearchServiceOptions, type OmnigraphSearchTypeOf, type OmnigraphSource, type OmnigraphSourceOptions, type PgEdgeType, type PgInterfaceType, type PgNodeType, type PgProperty, type PgScalarName, type PgSchema, type PgType, UnknownEdgeTypeError, bigIntKeyWarnings, classifyExportLine, createOmnigraphSearchService, createOmnigraphSource, decodeSourceId, edgeEndpointTypes, encodeSourceId, encodeSyntheticEdgeId, normalizeEdge, normalizeNode, parsePgSchema, schemaFingerprint };